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