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