node-opcua-pki 3.0.2 → 3.1.1

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 (53) hide show
  1. package/.ignore +6 -6
  2. package/.prettierrc +5 -5
  3. package/LICENSE +22 -22
  4. package/bin/crypto_create_CA.js +0 -0
  5. package/bin/crypto_create_CA_config.example.js +18 -18
  6. package/bin/install_prerequisite.js +9 -9
  7. package/dist/crypto_create_CA.d.ts +2 -2
  8. package/dist/crypto_create_CA.js +897 -897
  9. package/dist/index.d.ts +6 -6
  10. package/dist/index.js +44 -44
  11. package/dist/misc/applicationurn.d.ts +1 -1
  12. package/dist/misc/applicationurn.js +46 -46
  13. package/dist/misc/hostname.d.ts +8 -8
  14. package/dist/misc/hostname.js +102 -102
  15. package/dist/misc/install_prerequisite.d.ts +9 -9
  16. package/dist/misc/install_prerequisite.js +363 -360
  17. package/dist/misc/install_prerequisite.js.map +1 -1
  18. package/dist/misc/subject.d.ts +26 -26
  19. package/dist/misc/subject.js +121 -121
  20. package/dist/pki/certificate_authority.d.ts +61 -61
  21. package/dist/pki/certificate_authority.js +481 -481
  22. package/dist/pki/certificate_manager.d.ts +144 -144
  23. package/dist/pki/certificate_manager.js +883 -883
  24. package/dist/pki/certificate_manager.js.map +1 -1
  25. package/dist/pki/common.d.ts +5 -5
  26. package/dist/pki/common.js +2 -2
  27. package/dist/pki/templates/ca_config_template.cnf.d.ts +2 -2
  28. package/dist/pki/templates/ca_config_template.cnf.js +129 -129
  29. package/dist/pki/templates/simple_config_template.cnf.d.ts +2 -2
  30. package/dist/pki/templates/simple_config_template.cnf.js +75 -75
  31. package/dist/pki/toolbox.d.ts +160 -160
  32. package/dist/pki/toolbox.js +699 -699
  33. package/dist/pki/toolbox_pfx.js +18 -18
  34. package/lib/crypto_create_CA.ts +1135 -1135
  35. package/lib/index.ts +28 -28
  36. package/lib/misc/applicationurn.ts +45 -45
  37. package/lib/misc/hostname.ts +89 -89
  38. package/lib/misc/install_prerequisite.ts +454 -454
  39. package/lib/misc/subject.ts +141 -141
  40. package/lib/pki/certificate_manager.ts +1 -1
  41. package/lib/pki/common.ts +5 -5
  42. package/lib/pki/templates/ca_config_template.cnf.ts +129 -129
  43. package/lib/pki/templates/simple_config_template.cnf.ts +75 -75
  44. package/lib/pki/toolbox_pfx.ts +19 -19
  45. package/package.json +89 -89
  46. package/readme.md +214 -214
  47. package/tsconfig.json +20 -20
  48. package/dist/misc/fs.d.ts +0 -24
  49. package/dist/misc/fs.js +0 -21
  50. package/dist/misc/fs.js.map +0 -1
  51. package/dist/misc/get_default_filesystem.d.ts +0 -2
  52. package/dist/misc/get_default_filesystem.js +0 -9
  53. package/dist/misc/get_default_filesystem.js.map +0 -1
@@ -1,482 +1,482 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CertificateAuthority = exports.defaultSubject = void 0;
4
- // ---------------------------------------------------------------------------------------------------------------------
5
- // node-opcua
6
- // ---------------------------------------------------------------------------------------------------------------------
7
- // Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
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
- // tslint:disable:no-shadowed-variable
27
- const assert = require("assert");
28
- const async = require("async");
29
- const chalk = require("chalk");
30
- const fs = require("fs");
31
- const path = require("path");
32
- const toolbox_1 = require("./toolbox");
33
- const subject_1 = require("../misc/subject");
34
- const node_opcua_crypto_1 = require("node-opcua-crypto");
35
- exports.defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
36
- const config = {
37
- certificateDir: "INVALID",
38
- forceCA: false,
39
- pkiDir: "INVALID",
40
- };
41
- const n = toolbox_1.make_path;
42
- const q = toolbox_1.quote;
43
- // convert 'c07b9179' to "192.123.145.121"
44
- function octetStringToIpAddress(a) {
45
- return (parseInt(a.substr(0, 2), 16).toString() +
46
- "." +
47
- parseInt(a.substr(2, 2), 16).toString() +
48
- "." +
49
- parseInt(a.substr(4, 2), 16).toString() +
50
- "." +
51
- parseInt(a.substr(6, 2), 16).toString());
52
- }
53
- function construct_CertificateAuthority(certificateAuthority, callback) {
54
- // create the CA directory store
55
- // create the CA directory store
56
- //
57
- // PKI/CA
58
- // |
59
- // +-+> private
60
- // |
61
- // +-+> public
62
- // |
63
- // +-+> certs
64
- // |
65
- // +-+> crl
66
- // |
67
- // +-+> conf
68
- // |
69
- // +-f: serial
70
- // +-f: crlNumber
71
- // +-f: index.txt
72
- //
73
- const subject = certificateAuthority.subject;
74
- const caRootDir = certificateAuthority.rootDir;
75
- function make_folders() {
76
- (0, toolbox_1.mkdir)(caRootDir);
77
- (0, toolbox_1.mkdir)(path.join(caRootDir, "private"));
78
- (0, toolbox_1.mkdir)(path.join(caRootDir, "public"));
79
- // xx execute("chmod 700 private");
80
- (0, toolbox_1.mkdir)(path.join(caRootDir, "certs"));
81
- (0, toolbox_1.mkdir)(path.join(caRootDir, "crl"));
82
- (0, toolbox_1.mkdir)(path.join(caRootDir, "conf"));
83
- }
84
- make_folders();
85
- function construct_default_files() {
86
- const serial = path.join(caRootDir, "serial");
87
- if (!fs.existsSync(serial)) {
88
- fs.writeFileSync(serial, "1000");
89
- }
90
- const crlNumber = path.join(caRootDir, "crlnumber");
91
- if (!fs.existsSync(crlNumber)) {
92
- fs.writeFileSync(crlNumber, "1000");
93
- }
94
- const indexFile = path.join(caRootDir, "index.txt");
95
- if (!fs.existsSync(indexFile)) {
96
- fs.writeFileSync(indexFile, "");
97
- }
98
- }
99
- construct_default_files();
100
- if (fs.existsSync(path.join(caRootDir, "private/cakey.pem")) && !config.forceCA) {
101
- // certificate already exists => do not overwrite
102
- (0, toolbox_1.debugLog)("CA private key already exists ... skipping");
103
- return callback();
104
- }
105
- // tslint:disable:no-empty
106
- (0, toolbox_1.displayTitle)("Create Certificate Authority (CA)", (_err) => { });
107
- const indexFileAttr = path.join(caRootDir, "index.txt.attr");
108
- if (!fs.existsSync(indexFileAttr)) {
109
- fs.writeFileSync(indexFileAttr, "unique_subject = no");
110
- }
111
- const caConfigFile = certificateAuthority.configFile;
112
- // eslint-disable-next-line no-constant-condition
113
- if (1 || !fs.existsSync(caConfigFile)) {
114
- let data = toolbox_1.configurationFileTemplate; // inlineText(configurationFile);
115
- data = data.replace(/%%ROOT_FOLDER%%/, (0, toolbox_1.make_path)(caRootDir));
116
- fs.writeFileSync(caConfigFile, data);
117
- }
118
- // http://www.akadia.com/services/ssh_test_certificate.html
119
- const subjectOpt = " -subj \"" + subject.toString() + "\" ";
120
- const options = { cwd: caRootDir };
121
- (0, toolbox_1.processAltNames)({});
122
- const configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", options);
123
- const configOption = " -config " + q(n(configFile));
124
- const keySize = certificateAuthority.keySize;
125
- const randomFile = "random.rnd";
126
- const tasks = [
127
- (callback) => (0, toolbox_1.displayTitle)("Creating random file random.rnd", callback),
128
- (callback) => (0, toolbox_1.createRandomFileIfNotExist)(randomFile, options, callback),
129
- (callback) => (0, toolbox_1.displayTitle)("Generate the CA private Key - " + keySize, callback),
130
- // The first step is to create your RSA Private Key.
131
- // This key is a 1025,2048,3072 or 2038 bit RSA key which is encrypted using
132
- // Triple-DES and stored in a PEM format so that it is readable as ASCII text.
133
- (callback) => (0, toolbox_1.execute_openssl)("genrsa " + " -out private/cakey.pem" + ((0, toolbox_1.useRandFile)() ? " -rand " + randomFile : "") + " " + keySize, options, callback),
134
- (callback) => (0, toolbox_1.displayTitle)("Generate a certificate request for the CA key", callback),
135
- // Once the private key is generated a Certificate Signing Request can be generated.
136
- // The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
137
- // Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
138
- // The second option is to self-sign the CSR, which will be demonstrated in the next section
139
- (callback) => (0, toolbox_1.execute_openssl)("req -new" +
140
- " -sha256 " +
141
- " -text " +
142
- " -extensions v3_ca" +
143
- configOption +
144
- " -key private/cakey.pem " +
145
- " -out private/cakey.csr " +
146
- subjectOpt, options, callback),
147
- // xx // Step 3: Remove Passphrase from Key
148
- // xx execute("cp private/cakey.pem private/cakey.pem.org");
149
- // xx execute(openssl_path + " rsa -in private/cakey.pem.org -out private/cakey.pem -passin pass:"+paraphrase);
150
- (callback) => (0, toolbox_1.displayTitle)("Generate CA Certificate (self-signed)", callback),
151
- (callback) => (0, toolbox_1.execute_openssl)(" x509 -sha256 -req -days 3650 " +
152
- " -text " +
153
- " -extensions v3_ca" +
154
- " -extfile " +
155
- q(n(configFile)) +
156
- " -in private/cakey.csr " +
157
- " -signkey private/cakey.pem " +
158
- " -out public/cacert.pem", options, callback),
159
- (callback) => (0, toolbox_1.displaySubtitle)("generate initial CRL (Certificate Revocation List)", callback),
160
- (callback) => regenerateCrl(certificateAuthority.revocationList, configOption, options, callback),
161
- (callback) => (0, toolbox_1.displayTitle)("Create Certificate Authority (CA) ---> DONE", callback),
162
- ];
163
- async.series(tasks, callback);
164
- }
165
- function regenerateCrl(revocationList, configOption, options, callback) {
166
- const tasks = [
167
- (callback) => (0, toolbox_1.displaySubtitle)("regenerate CRL (Certificate Revocation List)", callback),
168
- (callback) =>
169
- // produce a CRL in PEM format
170
- (0, toolbox_1.execute_openssl)("ca -gencrl " + configOption + " -out crl/revocation_list.crl", options, callback),
171
- (callback) => (0, toolbox_1.execute_openssl)("crl " + " -in crl/revocation_list.crl -out crl/revocation_list.der " + " -outform der", options, callback),
172
- (callback) => (0, toolbox_1.displaySubtitle)("Display (Certificate Revocation List)", callback),
173
- (callback) => (0, toolbox_1.execute_openssl)("crl " + " -in " + q(n(revocationList)) + " -text " + " -noout", options, callback),
174
- ];
175
- async.series(tasks, callback);
176
- }
177
- class CertificateAuthority {
178
- constructor(options) {
179
- assert(Object.prototype.hasOwnProperty.call(options, "location"));
180
- assert(Object.prototype.hasOwnProperty.call(options, "keySize"));
181
- this.location = options.location;
182
- this.keySize = options.keySize || 2048;
183
- this.subject = new subject_1.Subject(options.subject || exports.defaultSubject);
184
- }
185
- get rootDir() {
186
- return this.location;
187
- }
188
- get configFile() {
189
- return path.normalize(path.join(this.rootDir, "./conf/caconfig.cnf"));
190
- }
191
- get caCertificate() {
192
- // the Certificate Authority Certificate
193
- return (0, toolbox_1.make_path)(this.rootDir, "./public/cacert.pem");
194
- }
195
- /**
196
- * the file name where the current Certificate Revocation List is stored (in DER format)
197
- */
198
- get revocationListDER() {
199
- return (0, toolbox_1.make_path)(this.rootDir, "./crl/revocation_list.der");
200
- }
201
- /**
202
- * the file name where the current Certificate Revocation List is stored (in PEM format)
203
- */
204
- get revocationList() {
205
- return (0, toolbox_1.make_path)(this.rootDir, "./crl/revocation_list.crl");
206
- }
207
- get caCertificateWithCrl() {
208
- return (0, toolbox_1.make_path)(this.rootDir, "./public/cacertificate_with_crl.pem");
209
- }
210
- initialize(callback) {
211
- assert(typeof callback === "function");
212
- construct_CertificateAuthority(this, callback);
213
- }
214
- constructCACertificateWithCRL(callback) {
215
- assert(typeof callback === "function");
216
- const cacertWithCRL = this.caCertificateWithCrl;
217
- // note : in order to check if the certificate is revoked,
218
- // you need to specify -crl_check and have both the CA cert and the (applicable) CRL in your trust store.
219
- // There are two ways to do that:
220
- // 1. concatenate cacert.pem and crl.pem into one file and use that for -CAfile.
221
- // 2. use some linked
222
- // ( from http://security.stackexchange.com/a/58305/59982)
223
- if (fs.existsSync(this.revocationList)) {
224
- fs.writeFileSync(cacertWithCRL, fs.readFileSync(this.caCertificate, "utf8") + fs.readFileSync(this.revocationList, "utf8"));
225
- }
226
- else {
227
- // there is no revocation list yet
228
- fs.writeFileSync(cacertWithCRL, fs.readFileSync(this.caCertificate));
229
- }
230
- callback();
231
- }
232
- constructCertificateChain(certificate, callback) {
233
- assert(typeof callback === "function");
234
- assert(fs.existsSync(certificate));
235
- assert(fs.existsSync(this.caCertificate));
236
- (0, toolbox_1.debugLog)(chalk.yellow(" certificate file :"), chalk.cyan(certificate));
237
- // append
238
- fs.writeFileSync(certificate, fs.readFileSync(certificate, "utf8") + fs.readFileSync(this.caCertificate, "utf8")
239
- // + fs.readFileSync(this.revocationList)
240
- );
241
- callback();
242
- }
243
- createSelfSignedCertificate(certificateFile, privateKey, params, callback) {
244
- assert(typeof privateKey === "string");
245
- assert(fs.existsSync(privateKey));
246
- assert(typeof callback === "function");
247
- if (!(0, toolbox_1.certificateFileExist)(certificateFile)) {
248
- return callback();
249
- }
250
- (0, toolbox_1.adjustDate)(params);
251
- (0, toolbox_1.adjustApplicationUri)(params);
252
- (0, toolbox_1.processAltNames)(params);
253
- const csrFile = certificateFile + "_csr";
254
- assert(csrFile);
255
- const configFile = (0, toolbox_1.generateStaticConfig)(this.configFile);
256
- const options = {
257
- cwd: this.rootDir,
258
- openssl_conf: (0, toolbox_1.make_path)(configFile),
259
- };
260
- const configOption = "";
261
- const subject = params.subject ? new subject_1.Subject(params.subject).toString() : "";
262
- const subjectOptions = subject && subject.length > 1 ? " -subj " + subject + " " : "";
263
- const tasks = [];
264
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- the certificate signing request", callback));
265
- tasks.push((callback) => (0, toolbox_1.execute_openssl)("req " +
266
- " -new -sha256 -text " +
267
- configOption +
268
- subjectOptions +
269
- " -batch -key " +
270
- q(n(privateKey)) +
271
- " -out " +
272
- q(n(csrFile)), options, callback));
273
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- creating the self-signed certificate", callback));
274
- tasks.push((callback) => (0, toolbox_1.execute_openssl)("ca " +
275
- " -selfsign " +
276
- " -keyfile " +
277
- q(n(privateKey)) +
278
- " -startdate " +
279
- (0, toolbox_1.x509Date)(params.startDate) +
280
- " -enddate " +
281
- (0, toolbox_1.x509Date)(params.endDate) +
282
- " -batch -out " +
283
- q(n(certificateFile)) +
284
- " -in " +
285
- q(n(csrFile)), options, callback));
286
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- dump the certificate for a check", callback));
287
- tasks.push((callback) => (0, toolbox_1.execute_openssl)("x509 -in " + q(n(certificateFile)) + " -dates -fingerprint -purpose -noout", {}, callback));
288
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- verify self-signed certificate", callback));
289
- tasks.push((callback) => (0, toolbox_1.execute_openssl_no_failure)("verify -verbose -CAfile " + q(n(certificateFile)) + " " + q(n(certificateFile)), options, callback));
290
- tasks.push((callback) => fs.unlink(csrFile, callback));
291
- async.series(tasks, (err) => {
292
- callback(err);
293
- });
294
- }
295
- revokeCertificate(certificate, params, callback) {
296
- assert(typeof callback === "function");
297
- const crlReasons = [
298
- "unspecified",
299
- "keyCompromise",
300
- "CACompromise",
301
- "affiliationChanged",
302
- "superseded",
303
- "cessationOfOperation",
304
- "certificateHold",
305
- "removeFromCRL",
306
- ];
307
- const configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", { cwd: this.rootDir });
308
- const options = {
309
- cwd: this.rootDir,
310
- openssl_conf: (0, toolbox_1.make_path)(configFile),
311
- };
312
- (0, toolbox_1.setEnv)("ALTNAME", "");
313
- const randomFile = path.join(this.rootDir, "random.rnd");
314
- (0, toolbox_1.setEnv)("RANDFILE", randomFile);
315
- // // tslint:disable-next-line:no-string-literal
316
- // if (!fs.existsSync((process.env as any)["OPENSSL_CONF"])) {
317
- // throw new Error("Cannot find OPENSSL_CONF");
318
- // }
319
- const configOption = " -config " + q(n(configFile));
320
- const reason = params.reason || "keyCompromise";
321
- assert(crlReasons.indexOf(reason) >= 0);
322
- const tasks = [
323
- (callback) => (0, toolbox_1.displayTitle)("Revoking certificate " + certificate, callback),
324
- (callback) => (0, toolbox_1.displaySubtitle)("Make sure random file exists" + randomFile, callback),
325
- (callback) => (0, toolbox_1.createRandomFileIfNotExist)(randomFile, {}, callback),
326
- (callback) => (0, toolbox_1.displaySubtitle)("Revoke certificate", callback),
327
- (callback) => {
328
- (0, toolbox_1.execute_openssl_no_failure)("ca -verbose " + configOption + " -revoke " + q(certificate) + " -crl_reason " + reason, options, callback);
329
- },
330
- // regenerate CRL (Certificate Revocation List)
331
- (callback) => regenerateCrl(this.revocationList, configOption, options, callback),
332
- (callback) => (0, toolbox_1.displaySubtitle)("Verify that certificate is revoked ", callback),
333
- (callback) => {
334
- (0, toolbox_1.execute_openssl_no_failure)("verify -verbose" +
335
- // configOption +
336
- " -CRLfile " +
337
- q(n(this.revocationList)) +
338
- " -CAfile " +
339
- q(n(this.caCertificate)) +
340
- " -crl_check " +
341
- q(n(certificate)), options, (err, output) => {
342
- callback();
343
- });
344
- },
345
- // produce CRL in DER format
346
- (callback) => (0, toolbox_1.displaySubtitle)("Produce CRL in DER form ", callback),
347
- (callback) => (0, toolbox_1.execute_openssl)("crl " + " -in " + q(n(this.revocationList)) + " -out " + "crl/revocation_list.der " + " -outform der", options, callback),
348
- // produce CRL in PEM format with text
349
- (callback) => (0, toolbox_1.displaySubtitle)("Produce CRL in PEM form ", callback),
350
- (callback) => (0, toolbox_1.execute_openssl)("crl " +
351
- " -in " +
352
- q(n(this.revocationList)) +
353
- " -out " +
354
- "crl/revocation_list.pem " +
355
- " -outform pem" +
356
- " -text ", options, callback),
357
- ];
358
- async.series(tasks, callback);
359
- }
360
- signCertificateRequest(certificate, certificateSigningRequestFilename, params, callback) {
361
- // istanbul ignore next
362
- if (!callback) {
363
- throw new Error("Internal Error");
364
- }
365
- (0, toolbox_1.ensure_openssl_installed)(() => {
366
- try {
367
- assert(fs.existsSync(certificateSigningRequestFilename));
368
- assert(typeof callback === "function");
369
- if (!(0, toolbox_1.certificateFileExist)(certificate)) {
370
- return callback(null);
371
- }
372
- (0, toolbox_1.adjustDate)(params);
373
- (0, toolbox_1.adjustApplicationUri)(params);
374
- (0, toolbox_1.processAltNames)(params);
375
- const options = { cwd: this.rootDir };
376
- let configFile;
377
- const tasks = [];
378
- let csrInfo;
379
- // note :
380
- // subjectAltName is not copied across
381
- // see https://github.com/openssl/openssl/issues/10458
382
- tasks.push((callback) => {
383
- (0, node_opcua_crypto_1.readCertificateSigningRequest)(certificateSigningRequestFilename)
384
- .then((csr) => {
385
- csrInfo = (0, node_opcua_crypto_1.exploreCertificateSigningRequest)(csr);
386
- callback();
387
- })
388
- .catch((err) => callback(err));
389
- });
390
- tasks.push((callback) => {
391
- const applicationUri = csrInfo.extensionRequest.subjectAltName.uniformResourceIdentifier[0];
392
- if (typeof applicationUri !== "string") {
393
- return callback(new Error("Cannot find applicationUri in CSR"));
394
- }
395
- const dns = csrInfo.extensionRequest.subjectAltName.dNSName || [];
396
- let ip = csrInfo.extensionRequest.subjectAltName.iPAddress || [];
397
- ip = ip.map(octetStringToIpAddress);
398
- const params = {
399
- applicationUri,
400
- dns,
401
- ip,
402
- };
403
- (0, toolbox_1.processAltNames)(params);
404
- configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", options);
405
- callback();
406
- });
407
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- then we ask the authority to sign the certificate signing request", callback));
408
- tasks.push((callback) => {
409
- const configOption = " -config " + configFile;
410
- (0, toolbox_1.execute_openssl)("ca " +
411
- configOption +
412
- " -startdate " +
413
- (0, toolbox_1.x509Date)(params.startDate) +
414
- " -enddate " +
415
- (0, toolbox_1.x509Date)(params.endDate) +
416
- " -batch -out " +
417
- q(n(certificate)) +
418
- " -in " +
419
- q(n(certificateSigningRequestFilename)), options, callback);
420
- });
421
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- dump the certificate for a check", callback));
422
- tasks.push((callback) => (0, toolbox_1.execute_openssl)("x509 -in " + q(n(certificate)) + " -dates -fingerprint -purpose -noout", options, callback));
423
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- construct CA certificate with CRL", callback));
424
- tasks.push((callback) => {
425
- this.constructCACertificateWithCRL(callback);
426
- });
427
- // construct certificate chain
428
- // concatenate certificate with CA Certificate and revocation list
429
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- construct certificate chain", callback));
430
- tasks.push((callback) => {
431
- this.constructCertificateChain(certificate, callback);
432
- });
433
- // todo
434
- tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- verify certificate against the root CA", callback));
435
- tasks.push((callback) => {
436
- this.verifyCertificate(certificate, callback);
437
- });
438
- async.series(tasks, (err) => {
439
- // istanbul ignore next
440
- if (err) {
441
- return callback(err);
442
- }
443
- callback(null, certificate);
444
- });
445
- }
446
- catch (err) {
447
- callback(err);
448
- }
449
- });
450
- }
451
- verifyCertificate(certificate, callback) {
452
- // openssl verify crashes on windows! we cannot use it reliably
453
- // istanbul ignore next
454
- const isImplemented = false;
455
- // istanbul ignore next
456
- if (isImplemented) {
457
- const options = { cwd: this.rootDir };
458
- const configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", options);
459
- (0, toolbox_1.setEnv)("OPENSSL_CONF", (0, toolbox_1.make_path)(configFile));
460
- const configOption = " -config " + configFile;
461
- (0, toolbox_1.execute_openssl_no_failure)("verify -verbose " + " -CAfile " + q(n(this.caCertificateWithCrl)) + " " + q(n(certificate)), options, (err) => {
462
- callback(err ? err : undefined);
463
- });
464
- }
465
- else {
466
- return callback();
467
- }
468
- }
469
- }
470
- exports.CertificateAuthority = CertificateAuthority;
471
- // tslint:disable:no-var-requires
472
- // eslint-disable-next-line @typescript-eslint/no-var-requires
473
- const thenify = require("thenify");
474
- const opts = { multiArgs: false };
475
- CertificateAuthority.prototype.initialize = thenify.withCallback(CertificateAuthority.prototype.initialize, opts);
476
- CertificateAuthority.prototype.constructCACertificateWithCRL = thenify.withCallback(CertificateAuthority.prototype.constructCACertificateWithCRL, opts);
477
- CertificateAuthority.prototype.constructCertificateChain = thenify.withCallback(CertificateAuthority.prototype.constructCertificateChain, opts);
478
- CertificateAuthority.prototype.createSelfSignedCertificate = thenify.withCallback(CertificateAuthority.prototype.createSelfSignedCertificate, opts);
479
- CertificateAuthority.prototype.revokeCertificate = thenify.withCallback(CertificateAuthority.prototype.revokeCertificate, opts);
480
- CertificateAuthority.prototype.verifyCertificate = thenify.withCallback(CertificateAuthority.prototype.verifyCertificate, opts);
481
- CertificateAuthority.prototype.signCertificateRequest = thenify.withCallback(CertificateAuthority.prototype.signCertificateRequest, opts);
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CertificateAuthority = exports.defaultSubject = void 0;
4
+ // ---------------------------------------------------------------------------------------------------------------------
5
+ // node-opcua
6
+ // ---------------------------------------------------------------------------------------------------------------------
7
+ // Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
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
+ // tslint:disable:no-shadowed-variable
27
+ const assert = require("assert");
28
+ const async = require("async");
29
+ const chalk = require("chalk");
30
+ const fs = require("fs");
31
+ const path = require("path");
32
+ const toolbox_1 = require("./toolbox");
33
+ const subject_1 = require("../misc/subject");
34
+ const node_opcua_crypto_1 = require("node-opcua-crypto");
35
+ exports.defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
36
+ const config = {
37
+ certificateDir: "INVALID",
38
+ forceCA: false,
39
+ pkiDir: "INVALID",
40
+ };
41
+ const n = toolbox_1.make_path;
42
+ const q = toolbox_1.quote;
43
+ // convert 'c07b9179' to "192.123.145.121"
44
+ function octetStringToIpAddress(a) {
45
+ return (parseInt(a.substr(0, 2), 16).toString() +
46
+ "." +
47
+ parseInt(a.substr(2, 2), 16).toString() +
48
+ "." +
49
+ parseInt(a.substr(4, 2), 16).toString() +
50
+ "." +
51
+ parseInt(a.substr(6, 2), 16).toString());
52
+ }
53
+ function construct_CertificateAuthority(certificateAuthority, callback) {
54
+ // create the CA directory store
55
+ // create the CA directory store
56
+ //
57
+ // PKI/CA
58
+ // |
59
+ // +-+> private
60
+ // |
61
+ // +-+> public
62
+ // |
63
+ // +-+> certs
64
+ // |
65
+ // +-+> crl
66
+ // |
67
+ // +-+> conf
68
+ // |
69
+ // +-f: serial
70
+ // +-f: crlNumber
71
+ // +-f: index.txt
72
+ //
73
+ const subject = certificateAuthority.subject;
74
+ const caRootDir = certificateAuthority.rootDir;
75
+ function make_folders() {
76
+ (0, toolbox_1.mkdir)(caRootDir);
77
+ (0, toolbox_1.mkdir)(path.join(caRootDir, "private"));
78
+ (0, toolbox_1.mkdir)(path.join(caRootDir, "public"));
79
+ // xx execute("chmod 700 private");
80
+ (0, toolbox_1.mkdir)(path.join(caRootDir, "certs"));
81
+ (0, toolbox_1.mkdir)(path.join(caRootDir, "crl"));
82
+ (0, toolbox_1.mkdir)(path.join(caRootDir, "conf"));
83
+ }
84
+ make_folders();
85
+ function construct_default_files() {
86
+ const serial = path.join(caRootDir, "serial");
87
+ if (!fs.existsSync(serial)) {
88
+ fs.writeFileSync(serial, "1000");
89
+ }
90
+ const crlNumber = path.join(caRootDir, "crlnumber");
91
+ if (!fs.existsSync(crlNumber)) {
92
+ fs.writeFileSync(crlNumber, "1000");
93
+ }
94
+ const indexFile = path.join(caRootDir, "index.txt");
95
+ if (!fs.existsSync(indexFile)) {
96
+ fs.writeFileSync(indexFile, "");
97
+ }
98
+ }
99
+ construct_default_files();
100
+ if (fs.existsSync(path.join(caRootDir, "private/cakey.pem")) && !config.forceCA) {
101
+ // certificate already exists => do not overwrite
102
+ (0, toolbox_1.debugLog)("CA private key already exists ... skipping");
103
+ return callback();
104
+ }
105
+ // tslint:disable:no-empty
106
+ (0, toolbox_1.displayTitle)("Create Certificate Authority (CA)", (_err) => { });
107
+ const indexFileAttr = path.join(caRootDir, "index.txt.attr");
108
+ if (!fs.existsSync(indexFileAttr)) {
109
+ fs.writeFileSync(indexFileAttr, "unique_subject = no");
110
+ }
111
+ const caConfigFile = certificateAuthority.configFile;
112
+ // eslint-disable-next-line no-constant-condition
113
+ if (1 || !fs.existsSync(caConfigFile)) {
114
+ let data = toolbox_1.configurationFileTemplate; // inlineText(configurationFile);
115
+ data = data.replace(/%%ROOT_FOLDER%%/, (0, toolbox_1.make_path)(caRootDir));
116
+ fs.writeFileSync(caConfigFile, data);
117
+ }
118
+ // http://www.akadia.com/services/ssh_test_certificate.html
119
+ const subjectOpt = " -subj \"" + subject.toString() + "\" ";
120
+ const options = { cwd: caRootDir };
121
+ (0, toolbox_1.processAltNames)({});
122
+ const configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", options);
123
+ const configOption = " -config " + q(n(configFile));
124
+ const keySize = certificateAuthority.keySize;
125
+ const randomFile = "random.rnd";
126
+ const tasks = [
127
+ (callback) => (0, toolbox_1.displayTitle)("Creating random file random.rnd", callback),
128
+ (callback) => (0, toolbox_1.createRandomFileIfNotExist)(randomFile, options, callback),
129
+ (callback) => (0, toolbox_1.displayTitle)("Generate the CA private Key - " + keySize, callback),
130
+ // The first step is to create your RSA Private Key.
131
+ // This key is a 1025,2048,3072 or 2038 bit RSA key which is encrypted using
132
+ // Triple-DES and stored in a PEM format so that it is readable as ASCII text.
133
+ (callback) => (0, toolbox_1.execute_openssl)("genrsa " + " -out private/cakey.pem" + ((0, toolbox_1.useRandFile)() ? " -rand " + randomFile : "") + " " + keySize, options, callback),
134
+ (callback) => (0, toolbox_1.displayTitle)("Generate a certificate request for the CA key", callback),
135
+ // Once the private key is generated a Certificate Signing Request can be generated.
136
+ // The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
137
+ // Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
138
+ // The second option is to self-sign the CSR, which will be demonstrated in the next section
139
+ (callback) => (0, toolbox_1.execute_openssl)("req -new" +
140
+ " -sha256 " +
141
+ " -text " +
142
+ " -extensions v3_ca" +
143
+ configOption +
144
+ " -key private/cakey.pem " +
145
+ " -out private/cakey.csr " +
146
+ subjectOpt, options, callback),
147
+ // xx // Step 3: Remove Passphrase from Key
148
+ // xx execute("cp private/cakey.pem private/cakey.pem.org");
149
+ // xx execute(openssl_path + " rsa -in private/cakey.pem.org -out private/cakey.pem -passin pass:"+paraphrase);
150
+ (callback) => (0, toolbox_1.displayTitle)("Generate CA Certificate (self-signed)", callback),
151
+ (callback) => (0, toolbox_1.execute_openssl)(" x509 -sha256 -req -days 3650 " +
152
+ " -text " +
153
+ " -extensions v3_ca" +
154
+ " -extfile " +
155
+ q(n(configFile)) +
156
+ " -in private/cakey.csr " +
157
+ " -signkey private/cakey.pem " +
158
+ " -out public/cacert.pem", options, callback),
159
+ (callback) => (0, toolbox_1.displaySubtitle)("generate initial CRL (Certificate Revocation List)", callback),
160
+ (callback) => regenerateCrl(certificateAuthority.revocationList, configOption, options, callback),
161
+ (callback) => (0, toolbox_1.displayTitle)("Create Certificate Authority (CA) ---> DONE", callback),
162
+ ];
163
+ async.series(tasks, callback);
164
+ }
165
+ function regenerateCrl(revocationList, configOption, options, callback) {
166
+ const tasks = [
167
+ (callback) => (0, toolbox_1.displaySubtitle)("regenerate CRL (Certificate Revocation List)", callback),
168
+ (callback) =>
169
+ // produce a CRL in PEM format
170
+ (0, toolbox_1.execute_openssl)("ca -gencrl " + configOption + " -out crl/revocation_list.crl", options, callback),
171
+ (callback) => (0, toolbox_1.execute_openssl)("crl " + " -in crl/revocation_list.crl -out crl/revocation_list.der " + " -outform der", options, callback),
172
+ (callback) => (0, toolbox_1.displaySubtitle)("Display (Certificate Revocation List)", callback),
173
+ (callback) => (0, toolbox_1.execute_openssl)("crl " + " -in " + q(n(revocationList)) + " -text " + " -noout", options, callback),
174
+ ];
175
+ async.series(tasks, callback);
176
+ }
177
+ class CertificateAuthority {
178
+ constructor(options) {
179
+ assert(Object.prototype.hasOwnProperty.call(options, "location"));
180
+ assert(Object.prototype.hasOwnProperty.call(options, "keySize"));
181
+ this.location = options.location;
182
+ this.keySize = options.keySize || 2048;
183
+ this.subject = new subject_1.Subject(options.subject || exports.defaultSubject);
184
+ }
185
+ get rootDir() {
186
+ return this.location;
187
+ }
188
+ get configFile() {
189
+ return path.normalize(path.join(this.rootDir, "./conf/caconfig.cnf"));
190
+ }
191
+ get caCertificate() {
192
+ // the Certificate Authority Certificate
193
+ return (0, toolbox_1.make_path)(this.rootDir, "./public/cacert.pem");
194
+ }
195
+ /**
196
+ * the file name where the current Certificate Revocation List is stored (in DER format)
197
+ */
198
+ get revocationListDER() {
199
+ return (0, toolbox_1.make_path)(this.rootDir, "./crl/revocation_list.der");
200
+ }
201
+ /**
202
+ * the file name where the current Certificate Revocation List is stored (in PEM format)
203
+ */
204
+ get revocationList() {
205
+ return (0, toolbox_1.make_path)(this.rootDir, "./crl/revocation_list.crl");
206
+ }
207
+ get caCertificateWithCrl() {
208
+ return (0, toolbox_1.make_path)(this.rootDir, "./public/cacertificate_with_crl.pem");
209
+ }
210
+ initialize(callback) {
211
+ assert(typeof callback === "function");
212
+ construct_CertificateAuthority(this, callback);
213
+ }
214
+ constructCACertificateWithCRL(callback) {
215
+ assert(typeof callback === "function");
216
+ const cacertWithCRL = this.caCertificateWithCrl;
217
+ // note : in order to check if the certificate is revoked,
218
+ // you need to specify -crl_check and have both the CA cert and the (applicable) CRL in your trust store.
219
+ // There are two ways to do that:
220
+ // 1. concatenate cacert.pem and crl.pem into one file and use that for -CAfile.
221
+ // 2. use some linked
222
+ // ( from http://security.stackexchange.com/a/58305/59982)
223
+ if (fs.existsSync(this.revocationList)) {
224
+ fs.writeFileSync(cacertWithCRL, fs.readFileSync(this.caCertificate, "utf8") + fs.readFileSync(this.revocationList, "utf8"));
225
+ }
226
+ else {
227
+ // there is no revocation list yet
228
+ fs.writeFileSync(cacertWithCRL, fs.readFileSync(this.caCertificate));
229
+ }
230
+ callback();
231
+ }
232
+ constructCertificateChain(certificate, callback) {
233
+ assert(typeof callback === "function");
234
+ assert(fs.existsSync(certificate));
235
+ assert(fs.existsSync(this.caCertificate));
236
+ (0, toolbox_1.debugLog)(chalk.yellow(" certificate file :"), chalk.cyan(certificate));
237
+ // append
238
+ fs.writeFileSync(certificate, fs.readFileSync(certificate, "utf8") + fs.readFileSync(this.caCertificate, "utf8")
239
+ // + fs.readFileSync(this.revocationList)
240
+ );
241
+ callback();
242
+ }
243
+ createSelfSignedCertificate(certificateFile, privateKey, params, callback) {
244
+ assert(typeof privateKey === "string");
245
+ assert(fs.existsSync(privateKey));
246
+ assert(typeof callback === "function");
247
+ if (!(0, toolbox_1.certificateFileExist)(certificateFile)) {
248
+ return callback();
249
+ }
250
+ (0, toolbox_1.adjustDate)(params);
251
+ (0, toolbox_1.adjustApplicationUri)(params);
252
+ (0, toolbox_1.processAltNames)(params);
253
+ const csrFile = certificateFile + "_csr";
254
+ assert(csrFile);
255
+ const configFile = (0, toolbox_1.generateStaticConfig)(this.configFile);
256
+ const options = {
257
+ cwd: this.rootDir,
258
+ openssl_conf: (0, toolbox_1.make_path)(configFile),
259
+ };
260
+ const configOption = "";
261
+ const subject = params.subject ? new subject_1.Subject(params.subject).toString() : "";
262
+ const subjectOptions = subject && subject.length > 1 ? " -subj " + subject + " " : "";
263
+ const tasks = [];
264
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- the certificate signing request", callback));
265
+ tasks.push((callback) => (0, toolbox_1.execute_openssl)("req " +
266
+ " -new -sha256 -text " +
267
+ configOption +
268
+ subjectOptions +
269
+ " -batch -key " +
270
+ q(n(privateKey)) +
271
+ " -out " +
272
+ q(n(csrFile)), options, callback));
273
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- creating the self-signed certificate", callback));
274
+ tasks.push((callback) => (0, toolbox_1.execute_openssl)("ca " +
275
+ " -selfsign " +
276
+ " -keyfile " +
277
+ q(n(privateKey)) +
278
+ " -startdate " +
279
+ (0, toolbox_1.x509Date)(params.startDate) +
280
+ " -enddate " +
281
+ (0, toolbox_1.x509Date)(params.endDate) +
282
+ " -batch -out " +
283
+ q(n(certificateFile)) +
284
+ " -in " +
285
+ q(n(csrFile)), options, callback));
286
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- dump the certificate for a check", callback));
287
+ tasks.push((callback) => (0, toolbox_1.execute_openssl)("x509 -in " + q(n(certificateFile)) + " -dates -fingerprint -purpose -noout", {}, callback));
288
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- verify self-signed certificate", callback));
289
+ tasks.push((callback) => (0, toolbox_1.execute_openssl_no_failure)("verify -verbose -CAfile " + q(n(certificateFile)) + " " + q(n(certificateFile)), options, callback));
290
+ tasks.push((callback) => fs.unlink(csrFile, callback));
291
+ async.series(tasks, (err) => {
292
+ callback(err);
293
+ });
294
+ }
295
+ revokeCertificate(certificate, params, callback) {
296
+ assert(typeof callback === "function");
297
+ const crlReasons = [
298
+ "unspecified",
299
+ "keyCompromise",
300
+ "CACompromise",
301
+ "affiliationChanged",
302
+ "superseded",
303
+ "cessationOfOperation",
304
+ "certificateHold",
305
+ "removeFromCRL",
306
+ ];
307
+ const configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", { cwd: this.rootDir });
308
+ const options = {
309
+ cwd: this.rootDir,
310
+ openssl_conf: (0, toolbox_1.make_path)(configFile),
311
+ };
312
+ (0, toolbox_1.setEnv)("ALTNAME", "");
313
+ const randomFile = path.join(this.rootDir, "random.rnd");
314
+ (0, toolbox_1.setEnv)("RANDFILE", randomFile);
315
+ // // tslint:disable-next-line:no-string-literal
316
+ // if (!fs.existsSync((process.env as any)["OPENSSL_CONF"])) {
317
+ // throw new Error("Cannot find OPENSSL_CONF");
318
+ // }
319
+ const configOption = " -config " + q(n(configFile));
320
+ const reason = params.reason || "keyCompromise";
321
+ assert(crlReasons.indexOf(reason) >= 0);
322
+ const tasks = [
323
+ (callback) => (0, toolbox_1.displayTitle)("Revoking certificate " + certificate, callback),
324
+ (callback) => (0, toolbox_1.displaySubtitle)("Make sure random file exists" + randomFile, callback),
325
+ (callback) => (0, toolbox_1.createRandomFileIfNotExist)(randomFile, {}, callback),
326
+ (callback) => (0, toolbox_1.displaySubtitle)("Revoke certificate", callback),
327
+ (callback) => {
328
+ (0, toolbox_1.execute_openssl_no_failure)("ca -verbose " + configOption + " -revoke " + q(certificate) + " -crl_reason " + reason, options, callback);
329
+ },
330
+ // regenerate CRL (Certificate Revocation List)
331
+ (callback) => regenerateCrl(this.revocationList, configOption, options, callback),
332
+ (callback) => (0, toolbox_1.displaySubtitle)("Verify that certificate is revoked ", callback),
333
+ (callback) => {
334
+ (0, toolbox_1.execute_openssl_no_failure)("verify -verbose" +
335
+ // configOption +
336
+ " -CRLfile " +
337
+ q(n(this.revocationList)) +
338
+ " -CAfile " +
339
+ q(n(this.caCertificate)) +
340
+ " -crl_check " +
341
+ q(n(certificate)), options, (err, output) => {
342
+ callback();
343
+ });
344
+ },
345
+ // produce CRL in DER format
346
+ (callback) => (0, toolbox_1.displaySubtitle)("Produce CRL in DER form ", callback),
347
+ (callback) => (0, toolbox_1.execute_openssl)("crl " + " -in " + q(n(this.revocationList)) + " -out " + "crl/revocation_list.der " + " -outform der", options, callback),
348
+ // produce CRL in PEM format with text
349
+ (callback) => (0, toolbox_1.displaySubtitle)("Produce CRL in PEM form ", callback),
350
+ (callback) => (0, toolbox_1.execute_openssl)("crl " +
351
+ " -in " +
352
+ q(n(this.revocationList)) +
353
+ " -out " +
354
+ "crl/revocation_list.pem " +
355
+ " -outform pem" +
356
+ " -text ", options, callback),
357
+ ];
358
+ async.series(tasks, callback);
359
+ }
360
+ signCertificateRequest(certificate, certificateSigningRequestFilename, params, callback) {
361
+ // istanbul ignore next
362
+ if (!callback) {
363
+ throw new Error("Internal Error");
364
+ }
365
+ (0, toolbox_1.ensure_openssl_installed)(() => {
366
+ try {
367
+ assert(fs.existsSync(certificateSigningRequestFilename));
368
+ assert(typeof callback === "function");
369
+ if (!(0, toolbox_1.certificateFileExist)(certificate)) {
370
+ return callback(null);
371
+ }
372
+ (0, toolbox_1.adjustDate)(params);
373
+ (0, toolbox_1.adjustApplicationUri)(params);
374
+ (0, toolbox_1.processAltNames)(params);
375
+ const options = { cwd: this.rootDir };
376
+ let configFile;
377
+ const tasks = [];
378
+ let csrInfo;
379
+ // note :
380
+ // subjectAltName is not copied across
381
+ // see https://github.com/openssl/openssl/issues/10458
382
+ tasks.push((callback) => {
383
+ (0, node_opcua_crypto_1.readCertificateSigningRequest)(certificateSigningRequestFilename)
384
+ .then((csr) => {
385
+ csrInfo = (0, node_opcua_crypto_1.exploreCertificateSigningRequest)(csr);
386
+ callback();
387
+ })
388
+ .catch((err) => callback(err));
389
+ });
390
+ tasks.push((callback) => {
391
+ const applicationUri = csrInfo.extensionRequest.subjectAltName.uniformResourceIdentifier[0];
392
+ if (typeof applicationUri !== "string") {
393
+ return callback(new Error("Cannot find applicationUri in CSR"));
394
+ }
395
+ const dns = csrInfo.extensionRequest.subjectAltName.dNSName || [];
396
+ let ip = csrInfo.extensionRequest.subjectAltName.iPAddress || [];
397
+ ip = ip.map(octetStringToIpAddress);
398
+ const params = {
399
+ applicationUri,
400
+ dns,
401
+ ip,
402
+ };
403
+ (0, toolbox_1.processAltNames)(params);
404
+ configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", options);
405
+ callback();
406
+ });
407
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- then we ask the authority to sign the certificate signing request", callback));
408
+ tasks.push((callback) => {
409
+ const configOption = " -config " + configFile;
410
+ (0, toolbox_1.execute_openssl)("ca " +
411
+ configOption +
412
+ " -startdate " +
413
+ (0, toolbox_1.x509Date)(params.startDate) +
414
+ " -enddate " +
415
+ (0, toolbox_1.x509Date)(params.endDate) +
416
+ " -batch -out " +
417
+ q(n(certificate)) +
418
+ " -in " +
419
+ q(n(certificateSigningRequestFilename)), options, callback);
420
+ });
421
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- dump the certificate for a check", callback));
422
+ tasks.push((callback) => (0, toolbox_1.execute_openssl)("x509 -in " + q(n(certificate)) + " -dates -fingerprint -purpose -noout", options, callback));
423
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- construct CA certificate with CRL", callback));
424
+ tasks.push((callback) => {
425
+ this.constructCACertificateWithCRL(callback);
426
+ });
427
+ // construct certificate chain
428
+ // concatenate certificate with CA Certificate and revocation list
429
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- construct certificate chain", callback));
430
+ tasks.push((callback) => {
431
+ this.constructCertificateChain(certificate, callback);
432
+ });
433
+ // todo
434
+ tasks.push((callback) => (0, toolbox_1.displaySubtitle)("- verify certificate against the root CA", callback));
435
+ tasks.push((callback) => {
436
+ this.verifyCertificate(certificate, callback);
437
+ });
438
+ async.series(tasks, (err) => {
439
+ // istanbul ignore next
440
+ if (err) {
441
+ return callback(err);
442
+ }
443
+ callback(null, certificate);
444
+ });
445
+ }
446
+ catch (err) {
447
+ callback(err);
448
+ }
449
+ });
450
+ }
451
+ verifyCertificate(certificate, callback) {
452
+ // openssl verify crashes on windows! we cannot use it reliably
453
+ // istanbul ignore next
454
+ const isImplemented = false;
455
+ // istanbul ignore next
456
+ if (isImplemented) {
457
+ const options = { cwd: this.rootDir };
458
+ const configFile = (0, toolbox_1.generateStaticConfig)("conf/caconfig.cnf", options);
459
+ (0, toolbox_1.setEnv)("OPENSSL_CONF", (0, toolbox_1.make_path)(configFile));
460
+ const configOption = " -config " + configFile;
461
+ (0, toolbox_1.execute_openssl_no_failure)("verify -verbose " + " -CAfile " + q(n(this.caCertificateWithCrl)) + " " + q(n(certificate)), options, (err) => {
462
+ callback(err ? err : undefined);
463
+ });
464
+ }
465
+ else {
466
+ return callback();
467
+ }
468
+ }
469
+ }
470
+ exports.CertificateAuthority = CertificateAuthority;
471
+ // tslint:disable:no-var-requires
472
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
473
+ const thenify = require("thenify");
474
+ const opts = { multiArgs: false };
475
+ CertificateAuthority.prototype.initialize = thenify.withCallback(CertificateAuthority.prototype.initialize, opts);
476
+ CertificateAuthority.prototype.constructCACertificateWithCRL = thenify.withCallback(CertificateAuthority.prototype.constructCACertificateWithCRL, opts);
477
+ CertificateAuthority.prototype.constructCertificateChain = thenify.withCallback(CertificateAuthority.prototype.constructCertificateChain, opts);
478
+ CertificateAuthority.prototype.createSelfSignedCertificate = thenify.withCallback(CertificateAuthority.prototype.createSelfSignedCertificate, opts);
479
+ CertificateAuthority.prototype.revokeCertificate = thenify.withCallback(CertificateAuthority.prototype.revokeCertificate, opts);
480
+ CertificateAuthority.prototype.verifyCertificate = thenify.withCallback(CertificateAuthority.prototype.verifyCertificate, opts);
481
+ CertificateAuthority.prototype.signCertificateRequest = thenify.withCallback(CertificateAuthority.prototype.signCertificateRequest, opts);
482
482
  //# sourceMappingURL=certificate_authority.js.map