node-opcua-crypto 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/.fossa.yml +18 -18
  2. package/.github/FUNDING.yml +12 -12
  3. package/.github/workflows/main.yml +32 -0
  4. package/.prettierrc.js +6 -6
  5. package/LICENSE +22 -22
  6. package/README.md +14 -14
  7. package/dist/asn1.d.ts +69 -0
  8. package/dist/asn1.js +349 -0
  9. package/dist/asn1.js.map +1 -0
  10. package/dist/buffer_utils.d.ts +6 -0
  11. package/dist/buffer_utils.js +22 -0
  12. package/dist/buffer_utils.js.map +1 -0
  13. package/dist/common.d.ts +11 -0
  14. package/dist/common.js +3 -0
  15. package/dist/common.js.map +1 -0
  16. package/dist/crypto_explore_certificate.d.ts +95 -0
  17. package/dist/crypto_explore_certificate.js +547 -0
  18. package/dist/crypto_explore_certificate.js.map +1 -0
  19. package/dist/crypto_utils.d.ts +106 -0
  20. package/dist/crypto_utils.js +370 -0
  21. package/dist/crypto_utils.js.map +1 -0
  22. package/dist/derived_keys.d.ts +72 -0
  23. package/dist/derived_keys.js +247 -0
  24. package/dist/derived_keys.js.map +1 -0
  25. package/dist/explore_certificate.d.ts +30 -0
  26. package/dist/explore_certificate.js +44 -0
  27. package/dist/explore_certificate.js.map +1 -0
  28. package/dist/explore_certificate_revocation_list.d.ts +30 -0
  29. package/dist/explore_certificate_revocation_list.js +67 -0
  30. package/dist/explore_certificate_revocation_list.js.map +1 -0
  31. package/dist/index.d.ts +10 -0
  32. package/dist/index.js +23 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/oid_map.d.ts +7 -0
  35. package/dist/oid_map.js +262 -0
  36. package/dist/oid_map.js.map +1 -0
  37. package/dist/source/asn1.d.ts +73 -72
  38. package/dist/source/asn1.js +359 -350
  39. package/dist/source/asn1.js.map +1 -1
  40. package/dist/source/buffer_utils.d.ts +6 -6
  41. package/dist/source/buffer_utils.js +21 -21
  42. package/dist/source/common.d.ts +12 -12
  43. package/dist/source/common.js +2 -2
  44. package/dist/source/crypto_explore_certificate.d.ts +107 -107
  45. package/dist/source/crypto_explore_certificate.js +600 -571
  46. package/dist/source/crypto_explore_certificate.js.map +1 -1
  47. package/dist/source/crypto_utils.d.ts +78 -78
  48. package/dist/source/crypto_utils.js +280 -280
  49. package/dist/source/crypto_utils.js.map +1 -1
  50. package/dist/source/derived_keys.d.ts +72 -72
  51. package/dist/source/derived_keys.js +248 -245
  52. package/dist/source/derived_keys.js.map +1 -1
  53. package/dist/source/explore_certificate.d.ts +30 -30
  54. package/dist/source/explore_certificate.js +43 -43
  55. package/dist/source/explore_certificate.js.map +1 -1
  56. package/dist/source/explore_certificate_revocation_list.d.ts +28 -28
  57. package/dist/source/explore_certificate_revocation_list.js +44 -44
  58. package/dist/source/explore_certificate_revocation_list.js.map +1 -1
  59. package/dist/source/explore_certificate_signing_request.d.ts +13 -13
  60. package/dist/source/explore_certificate_signing_request.js +44 -44
  61. package/dist/source/explore_certificate_signing_request.js.map +1 -1
  62. package/dist/source/explore_private_key.d.ts +29 -29
  63. package/dist/source/explore_private_key.js +96 -96
  64. package/dist/source/explore_private_key.js.map +1 -1
  65. package/dist/source/index.d.ts +13 -13
  66. package/dist/source/index.js +25 -25
  67. package/dist/source/oid_map.d.ts +7 -7
  68. package/dist/source/oid_map.js +303 -261
  69. package/dist/source/oid_map.js.map +1 -1
  70. package/dist/source/public_private_match.d.ts +3 -3
  71. package/dist/source/public_private_match.js +16 -16
  72. package/dist/source/public_private_match.js.map +1 -1
  73. package/dist/source/verify_certificate_signature.d.ts +10 -10
  74. package/dist/source/verify_certificate_signature.js +101 -101
  75. package/dist/source/verify_certificate_signature.js.map +1 -1
  76. package/dist/source_nodejs/index.d.ts +3 -3
  77. package/dist/source_nodejs/index.js +15 -15
  78. package/dist/source_nodejs/read.d.ts +29 -29
  79. package/dist/source_nodejs/read.js +94 -94
  80. package/dist/source_nodejs/read.js.map +1 -1
  81. package/dist/source_nodejs/read_certificate_revocation_list.d.ts +2 -2
  82. package/dist/source_nodejs/read_certificate_revocation_list.js +27 -27
  83. package/dist/source_nodejs/read_certificate_revocation_list.js.map +1 -1
  84. package/dist/source_nodejs/read_certificate_signing_request.d.ts +3 -3
  85. package/dist/source_nodejs/read_certificate_signing_request.js +27 -27
  86. package/dist/source_nodejs/read_certificate_signing_request.js.map +1 -1
  87. package/dist/verify_cerficate_signature.d.ts +10 -0
  88. package/dist/verify_cerficate_signature.js +102 -0
  89. package/dist/verify_cerficate_signature.js.map +1 -0
  90. package/index.d.ts +2 -2
  91. package/index.js +4 -4
  92. package/index_web.js +3 -3
  93. package/package.json +19 -22
  94. package/source/asn1.ts +404 -398
  95. package/source/buffer_utils.ts +18 -18
  96. package/source/common.ts +13 -13
  97. package/source/crypto_explore_certificate.ts +763 -728
  98. package/source/crypto_utils.ts +321 -321
  99. package/source/derived_keys.ts +287 -284
  100. package/source/explore_certificate.ts +66 -66
  101. package/source/explore_certificate_revocation_list.ts +93 -93
  102. package/source/explore_certificate_signing_request.ts +58 -58
  103. package/source/explore_private_key.ts +121 -121
  104. package/source/index.ts +13 -13
  105. package/source/oid_map.ts +310 -265
  106. package/source/public_private_match.ts +17 -17
  107. package/source/verify_certificate_signature.ts +105 -105
  108. package/source_nodejs/index.ts +2 -2
  109. package/source_nodejs/read.ts +95 -95
  110. package/source_nodejs/read_certificate_revocation_list.ts +14 -14
  111. package/source_nodejs/read_certificate_signing_request.ts +17 -17
  112. package/test_certificate.ts +34 -34
  113. package/tsconfig.json +18 -18
  114. package/tslint.json +34 -34
  115. package/pnpm-lock.yaml +0 -1767
@@ -1,728 +1,763 @@
1
- /**
2
- * @module node_opcua_crypto
3
- */
4
- // ---------------------------------------------------------------------------------------------------------------------
5
- // crypto_explore_certificate
6
- // ---------------------------------------------------------------------------------------------------------------------
7
- // Copyright (c) 2014-2020 - Etienne Rossignon
8
- // ---------------------------------------------------------------------------------------------------------------------
9
- //
10
- // This project is licensed under the terms of the MIT license.
11
- //
12
- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13
- // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
14
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
15
- // permit persons to whom the Software is furnished to do so, subject to the following conditions:
16
- //
17
- // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
18
- // Software.
19
- //
20
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21
- // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22
- // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23
- // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
- //
25
- //
26
- // ---------------------------------------------------------------------------------------------------------------------
27
- // ASN.1 JavaScript decoder Copyright (c) 2008-2014 Lapo Luchini lapo@lapo.it
28
- // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
29
- // granted, provided that the above copyright notice and this permission notice appear in all copies.
30
- // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
31
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
32
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
33
- // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
34
- // PERFORMANCE OF THIS SOFTWARE.
35
- // ---------------------------------------------------------------------------------------------------------------------
36
- /*jslint bitwise: true */
37
- // tslint:disable:no-shadowed-variable
38
-
39
- // references:
40
- // - http://tools.ietf.org/html/rfc5280
41
- // - http://www-lor.int-evry.fr/~michel/Supports/presentation.pdf
42
- // - ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc
43
- // - pubs.opengroup.org/onlinepubs/009609799/7a_nch02.htm#tagcjh_49_03
44
- // - https://github.com/lapo-luchini/asn1js/blob/master/asn1.js
45
- // - http://lapo.it/asn1js
46
- // - https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg
47
- // - http://pubs.opengroup.org/onlinepubs/009609799/7a_nch02.htm
48
- // - http://stackoverflow.com/questions/5929050/how-does-asn-1-encode-an-object-identifier
49
- // - http://luca.ntop.org/Teaching/Appunti/asn1.html
50
-
51
- // note:
52
- // - http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art030
53
- // openssl can be also used to discover the content of a DER file
54
- // $ openssl asn1parse -in cert.pem
55
- import * as assert from "assert";
56
-
57
- import {
58
- _readBitString,
59
- BlockInfo,
60
- TagType,
61
- readTag,
62
- _getBlock,
63
- _readStruct,
64
- formatBuffer2DigitHexWithColum,
65
- _readOctetString,
66
- AlgorithmIdentifier,
67
- _readListOfInteger,
68
- _readObjectIdentifier,
69
- _readAlgorithmIdentifier,
70
- _readBooleanValue,
71
- _readIntegerValue,
72
- _readLongIntegerValue,
73
- _readVersionValue,
74
- SignatureValue,
75
- _readSignatureValue,
76
- DirectoryName,
77
- _readValue,
78
- _readTime,
79
- _findBlockAtIndex,
80
- _readDirectoryName,
81
- } from "./asn1";
82
- import { Certificate, PrivateKey } from "./common";
83
- import { PublicKeyLength } from "./explore_certificate";
84
- import { makeSHA1Thumbprint } from "./crypto_utils";
85
-
86
- // Converted from: https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg
87
- // which is made by Peter Gutmann and whose license states:
88
- // You can use this code in whatever way you want,
89
- // as long as you don't try to claim you wrote it.
90
-
91
- const doDebug = false;
92
-
93
- export interface AttributeTypeAndValue {
94
- [key: string]: any;
95
- }
96
-
97
- function _readAttributeTypeAndValue(buffer: Buffer, block: BlockInfo): AttributeTypeAndValue {
98
- let inner_blocks = _readStruct(buffer, block);
99
- inner_blocks = _readStruct(buffer, inner_blocks[0]);
100
-
101
- const data = {
102
- identifier: _readObjectIdentifier(buffer, inner_blocks[0]).name,
103
- value: _readValue(buffer, inner_blocks[1]),
104
- };
105
-
106
- const result: AttributeTypeAndValue = {};
107
-
108
- for (const [key, value] of Object.entries(data)) {
109
- result[key] = value;
110
- }
111
- return result;
112
- }
113
-
114
- interface RelativeDistinguishedName {
115
- [prop: string]: any;
116
- }
117
-
118
- function _readRelativeDistinguishedName(buffer: Buffer, block: BlockInfo): RelativeDistinguishedName {
119
- const inner_blocks = _readStruct(buffer, block);
120
- const data = inner_blocks.map((block) => _readAttributeTypeAndValue(buffer, block));
121
- const result: any = {};
122
- for (const e of data) {
123
- result[e.identifier] = e.value;
124
- }
125
- return result;
126
- }
127
-
128
- function _readName(buffer: Buffer, block: BlockInfo): RelativeDistinguishedName {
129
- return _readRelativeDistinguishedName(buffer, block);
130
- }
131
-
132
- export interface Validity {
133
- notBefore: Date;
134
- notAfter: Date;
135
- }
136
-
137
- function _readValidity(buffer: Buffer, block: BlockInfo): Validity {
138
- const inner_blocks = _readStruct(buffer, block);
139
- return {
140
- notBefore: _readTime(buffer, inner_blocks[0]),
141
- notAfter: _readTime(buffer, inner_blocks[1]),
142
- };
143
- }
144
-
145
- function _readAuthorityKeyIdentifier(buffer: Buffer): AuthorityKeyIdentifier {
146
- /**
147
- * where a CA distributes its public key in the form of a "self-signed"
148
- * certificate, the authority key identifier MAY be omitted. Th
149
- * signature on a self-signed certificate is generated with the private
150
- * key associated with the certificate's subject public key. (This
151
- * proves that the issuer possesses both the public and private keys.)
152
- * In this case, the subject and authority key identifiers would be
153
- * identical, but only the subject key identifier is needed for
154
- * certification path building.
155
- */
156
- // see: https://www.ietf.org/rfc/rfc3280.txt page 25
157
- // AuthorityKeyIdentifier ::= SEQUENCE {
158
- // keyIdentifier [0] KeyIdentifier OPTIONAL,
159
- // authorityCertIssuer [1] GeneralNames OPTIONAL,
160
- // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
161
- // KeyIdentifier ::= OCTET STRING
162
-
163
- const block_info = readTag(buffer, 0);
164
- const blocks = _readStruct(buffer, block_info);
165
-
166
- const keyIdentifier_block = _findBlockAtIndex(blocks, 0);
167
- const authorityCertIssuer_block = _findBlockAtIndex(blocks, 1);
168
- const authorityCertSerialNumber_block = _findBlockAtIndex(blocks, 2);
169
-
170
- function _readAuthorityCertIssuer(block: BlockInfo): DirectoryName {
171
- const inner_blocks = _readStruct(buffer, block);
172
- const directoryName_block = _findBlockAtIndex(inner_blocks, 4);
173
- if (directoryName_block) {
174
- const a = _readStruct(buffer, directoryName_block);
175
- return _readDirectoryName(buffer, a[0]);
176
- } else {
177
- throw new Error("Invalid _readAuthorityCertIssuer");
178
- }
179
- }
180
- function _readAuthorityCertIssuerFingerPrint(block: BlockInfo): string {
181
- const inner_blocks = _readStruct(buffer, block);
182
- const directoryName_block = _findBlockAtIndex(inner_blocks, 4)!;
183
- if (!directoryName_block) {
184
- return "";
185
- }
186
- const a = _readStruct(buffer, directoryName_block);
187
- if (a.length < 1) {
188
- return "";
189
- }
190
- return directoryName_block ? formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, a[0]))) : "";
191
- }
192
-
193
- const authorityCertIssuer = authorityCertIssuer_block ? _readAuthorityCertIssuer(authorityCertIssuer_block) : null;
194
- const authorityCertIssuerFingerPrint = authorityCertIssuer_block
195
- ? _readAuthorityCertIssuerFingerPrint(authorityCertIssuer_block)
196
- : "";
197
-
198
- return {
199
- authorityCertIssuer,
200
- authorityCertIssuerFingerPrint,
201
- serial: authorityCertSerialNumber_block
202
- ? formatBuffer2DigitHexWithColum(_getBlock(buffer, authorityCertSerialNumber_block!))
203
- : null, // can be null for self-signed cert
204
- keyIdentifier: keyIdentifier_block ? formatBuffer2DigitHexWithColum(_getBlock(buffer, keyIdentifier_block!)) : null, // can be null for self-signed certf
205
- };
206
- }
207
-
208
- /*
209
- Extension ::= SEQUENCE {
210
- extnID OBJECT IDENTIFIER,
211
- critical BOOLEAN DEFAULT FALSE,
212
- extnValue OCTET STRING
213
- -- contains the DER encoding of an ASN.1 value
214
- -- corresponding to the extension type identified
215
- -- by extnID
216
- }
217
-
218
- id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
219
-
220
- KeyUsage ::= BIT STRING {
221
- digitalSignature (0),
222
- nonRepudiation (1), -- recent editions of X.509 have
223
- -- renamed this bit to contentCommitment
224
- keyEncipherment (2),
225
- dataEncipherment (3),
226
- keyAgreement (4),
227
- keyCertSign (5),
228
- cRLSign (6),
229
- encipherOnly (7),
230
- decipherOnly (8) }
231
-
232
- extKeyUsage
233
- */
234
-
235
- function readBasicConstraint2_5_29_19(buffer: Buffer, block: BlockInfo): BasicConstraints {
236
- const block_info = readTag(buffer, 0);
237
- const inner_blocks = _readStruct(buffer, block_info);
238
- const cA = inner_blocks.length > 0 ? _readBooleanValue(buffer, inner_blocks[0]) : false;
239
-
240
- // console.log("buffer[block_info.position] = ", buffer[block_info.position]);
241
- // const cA = buffer[block_info.position] ? true : false;
242
-
243
- let pathLengthConstraint = 0;
244
- if (inner_blocks.length > 1) {
245
- pathLengthConstraint = _readIntegerValue(buffer, inner_blocks[1]);
246
- }
247
- return { critical: true, cA, pathLengthConstraint };
248
- }
249
-
250
- // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
251
- // GeneralName ::= CHOICE {
252
- // otherName [0] AnotherName,
253
- // rfc822Name [1] IA5String,
254
- // dNSName [2] IA5String,
255
- // x400Address [3] ORAddress,
256
- // directoryName [4] Name,
257
- // ediPartyName [5] EDIPartyName,
258
- // uniformResourceIdentifier [6] IA5String,
259
- // iPAddress [7] OCTET STRING,
260
- // registeredID [8] OBJECT IDENTIFIER }
261
- function _readGeneralNames(buffer: Buffer, block: BlockInfo) {
262
- const _data: { [key: number]: { name: string; type: string } } = {
263
- 1: { name: "rfc822Name", type: "IA5String" },
264
- 2: { name: "dNSName", type: "IA5String" },
265
- 3: { name: "x400Address", type: "ORAddress" },
266
- 4: { name: "directoryName", type: "Name" },
267
- 5: { name: "ediPartyName", type: "EDIPartyName" },
268
- 6: { name: "uniformResourceIdentifier", type: "IA5String" },
269
- 7: { name: "iPAddress", type: "OCTET_STRING" },
270
- 8: { name: "registeredID", type: "OBJECT_IDENTIFIER" },
271
- };
272
- const blocks = _readStruct(buffer, block);
273
-
274
- function _readFromType(buffer: Buffer, block: BlockInfo, type: string) {
275
- switch (type) {
276
- case "IA5String":
277
- return buffer.slice(block.position, block.position + block.length).toString("ascii");
278
- default:
279
- return buffer.slice(block.position, block.position + block.length).toString("hex");
280
- }
281
- }
282
-
283
- const n: { [key: string]: string[] } = {};
284
- for (const block of blocks) {
285
- // tslint:disable-next-line: no-bitwise
286
- assert((block.tag & 0x80) === 0x80);
287
- // tslint:disable-next-line: no-bitwise
288
- const t = block.tag & 0x7f;
289
- const type = _data[t] as { name: string; type: string } | undefined;
290
-
291
- // istanbul ignore next
292
- if (!type) {
293
- throw new Error(" INVALID TYPE => " + t + "0x" + t.toString(16));
294
- }
295
- n[type.name] = n[type.name] || [];
296
- n[type.name].push(_readFromType(buffer, block, type.type));
297
- }
298
- return n;
299
- }
300
-
301
- function _readSubjectAltNames(buffer: Buffer) {
302
- const block_info = readTag(buffer, 0);
303
- return _readGeneralNames(buffer, block_info);
304
- }
305
-
306
- // named X509KeyUsage not to confuse with DOM KeyUsage
307
- export interface X509KeyUsage {
308
- digitalSignature: boolean;
309
- nonRepudiation: boolean;
310
- keyEncipherment: boolean;
311
- dataEncipherment: boolean;
312
- keyAgreement: boolean;
313
- keyCertSign: boolean;
314
- cRLSign: boolean;
315
- encipherOnly: boolean;
316
- decipherOnly: boolean;
317
- }
318
- export interface X509ExtKeyUsage {
319
- clientAuth: boolean;
320
- serverAuth: boolean;
321
- codeSigning: boolean;
322
- emailProtection: boolean;
323
- timeStamping: boolean;
324
- ocspSigning: boolean;
325
- ipsecEndSystem: boolean;
326
- ipsecTunnel: boolean;
327
- ipsecUser: boolean;
328
- // etc ... to be completed
329
- }
330
-
331
- function readKeyUsage(oid: string, buffer: Buffer): X509KeyUsage {
332
- const block_info = readTag(buffer, 0);
333
-
334
- // get value as BIT STRING
335
- let b2 = 0x00;
336
- let b3 = 0x00;
337
- if (block_info.length > 1) {
338
- // skip first byte, just indicates unused bits which
339
- // will be padded with 0s anyway
340
- // get bytes with flag bits
341
- b2 = buffer[block_info.position + 1];
342
- b3 = block_info.length > 2 ? buffer[block_info.position + 2] : 0;
343
- }
344
-
345
- // set flags
346
- return {
347
- // tslint:disable-next-line: no-bitwise
348
- digitalSignature: (b2 & 0x80) === 0x80,
349
- // tslint:disable-next-line: no-bitwise
350
- nonRepudiation: (b2 & 0x40) === 0x40,
351
- // tslint:disable-next-line: no-bitwise
352
- keyEncipherment: (b2 & 0x20) === 0x20,
353
- // tslint:disable-next-line: no-bitwise
354
- dataEncipherment: (b2 & 0x10) === 0x10,
355
- // tslint:disable-next-line: no-bitwise
356
- keyAgreement: (b2 & 0x08) === 0x08,
357
- // tslint:disable-next-line: no-bitwise
358
- keyCertSign: (b2 & 0x04) === 0x04,
359
- // tslint:disable-next-line: no-bitwise
360
- cRLSign: (b2 & 0x02) === 0x02,
361
- // tslint:disable-next-line: no-bitwise
362
- encipherOnly: (b2 & 0x01) === 0x01,
363
- // tslint:disable-next-line: no-bitwise
364
- decipherOnly: (b3 & 0x80) === 0x80,
365
- };
366
- }
367
-
368
- function readExtKeyUsage(oid: string, buffer: Buffer): X509ExtKeyUsage {
369
- assert(oid === "2.5.29.37");
370
- // see https://tools.ietf.org/html/rfc5280#section-4.2.1.12
371
- const block_info = readTag(buffer, 0);
372
-
373
- const inner_blocks = _readStruct(buffer, block_info);
374
-
375
- const extKeyUsage: X509ExtKeyUsage = {
376
- serverAuth: false,
377
- clientAuth: false,
378
- codeSigning: false,
379
- emailProtection: false,
380
- timeStamping: false,
381
- ipsecEndSystem: false,
382
- ipsecTunnel: false,
383
- ipsecUser: false,
384
- ocspSigning: false,
385
- };
386
- for (const block of inner_blocks) {
387
- const identifier = _readObjectIdentifier(buffer, block);
388
- (extKeyUsage as any)[identifier.name] = true;
389
- }
390
- /*
391
-
392
- id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
393
-
394
- id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
395
- -- TLS WWW server authentication
396
- -- Key usage bits that may be consistent: digitalSignature,
397
- -- keyEncipherment or keyAgreement
398
-
399
- id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
400
- -- TLS WWW client authentication
401
- -- Key usage bits that may be consistent: digitalSignature
402
- -- and/or keyAgreement
403
-
404
- id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
405
- -- Signing of downloadable executable code
406
- -- Key usage bits that may be consistent: digitalSignature
407
-
408
- id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
409
- -- Email protection
410
- -- Key usage bits that may be consistent: digitalSignature,
411
- -- nonRepudiation, and/or (keyEncipherment or keyAgreement)
412
-
413
- id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
414
- -- Binding the hash of an object to a time
415
- -- Key usage bits that may be consistent: digitalSignature
416
- -- and/or nonRepudiation
417
-
418
- id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
419
- -- Signing OCSP responses
420
- -- Key usage bits that may be consistent: digitalSignature
421
- -- and/or nonRepudiation
422
-
423
- */
424
- // set flags
425
- return extKeyUsage;
426
- }
427
-
428
- export interface SubjectPublicKey {
429
- modulus: Buffer;
430
- }
431
- function _readSubjectPublicKey(buffer: Buffer): SubjectPublicKey {
432
- const block_info = readTag(buffer, 0);
433
- const blocks = _readStruct(buffer, block_info);
434
-
435
- return {
436
- modulus: buffer.slice(blocks[0].position + 1, blocks[0].position + blocks[0].length),
437
- };
438
- }
439
- /*
440
- Extension ::= SEQUENCE {
441
- extnID OBJECT IDENTIFIER,
442
- critical BOOLEAN DEFAULT FALSE,
443
- extnValue OCTET STRING
444
- -- contains the DER encoding of an ASN.1 value
445
- -- corresponding to the extension type identified
446
- -- by extnID
447
- }
448
- */
449
- export function _readExtension(buffer: Buffer, block: BlockInfo): { identifier: { oid: string; name: string }; value: any } {
450
- const inner_blocks = _readStruct(buffer, block);
451
-
452
- if (inner_blocks.length === 3) {
453
- assert(inner_blocks[1].tag === TagType.BOOLEAN);
454
- inner_blocks[1] = inner_blocks[2];
455
- }
456
-
457
- const identifier = _readObjectIdentifier(buffer, inner_blocks[0]);
458
- const buf = _getBlock(buffer, inner_blocks[1]);
459
- let value = null;
460
- switch (identifier.name) {
461
- case "subjectKeyIdentifier":
462
- /* from https://tools.ietf.org/html/rfc3280#section-4.1 :
463
- For CA certificates, subject key identifiers SHOULD be derived from
464
- the public key or a method that generates unique values. Two common
465
- methods for generating key identifiers from the public key are:
466
-
467
- (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
468
- value of the BIT STRING subjectPublicKey (excluding the tag,
469
- length, and number of unused bits).
470
-
471
- (2) The keyIdentifier is composed of a four bit type field with
472
- the value 0100 followed by the least significant 60 bits of the
473
- SHA-1 hash of the value of the BIT STRING subjectPublicKey
474
- (excluding the tag, length, and number of unused bit string bits).
475
- */
476
- value = formatBuffer2DigitHexWithColum(_readOctetString(buffer, inner_blocks[1]));
477
- break;
478
- case "subjectAltName":
479
- value = _readSubjectAltNames(buf);
480
- break;
481
- case "authorityKeyIdentifier":
482
- value = _readAuthorityKeyIdentifier(buf);
483
- break;
484
- case "basicConstraints":
485
- value = readBasicConstraint2_5_29_19(buf, inner_blocks[1]); // "2.5.29.19":
486
- // "basicConstraints ( not implemented yet) " + buf.toString("hex");
487
- break;
488
- case "certExtension": // Netscape
489
- value = "basicConstraints ( not implemented yet) " + buf.toString("hex");
490
- break;
491
- case "extKeyUsage":
492
- value = readExtKeyUsage(identifier.oid, buf);
493
- break;
494
- case "keyUsage":
495
- value = readKeyUsage(identifier.oid, buf);
496
- break;
497
- default:
498
- value = "Unknown " + identifier.name + buf.toString("hex");
499
- }
500
- return {
501
- identifier,
502
- value,
503
- };
504
- }
505
-
506
- // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
507
- function _readExtensions(buffer: Buffer, block: BlockInfo): CertificateExtension {
508
- assert(block.tag === 0xa3);
509
- let inner_blocks = _readStruct(buffer, block);
510
- inner_blocks = _readStruct(buffer, inner_blocks[0]);
511
-
512
- const extensions = inner_blocks.map((block) => _readExtension(buffer, block));
513
-
514
- const result: any = {};
515
- for (const e of extensions) {
516
- result[e.identifier.name] = e.value;
517
- }
518
- return result as CertificateExtension;
519
- }
520
-
521
- /*
522
- SEQUENCE {
523
- 204 13: SEQUENCE {
524
- 206 9: OBJECT IDENTIFIER
525
- : rsaEncryption (1 2 840 113549 1 1 1)
526
- 217 0: NULL
527
- : }
528
- 219 141: BIT STRING, encapsulates {
529
- 223 137: SEQUENCE {
530
- 226 129: INTEGER
531
- : 00 C2 D7 97 6D 28 70 AA 5B CF 23 2E 80 70 39 EE
532
- : DB 6F D5 2D D5 6A 4F 7A 34 2D F9 22 72 47 70 1D
533
- : EF 80 E9 CA 30 8C 00 C4 9A 6E 5B 45 B4 6E A5 E6
534
- : 6C 94 0D FA 91 E9 40 FC 25 9D C7 B7 68 19 56 8F
535
- : 11 70 6A D7 F1 C9 11 4F 3A 7E 3F 99 8D 6E 76 A5
536
- : 74 5F 5E A4 55 53 E5 C7 68 36 53 C7 1D 3B 12 A6
537
- : 85 FE BD 6E A1 CA DF 35 50 AC 08 D7 B9 B4 7E 5C
538
- : FE E2 A3 2C D1 23 84 AA 98 C0 9B 66 18 9A 68 47
539
- : E9
540
- 358 3: INTEGER 65537
541
- : }
542
- : }
543
- : }
544
- */
545
-
546
- function _readSubjectPublicKeyInfo(buffer: Buffer, block: BlockInfo): SubjectPublicKeyInfo {
547
- const inner_blocks = _readStruct(buffer, block);
548
-
549
- // algorithm identifier
550
- const algorithm = _readAlgorithmIdentifier(buffer, inner_blocks[0]);
551
- //const parameters = _readBitString(buffer,inner_blocks[1]);
552
- const subjectPublicKey = _readBitString(buffer, inner_blocks[1]);
553
-
554
- // read the 2 big integers of the key
555
- const data = subjectPublicKey.data;
556
- const values = _readListOfInteger(data);
557
- // xx const value = _readListOfInteger(data);
558
- return {
559
- algorithm: algorithm.identifier,
560
- keyLength: (values[0].length - 1) as PublicKeyLength,
561
- subjectPublicKey: _readSubjectPublicKey(subjectPublicKey.data),
562
- //xx values: values,
563
- //xx values_length : values.map(function (a){ return a.length; })
564
- };
565
- }
566
-
567
- export interface SubjectPublicKeyInfo {
568
- algorithm: string;
569
- keyLength: PublicKeyLength;
570
- subjectPublicKey: SubjectPublicKey;
571
- }
572
-
573
- export interface BasicConstraints {
574
- critical: boolean;
575
- cA: boolean;
576
- pathLengthConstraint?: number; // 0 Unlimited
577
- }
578
-
579
- export interface AuthorityKeyIdentifier {
580
- keyIdentifier: string | null;
581
- authorityCertIssuer: DirectoryName | null;
582
- authorityCertIssuerFingerPrint: string;
583
- serial: string | null;
584
- }
585
-
586
- export interface CertificateExtension {
587
- basicConstraints: BasicConstraints;
588
- subjectKeyIdentifier?: string;
589
- authorityKeyIdentifier?: AuthorityKeyIdentifier;
590
- keyUsage?: X509KeyUsage;
591
- extKeyUsage?: X509ExtKeyUsage;
592
- subjectAltName?: any;
593
- }
594
-
595
- export interface TbsCertificate {
596
- version: number;
597
- serialNumber: string;
598
- issuer: any;
599
- signature: AlgorithmIdentifier;
600
- validity: Validity;
601
- subject: DirectoryName;
602
- subjectFingerPrint: string;
603
- subjectPublicKeyInfo: SubjectPublicKeyInfo;
604
- extensions: CertificateExtension | null;
605
- }
606
-
607
- export function readTbsCertificate(buffer: Buffer, block: BlockInfo): TbsCertificate {
608
- const blocks = _readStruct(buffer, block);
609
-
610
- let version, serialNumber, signature, issuer, validity, subject, subjectFingerPrint, subjectPublicKeyInfo, extensions;
611
-
612
- if (blocks.length === 6) {
613
- // X509 Version 1:
614
- version = 1;
615
-
616
- serialNumber = formatBuffer2DigitHexWithColum(_readLongIntegerValue(buffer, blocks[0]));
617
- signature = _readAlgorithmIdentifier(buffer, blocks[1]);
618
- issuer = _readName(buffer, blocks[2]);
619
- validity = _readValidity(buffer, blocks[3]);
620
- subject = _readName(buffer, blocks[4]);
621
- subjectFingerPrint = formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, blocks[4])));
622
- subjectPublicKeyInfo = _readSubjectPublicKeyInfo(buffer, blocks[5]);
623
-
624
- extensions = null;
625
- } else {
626
- // X509 Version 3:
627
-
628
- const version_block = _findBlockAtIndex(blocks, 0);
629
- if (!version_block) {
630
- throw new Error("cannot find version block");
631
- }
632
- version = _readVersionValue(buffer, version_block) + 1;
633
- serialNumber = formatBuffer2DigitHexWithColum(_readLongIntegerValue(buffer, blocks[1]));
634
- signature = _readAlgorithmIdentifier(buffer, blocks[2]);
635
- issuer = _readName(buffer, blocks[3]);
636
- validity = _readValidity(buffer, blocks[4]);
637
- subject = _readName(buffer, blocks[5]);
638
- subjectFingerPrint = formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, blocks[5])));
639
- subjectPublicKeyInfo = _readSubjectPublicKeyInfo(buffer, blocks[6]);
640
-
641
- const extensionBlock = _findBlockAtIndex(blocks, 3);
642
- if (!extensionBlock) {
643
- // tslint:disable-next-line: no-console
644
- console.log("X509 certificate is invalid : cannot find extension block version =" + version_block);
645
- extensions = null;
646
- } else {
647
- extensions = _readExtensions(buffer, extensionBlock);
648
- }
649
- }
650
-
651
- return {
652
- version,
653
- serialNumber,
654
- signature,
655
- issuer,
656
- validity,
657
- subject,
658
- subjectFingerPrint,
659
- subjectPublicKeyInfo,
660
- extensions,
661
- };
662
- }
663
- export interface CertificateInternals {
664
- tbsCertificate: TbsCertificate;
665
- signatureAlgorithm: AlgorithmIdentifier;
666
- signatureValue: SignatureValue;
667
- }
668
-
669
- /**
670
- * explore a certificate structure
671
- * @param certificate
672
- * @returns a json object that exhibits the internal data of the certificate
673
- */
674
- export function exploreCertificate(certificate: Certificate): CertificateInternals {
675
- assert(certificate instanceof Buffer);
676
- if (!(certificate as any)._exploreCertificate_cache) {
677
- const block_info = readTag(certificate, 0);
678
- const blocks = _readStruct(certificate, block_info);
679
- (certificate as any)._exploreCertificate_cache = {
680
- tbsCertificate: readTbsCertificate(certificate, blocks[0]),
681
- signatureAlgorithm: _readAlgorithmIdentifier(certificate, blocks[1]),
682
- signatureValue: _readSignatureValue(certificate, blocks[2]),
683
- };
684
- }
685
- return (certificate as any)._exploreCertificate_cache;
686
- }
687
-
688
- /**
689
- * @method split_der
690
- * split a multi chain certificates
691
- * @param certificateChain the certificate chain in der (binary) format}
692
- * @returns an array of Der , each element of the array is one certificate of the chain
693
- */
694
- export function split_der(certificateChain: Certificate): Certificate[] {
695
- const certificate_chain: Buffer[] = [];
696
-
697
- do {
698
- const block_info = readTag(certificateChain, 0);
699
- const length = block_info.position + block_info.length;
700
- const der_certificate = certificateChain.slice(0, length);
701
- certificate_chain.push(der_certificate);
702
- certificateChain = certificateChain.slice(length);
703
- } while (certificateChain.length > 0);
704
- return certificate_chain;
705
- }
706
-
707
- /**
708
- * @method combine_der
709
- * combine an array of certificates into a single blob
710
- * @param certificates a array with the individual DER certificates of the chain
711
- * @return a concatenated buffer containing the certificates
712
- */
713
- export function combine_der(certificates: Certificate[]): Certificate {
714
- // perform some sanity check
715
- for (const cert of certificates) {
716
- const b = split_der(cert);
717
- let sum = 0;
718
- b.forEach((block) => {
719
- const block_info = readTag(block, 0);
720
- //xx console.log("xxxx" ,cert.length,block_info);
721
- //xx console.log(cert.toString("base64"));
722
- assert(block_info.position + block_info.length === block.length);
723
- sum += block.length;
724
- });
725
- assert(sum === cert.length);
726
- }
727
- return Buffer.concat(certificates);
728
- }
1
+ /**
2
+ * @module node_opcua_crypto
3
+ */
4
+ // ---------------------------------------------------------------------------------------------------------------------
5
+ // crypto_explore_certificate
6
+ // ---------------------------------------------------------------------------------------------------------------------
7
+ // Copyright (c) 2014-2020 - Etienne Rossignon
8
+ // ---------------------------------------------------------------------------------------------------------------------
9
+ //
10
+ // This project is licensed under the terms of the MIT license.
11
+ //
12
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13
+ // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
14
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
15
+ // permit persons to whom the Software is furnished to do so, subject to the following conditions:
16
+ //
17
+ // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
18
+ // Software.
19
+ //
20
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21
+ // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22
+ // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ //
25
+ //
26
+ // ---------------------------------------------------------------------------------------------------------------------
27
+ // ASN.1 JavaScript decoder Copyright (c) 2008-2014 Lapo Luchini lapo@lapo.it
28
+ // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
29
+ // granted, provided that the above copyright notice and this permission notice appear in all copies.
30
+ // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
31
+ // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
32
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
33
+ // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
34
+ // PERFORMANCE OF THIS SOFTWARE.
35
+ // ---------------------------------------------------------------------------------------------------------------------
36
+ /*jslint bitwise: true */
37
+ // tslint:disable:no-shadowed-variable
38
+
39
+ // references:
40
+ // - http://tools.ietf.org/html/rfc5280
41
+ // - http://www-lor.int-evry.fr/~michel/Supports/presentation.pdf
42
+ // - ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc
43
+ // - pubs.opengroup.org/onlinepubs/009609799/7a_nch02.htm#tagcjh_49_03
44
+ // - https://github.com/lapo-luchini/asn1js/blob/master/asn1.js
45
+ // - http://lapo.it/asn1js
46
+ // - https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg
47
+ // - http://pubs.opengroup.org/onlinepubs/009609799/7a_nch02.htm
48
+ // - http://stackoverflow.com/questions/5929050/how-does-asn-1-encode-an-object-identifier
49
+ // - http://luca.ntop.org/Teaching/Appunti/asn1.html
50
+
51
+ // note:
52
+ // - http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art030
53
+ // openssl can be also used to discover the content of a DER file
54
+ // $ openssl asn1parse -in cert.pem
55
+ import * as assert from "assert";
56
+
57
+ import {
58
+ _readBitString,
59
+ BlockInfo,
60
+ TagType,
61
+ readTag,
62
+ _getBlock,
63
+ _readStruct,
64
+ formatBuffer2DigitHexWithColum,
65
+ _readOctetString,
66
+ AlgorithmIdentifier,
67
+ _readListOfInteger,
68
+ _readObjectIdentifier,
69
+ _readAlgorithmIdentifier,
70
+ _readECCAlgorithmIdentifier,
71
+ _readBooleanValue,
72
+ _readIntegerValue,
73
+ _readLongIntegerValue,
74
+ _readVersionValue,
75
+ SignatureValue,
76
+ _readSignatureValue,
77
+ DirectoryName,
78
+ _readValue,
79
+ _readTime,
80
+ _findBlockAtIndex,
81
+ _readDirectoryName,
82
+ } from "./asn1";
83
+ import { Certificate, PrivateKey } from "./common";
84
+ import { PublicKeyLength } from "./explore_certificate";
85
+ import { makeSHA1Thumbprint } from "./crypto_utils";
86
+
87
+ // Converted from: https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg
88
+ // which is made by Peter Gutmann and whose license states:
89
+ // You can use this code in whatever way you want,
90
+ // as long as you don't try to claim you wrote it.
91
+
92
+ const doDebug = false;
93
+
94
+ export interface AttributeTypeAndValue {
95
+ [key: string]: any;
96
+ }
97
+
98
+ function _readAttributeTypeAndValue(buffer: Buffer, block: BlockInfo): AttributeTypeAndValue {
99
+ let inner_blocks = _readStruct(buffer, block);
100
+ inner_blocks = _readStruct(buffer, inner_blocks[0]);
101
+
102
+ const data = {
103
+ identifier: _readObjectIdentifier(buffer, inner_blocks[0]).name,
104
+ value: _readValue(buffer, inner_blocks[1]),
105
+ };
106
+
107
+ const result: AttributeTypeAndValue = {};
108
+
109
+ for (const [key, value] of Object.entries(data)) {
110
+ result[key] = value;
111
+ }
112
+ return result;
113
+ }
114
+
115
+ interface RelativeDistinguishedName {
116
+ [prop: string]: any;
117
+ }
118
+
119
+ function _readRelativeDistinguishedName(buffer: Buffer, block: BlockInfo): RelativeDistinguishedName {
120
+ const inner_blocks = _readStruct(buffer, block);
121
+ const data = inner_blocks.map((block) => _readAttributeTypeAndValue(buffer, block));
122
+ const result: any = {};
123
+ for (const e of data) {
124
+ result[e.identifier] = e.value;
125
+ }
126
+ return result;
127
+ }
128
+
129
+ function _readName(buffer: Buffer, block: BlockInfo): RelativeDistinguishedName {
130
+ return _readRelativeDistinguishedName(buffer, block);
131
+ }
132
+
133
+ export interface Validity {
134
+ notBefore: Date;
135
+ notAfter: Date;
136
+ }
137
+
138
+ function _readValidity(buffer: Buffer, block: BlockInfo): Validity {
139
+ const inner_blocks = _readStruct(buffer, block);
140
+ return {
141
+ notBefore: _readTime(buffer, inner_blocks[0]),
142
+ notAfter: _readTime(buffer, inner_blocks[1]),
143
+ };
144
+ }
145
+
146
+ function _readAuthorityKeyIdentifier(buffer: Buffer): AuthorityKeyIdentifier {
147
+ /**
148
+ * where a CA distributes its public key in the form of a "self-signed"
149
+ * certificate, the authority key identifier MAY be omitted. Th
150
+ * signature on a self-signed certificate is generated with the private
151
+ * key associated with the certificate's subject public key. (This
152
+ * proves that the issuer possesses both the public and private keys.)
153
+ * In this case, the subject and authority key identifiers would be
154
+ * identical, but only the subject key identifier is needed for
155
+ * certification path building.
156
+ */
157
+ // see: https://www.ietf.org/rfc/rfc3280.txt page 25
158
+ // AuthorityKeyIdentifier ::= SEQUENCE {
159
+ // keyIdentifier [0] KeyIdentifier OPTIONAL,
160
+ // authorityCertIssuer [1] GeneralNames OPTIONAL,
161
+ // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
162
+ // KeyIdentifier ::= OCTET STRING
163
+
164
+ const block_info = readTag(buffer, 0);
165
+ const blocks = _readStruct(buffer, block_info);
166
+
167
+ const keyIdentifier_block = _findBlockAtIndex(blocks, 0);
168
+ const authorityCertIssuer_block = _findBlockAtIndex(blocks, 1);
169
+ const authorityCertSerialNumber_block = _findBlockAtIndex(blocks, 2);
170
+
171
+ function _readAuthorityCertIssuer(block: BlockInfo): DirectoryName {
172
+ const inner_blocks = _readStruct(buffer, block);
173
+ const directoryName_block = _findBlockAtIndex(inner_blocks, 4);
174
+ if (directoryName_block) {
175
+ const a = _readStruct(buffer, directoryName_block);
176
+ return _readDirectoryName(buffer, a[0]);
177
+ } else {
178
+ throw new Error("Invalid _readAuthorityCertIssuer");
179
+ }
180
+ }
181
+ function _readAuthorityCertIssuerFingerPrint(block: BlockInfo): string {
182
+ const inner_blocks = _readStruct(buffer, block);
183
+ const directoryName_block = _findBlockAtIndex(inner_blocks, 4)!;
184
+ if (!directoryName_block) {
185
+ return "";
186
+ }
187
+ const a = _readStruct(buffer, directoryName_block);
188
+ if (a.length < 1) {
189
+ return "";
190
+ }
191
+ return directoryName_block ? formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, a[0]))) : "";
192
+ }
193
+
194
+ const authorityCertIssuer = authorityCertIssuer_block ? _readAuthorityCertIssuer(authorityCertIssuer_block) : null;
195
+ const authorityCertIssuerFingerPrint = authorityCertIssuer_block
196
+ ? _readAuthorityCertIssuerFingerPrint(authorityCertIssuer_block)
197
+ : "";
198
+
199
+ return {
200
+ authorityCertIssuer,
201
+ authorityCertIssuerFingerPrint,
202
+ serial: authorityCertSerialNumber_block
203
+ ? formatBuffer2DigitHexWithColum(_getBlock(buffer, authorityCertSerialNumber_block!))
204
+ : null, // can be null for self-signed cert
205
+ keyIdentifier: keyIdentifier_block ? formatBuffer2DigitHexWithColum(_getBlock(buffer, keyIdentifier_block!)) : null, // can be null for self-signed certf
206
+ };
207
+ }
208
+
209
+ /*
210
+ Extension ::= SEQUENCE {
211
+ extnID OBJECT IDENTIFIER,
212
+ critical BOOLEAN DEFAULT FALSE,
213
+ extnValue OCTET STRING
214
+ -- contains the DER encoding of an ASN.1 value
215
+ -- corresponding to the extension type identified
216
+ -- by extnID
217
+ }
218
+
219
+ id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
220
+
221
+ KeyUsage ::= BIT STRING {
222
+ digitalSignature (0),
223
+ nonRepudiation (1), -- recent editions of X.509 have
224
+ -- renamed this bit to contentCommitment
225
+ keyEncipherment (2),
226
+ dataEncipherment (3),
227
+ keyAgreement (4),
228
+ keyCertSign (5),
229
+ cRLSign (6),
230
+ encipherOnly (7),
231
+ decipherOnly (8) }
232
+
233
+ extKeyUsage
234
+ */
235
+
236
+ function readBasicConstraint2_5_29_19(buffer: Buffer, block: BlockInfo): BasicConstraints {
237
+ const block_info = readTag(buffer, 0);
238
+ const inner_blocks = _readStruct(buffer, block_info);
239
+ const cA = inner_blocks.length > 0 ? _readBooleanValue(buffer, inner_blocks[0]) : false;
240
+
241
+ // console.log("buffer[block_info.position] = ", buffer[block_info.position]);
242
+ // const cA = buffer[block_info.position] ? true : false;
243
+
244
+ let pathLengthConstraint = 0;
245
+ if (inner_blocks.length > 1) {
246
+ pathLengthConstraint = _readIntegerValue(buffer, inner_blocks[1]);
247
+ }
248
+ return { critical: true, cA, pathLengthConstraint };
249
+ }
250
+
251
+ // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
252
+ // GeneralName ::= CHOICE {
253
+ // otherName [0] AnotherName,
254
+ // rfc822Name [1] IA5String,
255
+ // dNSName [2] IA5String,
256
+ // x400Address [3] ORAddress,
257
+ // directoryName [4] Name,
258
+ // ediPartyName [5] EDIPartyName,
259
+ // uniformResourceIdentifier [6] IA5String,
260
+ // iPAddress [7] OCTET STRING,
261
+ // registeredID [8] OBJECT IDENTIFIER }
262
+ function _readGeneralNames(buffer: Buffer, block: BlockInfo) {
263
+ const _data: { [key: number]: { name: string; type: string } } = {
264
+ 1: { name: "rfc822Name", type: "IA5String" },
265
+ 2: { name: "dNSName", type: "IA5String" },
266
+ 3: { name: "x400Address", type: "ORAddress" },
267
+ 4: { name: "directoryName", type: "Name" },
268
+ 5: { name: "ediPartyName", type: "EDIPartyName" },
269
+ 6: { name: "uniformResourceIdentifier", type: "IA5String" },
270
+ 7: { name: "iPAddress", type: "OCTET_STRING" },
271
+ 8: { name: "registeredID", type: "OBJECT_IDENTIFIER" },
272
+ };
273
+ const blocks = _readStruct(buffer, block);
274
+
275
+ function _readFromType(buffer: Buffer, block: BlockInfo, type: string) {
276
+ switch (type) {
277
+ case "IA5String":
278
+ return buffer.slice(block.position, block.position + block.length).toString("ascii");
279
+ default:
280
+ return buffer.slice(block.position, block.position + block.length).toString("hex");
281
+ }
282
+ }
283
+
284
+ const n: { [key: string]: string[] } = {};
285
+ for (const block of blocks) {
286
+ // tslint:disable-next-line: no-bitwise
287
+ assert((block.tag & 0x80) === 0x80);
288
+ // tslint:disable-next-line: no-bitwise
289
+ const t = block.tag & 0x7f;
290
+ const type = _data[t] as { name: string; type: string } | undefined;
291
+
292
+ // istanbul ignore next
293
+ if (!type) {
294
+ throw new Error(" INVALID TYPE => " + t + "0x" + t.toString(16));
295
+ }
296
+ n[type.name] = n[type.name] || [];
297
+ n[type.name].push(_readFromType(buffer, block, type.type));
298
+ }
299
+ return n;
300
+ }
301
+
302
+ function _readSubjectAltNames(buffer: Buffer) {
303
+ const block_info = readTag(buffer, 0);
304
+ return _readGeneralNames(buffer, block_info);
305
+ }
306
+
307
+ // named X509KeyUsage not to confuse with DOM KeyUsage
308
+ export interface X509KeyUsage {
309
+ digitalSignature: boolean;
310
+ nonRepudiation: boolean;
311
+ keyEncipherment: boolean;
312
+ dataEncipherment: boolean;
313
+ keyAgreement: boolean;
314
+ keyCertSign: boolean;
315
+ cRLSign: boolean;
316
+ encipherOnly: boolean;
317
+ decipherOnly: boolean;
318
+ }
319
+ export interface X509ExtKeyUsage {
320
+ clientAuth: boolean;
321
+ serverAuth: boolean;
322
+ codeSigning: boolean;
323
+ emailProtection: boolean;
324
+ timeStamping: boolean;
325
+ ocspSigning: boolean;
326
+ ipsecEndSystem: boolean;
327
+ ipsecTunnel: boolean;
328
+ ipsecUser: boolean;
329
+ // etc ... to be completed
330
+ }
331
+
332
+ function readKeyUsage(oid: string, buffer: Buffer): X509KeyUsage {
333
+ const block_info = readTag(buffer, 0);
334
+
335
+ // get value as BIT STRING
336
+ let b2 = 0x00;
337
+ let b3 = 0x00;
338
+ if (block_info.length > 1) {
339
+ // skip first byte, just indicates unused bits which
340
+ // will be padded with 0s anyway
341
+ // get bytes with flag bits
342
+ b2 = buffer[block_info.position + 1];
343
+ b3 = block_info.length > 2 ? buffer[block_info.position + 2] : 0;
344
+ }
345
+
346
+ // set flags
347
+ return {
348
+ // tslint:disable-next-line: no-bitwise
349
+ digitalSignature: (b2 & 0x80) === 0x80,
350
+ // tslint:disable-next-line: no-bitwise
351
+ nonRepudiation: (b2 & 0x40) === 0x40,
352
+ // tslint:disable-next-line: no-bitwise
353
+ keyEncipherment: (b2 & 0x20) === 0x20,
354
+ // tslint:disable-next-line: no-bitwise
355
+ dataEncipherment: (b2 & 0x10) === 0x10,
356
+ // tslint:disable-next-line: no-bitwise
357
+ keyAgreement: (b2 & 0x08) === 0x08,
358
+ // tslint:disable-next-line: no-bitwise
359
+ keyCertSign: (b2 & 0x04) === 0x04,
360
+ // tslint:disable-next-line: no-bitwise
361
+ cRLSign: (b2 & 0x02) === 0x02,
362
+ // tslint:disable-next-line: no-bitwise
363
+ encipherOnly: (b2 & 0x01) === 0x01,
364
+ // tslint:disable-next-line: no-bitwise
365
+ decipherOnly: (b3 & 0x80) === 0x80,
366
+ };
367
+ }
368
+
369
+ function readExtKeyUsage(oid: string, buffer: Buffer): X509ExtKeyUsage {
370
+ assert(oid === "2.5.29.37");
371
+ // see https://tools.ietf.org/html/rfc5280#section-4.2.1.12
372
+ const block_info = readTag(buffer, 0);
373
+
374
+ const inner_blocks = _readStruct(buffer, block_info);
375
+
376
+ const extKeyUsage: X509ExtKeyUsage = {
377
+ serverAuth: false,
378
+ clientAuth: false,
379
+ codeSigning: false,
380
+ emailProtection: false,
381
+ timeStamping: false,
382
+ ipsecEndSystem: false,
383
+ ipsecTunnel: false,
384
+ ipsecUser: false,
385
+ ocspSigning: false,
386
+ };
387
+ for (const block of inner_blocks) {
388
+ const identifier = _readObjectIdentifier(buffer, block);
389
+ (extKeyUsage as any)[identifier.name] = true;
390
+ }
391
+ /*
392
+
393
+ id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
394
+
395
+ id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
396
+ -- TLS WWW server authentication
397
+ -- Key usage bits that may be consistent: digitalSignature,
398
+ -- keyEncipherment or keyAgreement
399
+
400
+ id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
401
+ -- TLS WWW client authentication
402
+ -- Key usage bits that may be consistent: digitalSignature
403
+ -- and/or keyAgreement
404
+
405
+ id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
406
+ -- Signing of downloadable executable code
407
+ -- Key usage bits that may be consistent: digitalSignature
408
+
409
+ id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
410
+ -- Email protection
411
+ -- Key usage bits that may be consistent: digitalSignature,
412
+ -- nonRepudiation, and/or (keyEncipherment or keyAgreement)
413
+
414
+ id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
415
+ -- Binding the hash of an object to a time
416
+ -- Key usage bits that may be consistent: digitalSignature
417
+ -- and/or nonRepudiation
418
+
419
+ id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
420
+ -- Signing OCSP responses
421
+ -- Key usage bits that may be consistent: digitalSignature
422
+ -- and/or nonRepudiation
423
+
424
+ */
425
+ // set flags
426
+ return extKeyUsage;
427
+ }
428
+
429
+ export interface SubjectPublicKey {
430
+ modulus: Buffer;
431
+ }
432
+ function _readSubjectPublicKey(buffer: Buffer): SubjectPublicKey {
433
+ const block_info = readTag(buffer, 0);
434
+ const blocks = _readStruct(buffer, block_info);
435
+
436
+ return {
437
+ modulus: buffer.slice(blocks[0].position + 1, blocks[0].position + blocks[0].length),
438
+ };
439
+ }
440
+ /*
441
+ Extension ::= SEQUENCE {
442
+ extnID OBJECT IDENTIFIER,
443
+ critical BOOLEAN DEFAULT FALSE,
444
+ extnValue OCTET STRING
445
+ -- contains the DER encoding of an ASN.1 value
446
+ -- corresponding to the extension type identified
447
+ -- by extnID
448
+ }
449
+ */
450
+ export function _readExtension(buffer: Buffer, block: BlockInfo): { identifier: { oid: string; name: string }; value: any } {
451
+ const inner_blocks = _readStruct(buffer, block);
452
+
453
+ if (inner_blocks.length === 3) {
454
+ assert(inner_blocks[1].tag === TagType.BOOLEAN);
455
+ inner_blocks[1] = inner_blocks[2];
456
+ }
457
+
458
+ const identifier = _readObjectIdentifier(buffer, inner_blocks[0]);
459
+ const buf = _getBlock(buffer, inner_blocks[1]);
460
+ let value = null;
461
+ switch (identifier.name) {
462
+ case "subjectKeyIdentifier":
463
+ /* from https://tools.ietf.org/html/rfc3280#section-4.1 :
464
+ For CA certificates, subject key identifiers SHOULD be derived from
465
+ the public key or a method that generates unique values. Two common
466
+ methods for generating key identifiers from the public key are:
467
+
468
+ (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
469
+ value of the BIT STRING subjectPublicKey (excluding the tag,
470
+ length, and number of unused bits).
471
+
472
+ (2) The keyIdentifier is composed of a four bit type field with
473
+ the value 0100 followed by the least significant 60 bits of the
474
+ SHA-1 hash of the value of the BIT STRING subjectPublicKey
475
+ (excluding the tag, length, and number of unused bit string bits).
476
+ */
477
+ value = formatBuffer2DigitHexWithColum(_readOctetString(buffer, inner_blocks[1]));
478
+ break;
479
+ case "subjectAltName":
480
+ value = _readSubjectAltNames(buf);
481
+ break;
482
+ case "authorityKeyIdentifier":
483
+ value = _readAuthorityKeyIdentifier(buf);
484
+ break;
485
+ case "basicConstraints":
486
+ value = readBasicConstraint2_5_29_19(buf, inner_blocks[1]); // "2.5.29.19":
487
+ // "basicConstraints ( not implemented yet) " + buf.toString("hex");
488
+ break;
489
+ case "certExtension": // Netscape
490
+ value = "basicConstraints ( not implemented yet) " + buf.toString("hex");
491
+ break;
492
+ case "extKeyUsage":
493
+ value = readExtKeyUsage(identifier.oid, buf);
494
+ break;
495
+ case "keyUsage":
496
+ value = readKeyUsage(identifier.oid, buf);
497
+ break;
498
+ default:
499
+ value = "Unknown " + identifier.name + buf.toString("hex");
500
+ }
501
+ return {
502
+ identifier,
503
+ value,
504
+ };
505
+ }
506
+
507
+ // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
508
+ function _readExtensions(buffer: Buffer, block: BlockInfo): CertificateExtension {
509
+ assert(block.tag === 0xa3);
510
+ let inner_blocks = _readStruct(buffer, block);
511
+ inner_blocks = _readStruct(buffer, inner_blocks[0]);
512
+
513
+ const extensions = inner_blocks.map((block) => _readExtension(buffer, block));
514
+
515
+ const result: any = {};
516
+ for (const e of extensions) {
517
+ result[e.identifier.name] = e.value;
518
+ }
519
+ return result as CertificateExtension;
520
+ }
521
+
522
+ /*
523
+ SEQUENCE {
524
+ 204 13: SEQUENCE {
525
+ 206 9: OBJECT IDENTIFIER
526
+ : rsaEncryption (1 2 840 113549 1 1 1)
527
+ 217 0: NULL
528
+ : }
529
+ 219 141: BIT STRING, encapsulates {
530
+ 223 137: SEQUENCE {
531
+ 226 129: INTEGER
532
+ : 00 C2 D7 97 6D 28 70 AA 5B CF 23 2E 80 70 39 EE
533
+ : DB 6F D5 2D D5 6A 4F 7A 34 2D F9 22 72 47 70 1D
534
+ : EF 80 E9 CA 30 8C 00 C4 9A 6E 5B 45 B4 6E A5 E6
535
+ : 6C 94 0D FA 91 E9 40 FC 25 9D C7 B7 68 19 56 8F
536
+ : 11 70 6A D7 F1 C9 11 4F 3A 7E 3F 99 8D 6E 76 A5
537
+ : 74 5F 5E A4 55 53 E5 C7 68 36 53 C7 1D 3B 12 A6
538
+ : 85 FE BD 6E A1 CA DF 35 50 AC 08 D7 B9 B4 7E 5C
539
+ : FE E2 A3 2C D1 23 84 AA 98 C0 9B 66 18 9A 68 47
540
+ : E9
541
+ 358 3: INTEGER 65537
542
+ : }
543
+ : }
544
+ : }
545
+ */
546
+
547
+ function _readSubjectPublicKeyInfo(buffer: Buffer, block: BlockInfo): SubjectPublicKeyInfo {
548
+ const inner_blocks = _readStruct(buffer, block);
549
+
550
+ // algorithm identifier
551
+ const algorithm = _readAlgorithmIdentifier(buffer, inner_blocks[0]);
552
+ //const parameters = _readBitString(buffer,inner_blocks[1]);
553
+ const subjectPublicKey = _readBitString(buffer, inner_blocks[1]);
554
+
555
+ // read the 2 big integers of the key
556
+ const data = subjectPublicKey.data;
557
+ const values = _readListOfInteger(data);
558
+ // xx const value = _readListOfInteger(data);
559
+ return {
560
+ algorithm: algorithm.identifier,
561
+ keyLength: (values[0].length - 1) as PublicKeyLength,
562
+ subjectPublicKey: _readSubjectPublicKey(subjectPublicKey.data),
563
+ //xx values: values,
564
+ //xx values_length : values.map(function (a){ return a.length; })
565
+ };
566
+ }
567
+
568
+ function _readSubjectECCPublicKeyInfo(buffer: Buffer, block: BlockInfo): SubjectPublicKeyInfo {
569
+ const inner_blocks = _readStruct(buffer, block);
570
+
571
+ // first parameter is the second element of the first block, which is why we have another algorithm
572
+ const algorithm = _readECCAlgorithmIdentifier(buffer, inner_blocks[0]);
573
+
574
+ // the public key is already in bit format, we just need to read it
575
+ const subjectPublicKey = _readBitString(buffer, inner_blocks[1]);
576
+
577
+ // take out the data which is the entirity of our public key
578
+ const data = subjectPublicKey.data;
579
+ return {
580
+ algorithm: algorithm.identifier,
581
+ keyLength: (data.length - 1) as PublicKeyLength,
582
+ subjectPublicKey: {
583
+ modulus: data
584
+ }
585
+ };
586
+ }
587
+
588
+ export interface SubjectPublicKeyInfo {
589
+ algorithm: string;
590
+ keyLength: PublicKeyLength;
591
+ subjectPublicKey: SubjectPublicKey;
592
+ }
593
+
594
+ export interface BasicConstraints {
595
+ critical: boolean;
596
+ cA: boolean;
597
+ pathLengthConstraint?: number; // 0 Unlimited
598
+ }
599
+
600
+ export interface AuthorityKeyIdentifier {
601
+ keyIdentifier: string | null;
602
+ authorityCertIssuer: DirectoryName | null;
603
+ authorityCertIssuerFingerPrint: string;
604
+ serial: string | null;
605
+ }
606
+
607
+ export interface CertificateExtension {
608
+ basicConstraints: BasicConstraints;
609
+ subjectKeyIdentifier?: string;
610
+ authorityKeyIdentifier?: AuthorityKeyIdentifier;
611
+ keyUsage?: X509KeyUsage;
612
+ extKeyUsage?: X509ExtKeyUsage;
613
+ subjectAltName?: any;
614
+ }
615
+
616
+ export interface TbsCertificate {
617
+ version: number;
618
+ serialNumber: string;
619
+ issuer: any;
620
+ signature: AlgorithmIdentifier;
621
+ validity: Validity;
622
+ subject: DirectoryName;
623
+ subjectFingerPrint: string;
624
+ subjectPublicKeyInfo: SubjectPublicKeyInfo;
625
+ extensions: CertificateExtension | null;
626
+ }
627
+
628
+ export function readTbsCertificate(buffer: Buffer, block: BlockInfo): TbsCertificate {
629
+ const blocks = _readStruct(buffer, block);
630
+
631
+ let version, serialNumber, signature, issuer, validity, subject, subjectFingerPrint, extensions;
632
+ let subjectPublicKeyInfo: SubjectPublicKeyInfo;
633
+
634
+ if (blocks.length === 6) {
635
+ // X509 Version 1:
636
+ version = 1;
637
+
638
+ serialNumber = formatBuffer2DigitHexWithColum(_readLongIntegerValue(buffer, blocks[0]));
639
+ signature = _readAlgorithmIdentifier(buffer, blocks[1]);
640
+ issuer = _readName(buffer, blocks[2]);
641
+ validity = _readValidity(buffer, blocks[3]);
642
+ subject = _readName(buffer, blocks[4]);
643
+ subjectFingerPrint = formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, blocks[4])));
644
+ subjectPublicKeyInfo = _readSubjectPublicKeyInfo(buffer, blocks[5]);
645
+
646
+ extensions = null;
647
+ } else {
648
+ // X509 Version 3:
649
+ const version_block = _findBlockAtIndex(blocks, 0);
650
+ if (!version_block) {
651
+ throw new Error("cannot find version block");
652
+ }
653
+ version = _readVersionValue(buffer, version_block) + 1;
654
+ serialNumber = formatBuffer2DigitHexWithColum(_readLongIntegerValue(buffer, blocks[1]));
655
+ signature = _readAlgorithmIdentifier(buffer, blocks[2]);
656
+ issuer = _readName(buffer, blocks[3]);
657
+ validity = _readValidity(buffer, blocks[4]);
658
+ subject = _readName(buffer, blocks[5]);
659
+ subjectFingerPrint = formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, blocks[5])));
660
+
661
+ const inner_block = _readStruct(buffer, blocks[6])
662
+ const what_type = _readAlgorithmIdentifier(buffer, inner_block[0]).identifier
663
+
664
+ switch (what_type) {
665
+ case "rsaEncryption": {
666
+ subjectPublicKeyInfo = _readSubjectPublicKeyInfo(buffer, blocks[6]);
667
+ break;
668
+ }
669
+ case "ecPublicKey":
670
+ default: {
671
+ subjectPublicKeyInfo = _readSubjectECCPublicKeyInfo(buffer, blocks[6]);
672
+ break;
673
+ }
674
+ }
675
+
676
+ const extensionBlock = _findBlockAtIndex(blocks, 3);
677
+ if (!extensionBlock) {
678
+ // tslint:disable-next-line: no-console
679
+ console.log("X509 certificate is invalid : cannot find extension block version =" + version_block);
680
+ extensions = null;
681
+ } else {
682
+ extensions = _readExtensions(buffer, extensionBlock);
683
+ }
684
+ }
685
+
686
+ return {
687
+ version,
688
+ serialNumber,
689
+ signature,
690
+ issuer,
691
+ validity,
692
+ subject,
693
+ subjectFingerPrint,
694
+ subjectPublicKeyInfo,
695
+ extensions,
696
+ };
697
+ }
698
+ export interface CertificateInternals {
699
+ tbsCertificate: TbsCertificate;
700
+ signatureAlgorithm: AlgorithmIdentifier;
701
+ signatureValue: SignatureValue;
702
+ }
703
+
704
+ /**
705
+ * explore a certificate structure
706
+ * @param certificate
707
+ * @returns a json object that exhibits the internal data of the certificate
708
+ */
709
+ export function exploreCertificate(certificate: Certificate): CertificateInternals {
710
+ assert(certificate instanceof Buffer);
711
+ if (!(certificate as any)._exploreCertificate_cache) {
712
+ const block_info = readTag(certificate, 0);
713
+ const blocks = _readStruct(certificate, block_info);
714
+ (certificate as any)._exploreCertificate_cache = {
715
+ tbsCertificate: readTbsCertificate(certificate, blocks[0]),
716
+ signatureAlgorithm: _readAlgorithmIdentifier(certificate, blocks[1]),
717
+ signatureValue: _readSignatureValue(certificate, blocks[2]),
718
+ };
719
+ }
720
+ return (certificate as any)._exploreCertificate_cache;
721
+ }
722
+
723
+ /**
724
+ * @method split_der
725
+ * split a multi chain certificates
726
+ * @param certificateChain the certificate chain in der (binary) format}
727
+ * @returns an array of Der , each element of the array is one certificate of the chain
728
+ */
729
+ export function split_der(certificateChain: Certificate): Certificate[] {
730
+ const certificate_chain: Buffer[] = [];
731
+
732
+ do {
733
+ const block_info = readTag(certificateChain, 0);
734
+ const length = block_info.position + block_info.length;
735
+ const der_certificate = certificateChain.slice(0, length);
736
+ certificate_chain.push(der_certificate);
737
+ certificateChain = certificateChain.slice(length);
738
+ } while (certificateChain.length > 0);
739
+ return certificate_chain;
740
+ }
741
+
742
+ /**
743
+ * @method combine_der
744
+ * combine an array of certificates into a single blob
745
+ * @param certificates a array with the individual DER certificates of the chain
746
+ * @return a concatenated buffer containing the certificates
747
+ */
748
+ export function combine_der(certificates: Certificate[]): Certificate {
749
+ // perform some sanity check
750
+ for (const cert of certificates) {
751
+ const b = split_der(cert);
752
+ let sum = 0;
753
+ b.forEach((block) => {
754
+ const block_info = readTag(block, 0);
755
+ //xx console.log("xxxx" ,cert.length,block_info);
756
+ //xx console.log(cert.toString("base64"));
757
+ assert(block_info.position + block_info.length === block.length);
758
+ sum += block.length;
759
+ });
760
+ assert(sum === cert.length);
761
+ }
762
+ return Buffer.concat(certificates);
763
+ }