node-opcua-pki 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,700 +1,700 @@
1
- "use strict";
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
- // ---------------------------------------------------------------------------------------------------------------------
4
- // node-opcua-pki
5
- // ---------------------------------------------------------------------------------------------------------------------
6
- // Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
7
- // Copyright (c) 2022 - Sterfive.com
8
- // ---------------------------------------------------------------------------------------------------------------------
9
- //
10
- // This project is licensed under the terms of the MIT license.
11
- //
12
- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13
- // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
14
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
15
- // permit persons to whom the Software is furnished to do so, subject to the following conditions:
16
- //
17
- // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
18
- // Software.
19
- //
20
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21
- // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22
- // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23
- // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
- // ---------------------------------------------------------------------------------------------------------------------
25
- // tslint:disable:no-console
26
- // tslint:disable:no-shadowed-variable
27
- Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.fingerprint = exports.toDer = exports.dumpCertificate = exports.configurationFileSimpleTemplate = exports.configurationFileTemplate = exports.createSelfSignCertificate = exports.processAltNames = exports.certificateFileExist = exports.adjustApplicationUri = exports.adjustDate = exports.CertificatePurpose = exports.x509Date = exports.createCertificateSigningRequest = exports.createRandomFileIfNotExist = exports.createRandomFile = exports.createPrivateKey = exports.getPublicKeyFromCertificate = exports.getPublicKeyFromPrivateKey = exports.make_path = exports.generateStaticConfig = exports.getEnvironmentVarNames = exports.displaySubtitle = exports.displayTitle = exports.displayChapter = exports.execute_openssl_no_failure = exports.executeOpensslAsync = exports.execute_openssl = exports.ensure_openssl_installed = exports.useRandFile = exports.execute = exports.hasEnv = exports.setEnv = exports.mkdir = exports.find_openssl = exports.debugLog = exports.g_config = exports.quote = void 0;
29
- const assert = require("assert");
30
- const async = require("async");
31
- const byline = require("byline");
32
- const chalk = require("chalk");
33
- const child_process = require("child_process");
34
- const fs = require("fs");
35
- const os = require("os");
36
- const path = require("path");
37
- const install_prerequisite_1 = require("../misc/install_prerequisite");
38
- const subject_1 = require("../misc/subject");
39
- const ca_config_template_cnf_1 = require("./templates/ca_config_template.cnf");
40
- const simple_config_template_cnf_1 = require("./templates/simple_config_template.cnf");
41
- const exportedEnvVars = {};
42
- function quote(str) {
43
- return "\"" + (str || "") + "\"";
44
- }
45
- exports.quote = quote;
46
- // tslint:disable-next-line:variable-name
47
- exports.g_config = {
48
- opensslVersion: "unset",
49
- silent: process.env.VERBOSE ? !process.env.VERBOSE : true,
50
- force: false,
51
- };
52
- const doDebug = process.env.NODEOPCUAPKIDEBUG || false;
53
- const displayError = true;
54
- const displayDebug = !!process.env.NODEOPCUAPKIDEBUG || false;
55
- // tslint:disable-next-line:no-empty
56
- function debugLog(...args) {
57
- // istanbul ignore next
58
- if (displayDebug) {
59
- console.log.apply(null, args);
60
- }
61
- }
62
- exports.debugLog = debugLog;
63
- let opensslPath; // not initialized
64
- function find_openssl(callback) {
65
- (0, install_prerequisite_1.get_openssl_exec_path)((err, _opensslPath) => {
66
- opensslPath = _opensslPath;
67
- callback(err, opensslPath);
68
- });
69
- }
70
- exports.find_openssl = find_openssl;
71
- function mkdir(folder) {
72
- if (!fs.existsSync(folder)) {
73
- // istanbul ignore next
74
- if (!exports.g_config.silent) {
75
- console.log(chalk.white(" .. constructing "), folder);
76
- }
77
- fs.mkdirSync(folder);
78
- }
79
- }
80
- exports.mkdir = mkdir;
81
- function setEnv(varName, value) {
82
- // istanbul ignore next
83
- if (!exports.g_config.silent) {
84
- console.log(" set " + varName + "=" + value);
85
- }
86
- exportedEnvVars[varName] = value;
87
- if (["OPENSSL_CONF"].indexOf(varName) >= 0) {
88
- process.env[varName] = value;
89
- }
90
- if (["RANDFILE"].indexOf(varName) >= 0) {
91
- process.env[varName] = value;
92
- }
93
- }
94
- exports.setEnv = setEnv;
95
- function hasEnv(varName) {
96
- return Object.prototype.hasOwnProperty.call(exportedEnvVars, varName);
97
- }
98
- exports.hasEnv = hasEnv;
99
- function execute(cmd, options, callback) {
100
- assert(typeof callback === "function");
101
- /// assert(g_config.CARootDir && fs.existsSync(option.CARootDir));
102
- options.cwd = options.cwd || process.cwd();
103
- // istanbul ignore next
104
- if (!exports.g_config.silent) {
105
- console.log(chalk.cyan(" CWD "), options.cwd);
106
- }
107
- const outputs = [];
108
- const child = child_process.exec(cmd, {
109
- cwd: options.cwd,
110
- windowsHide: true,
111
- }, (err) => {
112
- // istanbul ignore next
113
- if (err) {
114
- if (!options.hideErrorMessage) {
115
- const fence = "###########################################";
116
- console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
117
- console.error(chalk.bgWhiteBright.redBright("CWD = " + options.cwd));
118
- console.error(chalk.bgWhiteBright.redBright(err.message));
119
- console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
120
- }
121
- // console.log(" ERR = ".bgWhite.red, err);
122
- callback(new Error(err.message));
123
- return;
124
- }
125
- callback(null, outputs.join(""));
126
- });
127
- if (child.stdout) {
128
- const stream2 = byline(child.stdout);
129
- stream2.on("data", (line) => {
130
- outputs.push(line + "\n");
131
- });
132
- if (!exports.g_config.silent) {
133
- stream2.on("data", (line) => {
134
- line = line.toString();
135
- if (doDebug) {
136
- process.stdout.write(chalk.white(" stdout ") + chalk.whiteBright(line) + "\n");
137
- }
138
- });
139
- }
140
- }
141
- // istanbul ignore next
142
- if (!exports.g_config.silent) {
143
- if (child.stderr) {
144
- const stream1 = byline(child.stderr);
145
- stream1.on("data", (line) => {
146
- line = line.toString();
147
- if (displayError) {
148
- process.stdout.write(chalk.white(" stderr ") + chalk.red(line) + "\n");
149
- }
150
- });
151
- }
152
- }
153
- }
154
- exports.execute = execute;
155
- function useRandFile() {
156
- // istanbul ignore next
157
- if (exports.g_config.opensslVersion && exports.g_config.opensslVersion.toLowerCase().indexOf("libressl") > -1) {
158
- return false;
159
- }
160
- return true;
161
- }
162
- exports.useRandFile = useRandFile;
163
- function openssl_require2DigitYearInDate() {
164
- // istanbul ignore next
165
- if (!exports.g_config.opensslVersion) {
166
- throw new Error("openssl_require2DigitYearInDate : openssl version is not known:" + " please call ensure_openssl_installed(callback)");
167
- }
168
- return exports.g_config.opensslVersion.match(/OpenSSL 0\.9/);
169
- }
170
- exports.g_config.opensslVersion = "";
171
- function ensure_openssl_installed(callback) {
172
- assert(typeof callback === "function");
173
- if (!opensslPath) {
174
- return find_openssl((err) => {
175
- // istanbul ignore next
176
- if (err) {
177
- return callback(err);
178
- }
179
- execute_openssl("version", { cwd: "." }, (err, outputs) => {
180
- // istanbul ignore next
181
- if (err || !outputs) {
182
- return callback(err || new Error("no outputs"));
183
- }
184
- exports.g_config.opensslVersion = outputs.trim();
185
- if (doDebug) {
186
- console.log("OpenSSL version : ", exports.g_config.opensslVersion);
187
- }
188
- callback(err ? err : undefined);
189
- });
190
- });
191
- }
192
- else {
193
- return callback();
194
- }
195
- }
196
- exports.ensure_openssl_installed = ensure_openssl_installed;
197
- function getTempFolder() {
198
- return os.tmpdir();
199
- }
200
- function execute_openssl(cmd, options, callback) {
201
- // tslint:disable-next-line:variable-name
202
- const empty_config_file = n(getTempFolder(), "empty_config.cnf");
203
- if (!fs.existsSync(empty_config_file)) {
204
- fs.writeFileSync(empty_config_file, "# empty config file");
205
- }
206
- assert(typeof callback === "function");
207
- options = options || {};
208
- options.openssl_conf = options.openssl_conf || empty_config_file; // "!! OPEN SLL CONF NOT DEFINED BAD FILE !!";
209
- assert(options.openssl_conf);
210
- setEnv("OPENSSL_CONF", options.openssl_conf);
211
- // istanbul ignore next
212
- if (!exports.g_config.silent) {
213
- console.log(chalk.cyan(" OPENSSL_CONF"), process.env.OPENSSL_CONF);
214
- console.log(chalk.cyan(" RANDFILE "), process.env.RANDFILE);
215
- console.log(chalk.cyan(" CMD openssl "), chalk.cyanBright(cmd));
216
- }
217
- ensure_openssl_installed((err) => {
218
- // istanbul ignore next
219
- if (err) {
220
- return callback(err);
221
- }
222
- execute(quote(opensslPath) + " " + cmd, options, callback);
223
- });
224
- }
225
- exports.execute_openssl = execute_openssl;
226
- function executeOpensslAsync(cmd, options) {
227
- return new Promise((resolve, reject) => {
228
- execute_openssl(cmd, options, (err, output) => {
229
- // istanbul ignore next
230
- if (err) {
231
- reject(err);
232
- }
233
- else {
234
- resolve(output || "");
235
- }
236
- });
237
- });
238
- }
239
- exports.executeOpensslAsync = executeOpensslAsync;
240
- function execute_openssl_no_failure(cmd, options, callback) {
241
- options = options || {};
242
- options.hideErrorMessage = true;
243
- execute_openssl(cmd, options, (err, output) => {
244
- // istanbul ignore next
245
- if (err) {
246
- debugLog(" (ignored error = ERROR : )", err.message);
247
- }
248
- callback(null, output);
249
- });
250
- }
251
- exports.execute_openssl_no_failure = execute_openssl_no_failure;
252
- // istanbul ignore next
253
- function displayChapter(str, callback) {
254
- const l = " ";
255
- console.log(chalk.bgWhite(l) + " ");
256
- str = (" " + str + l).substring(0, l.length);
257
- console.log(chalk.bgWhite.cyan(str));
258
- console.log(chalk.bgWhite(l) + " ");
259
- if (callback) {
260
- callback();
261
- }
262
- }
263
- exports.displayChapter = displayChapter;
264
- function displayTitle(str, callback) {
265
- // istanbul ignore next
266
- if (!exports.g_config.silent) {
267
- console.log("");
268
- console.log(chalk.yellowBright(str));
269
- console.log(chalk.yellow(new Array(str.length + 1).join("=")), "\n");
270
- }
271
- if (callback) {
272
- callback();
273
- }
274
- }
275
- exports.displayTitle = displayTitle;
276
- function displaySubtitle(str, callback) {
277
- // istanbul ignore next
278
- if (!exports.g_config.silent) {
279
- console.log("");
280
- console.log(" " + chalk.yellowBright(str));
281
- console.log(" " + chalk.white(new Array(str.length + 1).join("-")), "\n");
282
- }
283
- if (callback) {
284
- callback();
285
- }
286
- }
287
- exports.displaySubtitle = displaySubtitle;
288
- function getEnvironmentVarNames() {
289
- return Object.keys(exportedEnvVars).map((varName) => {
290
- return { key: varName, pattern: "\\$ENV\\:\\:" + varName };
291
- });
292
- }
293
- exports.getEnvironmentVarNames = getEnvironmentVarNames;
294
- function generateStaticConfig(configPath, options) {
295
- const prePath = (options && options.cwd) || "";
296
- const staticConfigPath = configPath + ".tmp";
297
- let staticConfig = fs.readFileSync(path.join(prePath, configPath), { encoding: "utf8" });
298
- for (const envVar of getEnvironmentVarNames()) {
299
- staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), exportedEnvVars[envVar.key]);
300
- }
301
- fs.writeFileSync(path.join(prePath, staticConfigPath), staticConfig);
302
- return staticConfigPath;
303
- }
304
- exports.generateStaticConfig = generateStaticConfig;
305
- const q = quote;
306
- function make_path(folderName, filename) {
307
- let s;
308
- if (filename) {
309
- s = path.join(path.normalize(folderName), filename);
310
- }
311
- else {
312
- assert(folderName);
313
- s = folderName;
314
- }
315
- s = s.replace(/\\/g, "/");
316
- return s;
317
- }
318
- exports.make_path = make_path;
319
- const n = make_path;
320
- /**
321
- * calculate the public key from private key
322
- * openssl rsa -pubout -in private_key.pem
323
- *
324
- * @method getPublicKeyFromPrivateKey
325
- * @param privateKeyFilename
326
- * @param publicKeyFilename
327
- * @param callback
328
- */
329
- function getPublicKeyFromPrivateKey(privateKeyFilename, publicKeyFilename, callback) {
330
- assert(fs.existsSync(privateKeyFilename));
331
- execute_openssl("rsa -pubout -in " + q(n(privateKeyFilename)) + " -out " + q(n(publicKeyFilename)), {}, callback);
332
- }
333
- exports.getPublicKeyFromPrivateKey = getPublicKeyFromPrivateKey;
334
- /**
335
- * extract public key from a certificate
336
- * openssl x509 -pubkey -in certificate.pem -nottext
337
- *
338
- * @method getPublicKeyFromCertificate
339
- * @param certificateFilename
340
- * @param publicKeyFilename
341
- * @param callback
342
- */
343
- function getPublicKeyFromCertificate(certificateFilename, publicKeyFilename, callback) {
344
- assert(fs.existsSync(certificateFilename));
345
- execute_openssl("x509 -pubkey -in " + q(n(certificateFilename)) + " > " + q(n(publicKeyFilename)), {}, callback);
346
- }
347
- exports.getPublicKeyFromCertificate = getPublicKeyFromCertificate;
348
- /**
349
- * create a RSA PRIVATE KEY
350
- *
351
- * @method createPrivateKey
352
- *
353
- * @param privateKeyFilename
354
- * @param keyLength
355
- * @param callback {Function}
356
- */
357
- function createPrivateKey(privateKeyFilename, keyLength, callback) {
358
- // istanbul ignore next
359
- if (useRandFile()) {
360
- assert(hasEnv("RANDFILE"));
361
- }
362
- assert([1024, 2048, 3072, 4096].indexOf(keyLength) >= 0);
363
- const randomFile = exportedEnvVars.RANDFILE ? n(exportedEnvVars.RANDFILE) : "random.rnd";
364
- const tasks = [
365
- (callback) => createRandomFileIfNotExist(randomFile, {}, callback),
366
- // Note OpenSSL1 generates a -----BEGIN RSA PRIVATE KEY---- whereas
367
- // OpenSSL3 generates a -----BEGIN PRIVATE KEY----- unless the new -traditional option is used
368
- //
369
- // a BEGIN PRIVATE KEY structure is
370
- //
371
- // SEQUENCE (3 elem)
372
- // INTEGER 0
373
- // SEQUENCE (2 elem)
374
- // OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
375
- // NULL
376
- // OCTET STRING (609 byte) 3082025D02010002818100C5B53231183906122A5E3778736B05C095C75F1BB80D48B
377
- // SEQUENCE (9 elem)
378
- //
379
- // a BEGIN RSA PRIVATE KEY structure is just
380
- // SEQUENCE (9 elem)
381
- //
382
- (callback) => {
383
- execute_openssl("genrsa " +
384
- " -out " +
385
- q(n(privateKeyFilename)) +
386
- (useRandFile() ? " -rand " + q(randomFile) : "") +
387
- " " +
388
- keyLength, {}, (err) => {
389
- callback(err ? err : undefined);
390
- });
391
- },
392
- ];
393
- async.series(tasks, callback);
394
- }
395
- exports.createPrivateKey = createPrivateKey;
396
- function createRandomFile(randomFile, options, callback) {
397
- // istanbul ignore next
398
- if (!useRandFile()) {
399
- return callback();
400
- }
401
- execute_openssl("rand " + " -out " + q(randomFile) + " -hex 256", options, (err) => {
402
- callback(err ? err : undefined);
403
- });
404
- }
405
- exports.createRandomFile = createRandomFile;
406
- function createRandomFileIfNotExist(randomFile, options, callback) {
407
- const randomFilePath = options.cwd ? path.join(options.cwd, randomFile) : randomFile;
408
- if (fs.existsSync(randomFilePath)) {
409
- if (doDebug) {
410
- console.log(chalk.yellow(" randomFile"), chalk.cyan(randomFile), chalk.yellow(" already exists => skipping"));
411
- }
412
- return callback();
413
- }
414
- else {
415
- createRandomFile(randomFile, options, callback);
416
- }
417
- }
418
- exports.createRandomFileIfNotExist = createRandomFileIfNotExist;
419
- /**
420
- * create a certificate signing request
421
- *
422
- * @param certificateSigningRequestFilename
423
- * @param params
424
- * @param callback
425
- */
426
- function createCertificateSigningRequest(certificateSigningRequestFilename, params, callback) {
427
- assert(params);
428
- assert(params.rootDir);
429
- assert(params.configFile);
430
- assert(params.privateKey);
431
- assert(typeof params.privateKey === "string");
432
- assert(fs.existsSync(params.configFile), "config file must exist " + params.configFile);
433
- assert(fs.existsSync(params.privateKey), "Private key must exist" + params.privateKey);
434
- assert(fs.existsSync(params.rootDir), "RootDir key must exist");
435
- assert(typeof certificateSigningRequestFilename === "string");
436
- // note : this openssl command requires a config file
437
- processAltNames(params);
438
- const configFile = generateStaticConfig(params.configFile);
439
- const options = { cwd: params.rootDir, openssl_conf: configFile };
440
- const configOption = " -config " + q(n(configFile));
441
- const subject = params.subject ? new subject_1.Subject(params.subject).toString() : undefined;
442
- // process.env.OPENSSL_CONF ="";
443
- const subjectOptions = subject ? " -subj \"" + subject + "\"" : "";
444
- async.series([
445
- (callback) => {
446
- displaySubtitle("- Creating a Certificate Signing Request", callback);
447
- },
448
- (callback) => {
449
- execute_openssl("req -new" +
450
- " -sha256 " +
451
- " -batch " +
452
- " -text " +
453
- configOption +
454
- " -key " +
455
- q(n(params.privateKey)) +
456
- subjectOptions +
457
- " -out " +
458
- q(n(certificateSigningRequestFilename)), options, (err) => {
459
- callback(err ? err : undefined);
460
- });
461
- },
462
- ], (err) => callback(err));
463
- }
464
- exports.createCertificateSigningRequest = createCertificateSigningRequest;
465
- function x509Date(date) {
466
- date = date || new Date();
467
- const Y = date.getUTCFullYear();
468
- const M = date.getUTCMonth() + 1;
469
- const D = date.getUTCDate();
470
- const h = date.getUTCHours();
471
- const m = date.getUTCMinutes();
472
- const s = date.getUTCSeconds();
473
- function w(s, l) {
474
- return ("" + s).padStart(l, "0");
475
- }
476
- if (openssl_require2DigitYearInDate()) {
477
- // for example: on MacOS , where openssl 0.98 is installed by default
478
- return w(Y, 2) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
479
- }
480
- else {
481
- // for instance when openssl version is greater than 1.0.0
482
- return w(Y, 4) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
483
- }
484
- }
485
- exports.x509Date = x509Date;
486
- // purpose of self-signed certificate
487
- var CertificatePurpose;
488
- (function (CertificatePurpose) {
489
- CertificatePurpose[CertificatePurpose["NotSpecified"] = 0] = "NotSpecified";
490
- CertificatePurpose[CertificatePurpose["ForCertificateAuthority"] = 1] = "ForCertificateAuthority";
491
- CertificatePurpose[CertificatePurpose["ForApplication"] = 2] = "ForApplication";
492
- CertificatePurpose[CertificatePurpose["ForUserAuthentication"] = 3] = "ForUserAuthentication";
493
- })(CertificatePurpose = exports.CertificatePurpose || (exports.CertificatePurpose = {}));
494
- function adjustDate(params) {
495
- assert(params instanceof Object);
496
- params.startDate = params.startDate || new Date();
497
- assert(params.startDate instanceof Date);
498
- params.validity = params.validity || 365; // one year
499
- params.endDate = new Date(params.startDate.getTime());
500
- params.endDate.setDate(params.startDate.getDate() + params.validity);
501
- // xx params.endDate = x509Date(endDate);
502
- // xx params.startDate = x509Date(startDate);
503
- assert(params.endDate instanceof Date);
504
- assert(params.startDate instanceof Date);
505
- // istanbul ignore next
506
- if (!exports.g_config.silent) {
507
- console.log(" start Date ", params.startDate.toUTCString(), x509Date(params.startDate));
508
- console.log(" end Date ", params.endDate.toUTCString(), x509Date(params.endDate));
509
- }
510
- }
511
- exports.adjustDate = adjustDate;
512
- function adjustApplicationUri(params) {
513
- const applicationUri = params.applicationUri || "";
514
- if (applicationUri.length > 200) {
515
- throw new Error("Openssl doesn't support urn with length greater than 200" + applicationUri);
516
- }
517
- }
518
- exports.adjustApplicationUri = adjustApplicationUri;
519
- function certificateFileExist(certificateFile) {
520
- assert(typeof certificateFile === "string");
521
- // istanbul ignore next
522
- if (fs.existsSync(certificateFile) && !exports.g_config.force) {
523
- console.log(chalk.yellow(" certificate ") + chalk.cyan(certificateFile) + chalk.yellow(" already exists => do not overwrite"));
524
- return false;
525
- }
526
- return true;
527
- }
528
- exports.certificateFileExist = certificateFileExist;
529
- /**
530
- *
531
- * @param params
532
- * @param params.applicationUri
533
- * @param params.dns
534
- * @param params.ip
535
- * @private
536
- */
537
- function processAltNames(params) {
538
- params.dns = params.dns || [];
539
- params.ip = params.ip || [];
540
- // construct subjectAtlName
541
- let subjectAltName = [];
542
- subjectAltName.push("URI:" + params.applicationUri);
543
- subjectAltName = [].concat(subjectAltName, params.dns.map((d) => "DNS:" + d));
544
- subjectAltName = [].concat(subjectAltName, params.ip.map((d) => "IP:" + d));
545
- const subjectAltNameString = subjectAltName.join(", ");
546
- setEnv("ALTNAME", subjectAltNameString);
547
- }
548
- exports.processAltNames = processAltNames;
549
- /**
550
- *
551
- * @param certificate
552
- * @param params
553
- * @param params.configFile
554
- * @param params.rootDir
555
- * @param params.privateKey
556
- * @param params.applicationUri
557
- * @param params.dns
558
- * @param params.ip
559
- * @param params.validity certificate duration in days
560
- * @param params.purpose
561
- * @param [params.subject= "C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=ZZNodeOPCUA"]
562
- * @param callback
563
- */
564
- function createSelfSignCertificate(certificate, params, callback) {
565
- ensure_openssl_installed((err) => {
566
- if (err) {
567
- return callback(err);
568
- }
569
- try {
570
- params.purpose = params.purpose || CertificatePurpose.ForApplication;
571
- assert(params.purpose, "Please provide a Certificate Purpose");
572
- /**
573
- * note: due to a limitation of openssl ,
574
- * it is not possible to control the startDate of the certificate validity
575
- * to achieve this the certificateAuthority tool shall be used.
576
- */
577
- assert(fs.existsSync(params.configFile));
578
- assert(fs.existsSync(params.rootDir));
579
- assert(fs.existsSync(params.privateKey));
580
- if (!params.subject) {
581
- return callback(new Error("Missing subject"));
582
- }
583
- assert(typeof params.applicationUri === "string");
584
- assert(params.dns instanceof Array);
585
- // xx no key size in self-signed assert(params.keySize == 2048 || params.keySize == 4096);
586
- processAltNames(params);
587
- adjustDate(params);
588
- assert(Object.prototype.hasOwnProperty.call(params, "validity"));
589
- let subject = new subject_1.Subject(params.subject);
590
- subject = subject.toString();
591
- const certificateRequestFilename = certificate + ".csr";
592
- const configFile = generateStaticConfig(params.configFile);
593
- const configOption = " -config " + q(n(configFile));
594
- let extension;
595
- switch (params.purpose) {
596
- case CertificatePurpose.ForApplication:
597
- extension = "v3_selfsigned";
598
- break;
599
- case CertificatePurpose.ForCertificateAuthority:
600
- extension = "v3_ca";
601
- break;
602
- case CertificatePurpose.ForUserAuthentication:
603
- default:
604
- extension = "v3_selfsigned";
605
- }
606
- const tasks = [
607
- (callback) => {
608
- displayTitle("Generate a certificate request", callback);
609
- },
610
- // Once the private key is generated a Certificate Signing Request can be generated.
611
- // The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
612
- // Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
613
- // The second option is to self-sign the CSR, which will be demonstrated in the next section
614
- (callback) => {
615
- execute_openssl("req -new" +
616
- " -sha256 " +
617
- " -text " +
618
- " -extensions " +
619
- extension +
620
- " " +
621
- configOption +
622
- " -key " +
623
- q(n(params.privateKey)) +
624
- " -out " +
625
- q(n(certificateRequestFilename)) +
626
- " -subj \"" +
627
- subject +
628
- "\"", {}, callback);
629
- },
630
- // Xx // Step 3: Remove Passphrase from Key
631
- // Xx execute("cp private/cakey.pem private/cakey.pem.org");
632
- // Xx execute(openssl_path + " rsa -in private/cakey.pem.org
633
- // Xx -out private/cakey.pem -passin pass:"+paraphrase);
634
- (callback) => {
635
- displayTitle("Generate Certificate (self-signed)", callback);
636
- },
637
- (callback) => {
638
- execute_openssl(" x509 -req " +
639
- " -days " +
640
- params.validity +
641
- " -extensions " +
642
- extension +
643
- " " +
644
- " -extfile " +
645
- q(n(configFile)) +
646
- " -in " +
647
- q(n(certificateRequestFilename)) +
648
- " -signkey " +
649
- q(n(params.privateKey)) +
650
- " -text " +
651
- " -out " +
652
- q(certificate) +
653
- " -text ", {}, callback);
654
- },
655
- // remove unnecessary certificate request file
656
- (callback) => {
657
- fs.unlink(certificateRequestFilename, callback);
658
- },
659
- ];
660
- async.series(tasks, callback);
661
- }
662
- catch (err) {
663
- callback(err);
664
- }
665
- });
666
- }
667
- exports.createSelfSignCertificate = createSelfSignCertificate;
668
- // tslint:disable-next-line:variable-name
669
- exports.configurationFileTemplate = ca_config_template_cnf_1.default;
670
- /**
671
- *
672
- * a minimalist config file for openssl that allows
673
- * self-signed certificate to be generated.
674
- *
675
- */
676
- // tslint:disable-next-line:variable-name
677
- exports.configurationFileSimpleTemplate = simple_config_template_cnf_1.default;
678
- /**
679
- * @param certificate - the certificate file in PEM format, file must exist
680
- * @param callback
681
- */
682
- function dumpCertificate(certificate, callback) {
683
- assert(fs.existsSync(certificate));
684
- assert(typeof callback === "function");
685
- execute_openssl("x509 " + " -in " + q(n(certificate)) + " -text " + " -noout", {}, callback);
686
- }
687
- exports.dumpCertificate = dumpCertificate;
688
- function toDer(certificatePem, callback) {
689
- assert(fs.existsSync(certificatePem));
690
- const certificateDer = certificatePem.replace(".pem", ".der");
691
- execute_openssl("x509 " + " -outform der " + " -in " + certificatePem + " -out " + certificateDer, {}, callback);
692
- }
693
- exports.toDer = toDer;
694
- function fingerprint(certificatePem, callback) {
695
- // openssl x509 -in my_certificate.pem -hash -dates -noout -fingerprint
696
- assert(fs.existsSync(certificatePem));
697
- execute_openssl("x509 " + " -fingerprint " + " -noout " + " -in " + certificatePem, {}, callback);
698
- }
699
- exports.fingerprint = fingerprint;
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ // ---------------------------------------------------------------------------------------------------------------------
4
+ // node-opcua-pki
5
+ // ---------------------------------------------------------------------------------------------------------------------
6
+ // Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
7
+ // Copyright (c) 2022 - Sterfive.com
8
+ // ---------------------------------------------------------------------------------------------------------------------
9
+ //
10
+ // This project is licensed under the terms of the MIT license.
11
+ //
12
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13
+ // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
14
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
15
+ // permit persons to whom the Software is furnished to do so, subject to the following conditions:
16
+ //
17
+ // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
18
+ // Software.
19
+ //
20
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21
+ // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22
+ // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ // ---------------------------------------------------------------------------------------------------------------------
25
+ // tslint:disable:no-console
26
+ // tslint:disable:no-shadowed-variable
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.fingerprint = exports.toDer = exports.dumpCertificate = exports.configurationFileSimpleTemplate = exports.configurationFileTemplate = exports.createSelfSignCertificate = exports.processAltNames = exports.certificateFileExist = exports.adjustApplicationUri = exports.adjustDate = exports.CertificatePurpose = exports.x509Date = exports.createCertificateSigningRequest = exports.createRandomFileIfNotExist = exports.createRandomFile = exports.createPrivateKey = exports.getPublicKeyFromCertificate = exports.getPublicKeyFromPrivateKey = exports.make_path = exports.generateStaticConfig = exports.getEnvironmentVarNames = exports.displaySubtitle = exports.displayTitle = exports.displayChapter = exports.execute_openssl_no_failure = exports.executeOpensslAsync = exports.execute_openssl = exports.ensure_openssl_installed = exports.useRandFile = exports.execute = exports.hasEnv = exports.setEnv = exports.mkdir = exports.find_openssl = exports.debugLog = exports.g_config = exports.quote = void 0;
29
+ const assert = require("assert");
30
+ const async = require("async");
31
+ const byline = require("byline");
32
+ const chalk = require("chalk");
33
+ const child_process = require("child_process");
34
+ const fs = require("fs");
35
+ const os = require("os");
36
+ const path = require("path");
37
+ const install_prerequisite_1 = require("../misc/install_prerequisite");
38
+ const subject_1 = require("../misc/subject");
39
+ const ca_config_template_cnf_1 = require("./templates/ca_config_template.cnf");
40
+ const simple_config_template_cnf_1 = require("./templates/simple_config_template.cnf");
41
+ const exportedEnvVars = {};
42
+ function quote(str) {
43
+ return "\"" + (str || "") + "\"";
44
+ }
45
+ exports.quote = quote;
46
+ // tslint:disable-next-line:variable-name
47
+ exports.g_config = {
48
+ opensslVersion: "unset",
49
+ silent: process.env.VERBOSE ? !process.env.VERBOSE : true,
50
+ force: false,
51
+ };
52
+ const doDebug = process.env.NODEOPCUAPKIDEBUG || false;
53
+ const displayError = true;
54
+ const displayDebug = !!process.env.NODEOPCUAPKIDEBUG || false;
55
+ // tslint:disable-next-line:no-empty
56
+ function debugLog(...args) {
57
+ // istanbul ignore next
58
+ if (displayDebug) {
59
+ console.log.apply(null, args);
60
+ }
61
+ }
62
+ exports.debugLog = debugLog;
63
+ let opensslPath; // not initialized
64
+ function find_openssl(callback) {
65
+ (0, install_prerequisite_1.get_openssl_exec_path)((err, _opensslPath) => {
66
+ opensslPath = _opensslPath;
67
+ callback(err, opensslPath);
68
+ });
69
+ }
70
+ exports.find_openssl = find_openssl;
71
+ function mkdir(folder) {
72
+ if (!fs.existsSync(folder)) {
73
+ // istanbul ignore next
74
+ if (!exports.g_config.silent) {
75
+ console.log(chalk.white(" .. constructing "), folder);
76
+ }
77
+ fs.mkdirSync(folder);
78
+ }
79
+ }
80
+ exports.mkdir = mkdir;
81
+ function setEnv(varName, value) {
82
+ // istanbul ignore next
83
+ if (!exports.g_config.silent) {
84
+ console.log(" set " + varName + "=" + value);
85
+ }
86
+ exportedEnvVars[varName] = value;
87
+ if (["OPENSSL_CONF"].indexOf(varName) >= 0) {
88
+ process.env[varName] = value;
89
+ }
90
+ if (["RANDFILE"].indexOf(varName) >= 0) {
91
+ process.env[varName] = value;
92
+ }
93
+ }
94
+ exports.setEnv = setEnv;
95
+ function hasEnv(varName) {
96
+ return Object.prototype.hasOwnProperty.call(exportedEnvVars, varName);
97
+ }
98
+ exports.hasEnv = hasEnv;
99
+ function execute(cmd, options, callback) {
100
+ assert(typeof callback === "function");
101
+ /// assert(g_config.CARootDir && fs.existsSync(option.CARootDir));
102
+ options.cwd = options.cwd || process.cwd();
103
+ // istanbul ignore next
104
+ if (!exports.g_config.silent) {
105
+ console.log(chalk.cyan(" CWD "), options.cwd);
106
+ }
107
+ const outputs = [];
108
+ const child = child_process.exec(cmd, {
109
+ cwd: options.cwd,
110
+ windowsHide: true,
111
+ }, (err) => {
112
+ // istanbul ignore next
113
+ if (err) {
114
+ if (!options.hideErrorMessage) {
115
+ const fence = "###########################################";
116
+ console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
117
+ console.error(chalk.bgWhiteBright.redBright("CWD = " + options.cwd));
118
+ console.error(chalk.bgWhiteBright.redBright(err.message));
119
+ console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
120
+ }
121
+ // console.log(" ERR = ".bgWhite.red, err);
122
+ callback(new Error(err.message));
123
+ return;
124
+ }
125
+ callback(null, outputs.join(""));
126
+ });
127
+ if (child.stdout) {
128
+ const stream2 = byline(child.stdout);
129
+ stream2.on("data", (line) => {
130
+ outputs.push(line + "\n");
131
+ });
132
+ if (!exports.g_config.silent) {
133
+ stream2.on("data", (line) => {
134
+ line = line.toString();
135
+ if (doDebug) {
136
+ process.stdout.write(chalk.white(" stdout ") + chalk.whiteBright(line) + "\n");
137
+ }
138
+ });
139
+ }
140
+ }
141
+ // istanbul ignore next
142
+ if (!exports.g_config.silent) {
143
+ if (child.stderr) {
144
+ const stream1 = byline(child.stderr);
145
+ stream1.on("data", (line) => {
146
+ line = line.toString();
147
+ if (displayError) {
148
+ process.stdout.write(chalk.white(" stderr ") + chalk.red(line) + "\n");
149
+ }
150
+ });
151
+ }
152
+ }
153
+ }
154
+ exports.execute = execute;
155
+ function useRandFile() {
156
+ // istanbul ignore next
157
+ if (exports.g_config.opensslVersion && exports.g_config.opensslVersion.toLowerCase().indexOf("libressl") > -1) {
158
+ return false;
159
+ }
160
+ return true;
161
+ }
162
+ exports.useRandFile = useRandFile;
163
+ function openssl_require2DigitYearInDate() {
164
+ // istanbul ignore next
165
+ if (!exports.g_config.opensslVersion) {
166
+ throw new Error("openssl_require2DigitYearInDate : openssl version is not known:" + " please call ensure_openssl_installed(callback)");
167
+ }
168
+ return exports.g_config.opensslVersion.match(/OpenSSL 0\.9/);
169
+ }
170
+ exports.g_config.opensslVersion = "";
171
+ function ensure_openssl_installed(callback) {
172
+ assert(typeof callback === "function");
173
+ if (!opensslPath) {
174
+ return find_openssl((err) => {
175
+ // istanbul ignore next
176
+ if (err) {
177
+ return callback(err);
178
+ }
179
+ execute_openssl("version", { cwd: "." }, (err, outputs) => {
180
+ // istanbul ignore next
181
+ if (err || !outputs) {
182
+ return callback(err || new Error("no outputs"));
183
+ }
184
+ exports.g_config.opensslVersion = outputs.trim();
185
+ if (doDebug) {
186
+ console.log("OpenSSL version : ", exports.g_config.opensslVersion);
187
+ }
188
+ callback(err ? err : undefined);
189
+ });
190
+ });
191
+ }
192
+ else {
193
+ return callback();
194
+ }
195
+ }
196
+ exports.ensure_openssl_installed = ensure_openssl_installed;
197
+ function getTempFolder() {
198
+ return os.tmpdir();
199
+ }
200
+ function execute_openssl(cmd, options, callback) {
201
+ // tslint:disable-next-line:variable-name
202
+ const empty_config_file = n(getTempFolder(), "empty_config.cnf");
203
+ if (!fs.existsSync(empty_config_file)) {
204
+ fs.writeFileSync(empty_config_file, "# empty config file");
205
+ }
206
+ assert(typeof callback === "function");
207
+ options = options || {};
208
+ options.openssl_conf = options.openssl_conf || empty_config_file; // "!! OPEN SLL CONF NOT DEFINED BAD FILE !!";
209
+ assert(options.openssl_conf);
210
+ setEnv("OPENSSL_CONF", options.openssl_conf);
211
+ // istanbul ignore next
212
+ if (!exports.g_config.silent) {
213
+ console.log(chalk.cyan(" OPENSSL_CONF"), process.env.OPENSSL_CONF);
214
+ console.log(chalk.cyan(" RANDFILE "), process.env.RANDFILE);
215
+ console.log(chalk.cyan(" CMD openssl "), chalk.cyanBright(cmd));
216
+ }
217
+ ensure_openssl_installed((err) => {
218
+ // istanbul ignore next
219
+ if (err) {
220
+ return callback(err);
221
+ }
222
+ execute(quote(opensslPath) + " " + cmd, options, callback);
223
+ });
224
+ }
225
+ exports.execute_openssl = execute_openssl;
226
+ function executeOpensslAsync(cmd, options) {
227
+ return new Promise((resolve, reject) => {
228
+ execute_openssl(cmd, options, (err, output) => {
229
+ // istanbul ignore next
230
+ if (err) {
231
+ reject(err);
232
+ }
233
+ else {
234
+ resolve(output || "");
235
+ }
236
+ });
237
+ });
238
+ }
239
+ exports.executeOpensslAsync = executeOpensslAsync;
240
+ function execute_openssl_no_failure(cmd, options, callback) {
241
+ options = options || {};
242
+ options.hideErrorMessage = true;
243
+ execute_openssl(cmd, options, (err, output) => {
244
+ // istanbul ignore next
245
+ if (err) {
246
+ debugLog(" (ignored error = ERROR : )", err.message);
247
+ }
248
+ callback(null, output);
249
+ });
250
+ }
251
+ exports.execute_openssl_no_failure = execute_openssl_no_failure;
252
+ // istanbul ignore next
253
+ function displayChapter(str, callback) {
254
+ const l = " ";
255
+ console.log(chalk.bgWhite(l) + " ");
256
+ str = (" " + str + l).substring(0, l.length);
257
+ console.log(chalk.bgWhite.cyan(str));
258
+ console.log(chalk.bgWhite(l) + " ");
259
+ if (callback) {
260
+ callback();
261
+ }
262
+ }
263
+ exports.displayChapter = displayChapter;
264
+ function displayTitle(str, callback) {
265
+ // istanbul ignore next
266
+ if (!exports.g_config.silent) {
267
+ console.log("");
268
+ console.log(chalk.yellowBright(str));
269
+ console.log(chalk.yellow(new Array(str.length + 1).join("=")), "\n");
270
+ }
271
+ if (callback) {
272
+ callback();
273
+ }
274
+ }
275
+ exports.displayTitle = displayTitle;
276
+ function displaySubtitle(str, callback) {
277
+ // istanbul ignore next
278
+ if (!exports.g_config.silent) {
279
+ console.log("");
280
+ console.log(" " + chalk.yellowBright(str));
281
+ console.log(" " + chalk.white(new Array(str.length + 1).join("-")), "\n");
282
+ }
283
+ if (callback) {
284
+ callback();
285
+ }
286
+ }
287
+ exports.displaySubtitle = displaySubtitle;
288
+ function getEnvironmentVarNames() {
289
+ return Object.keys(exportedEnvVars).map((varName) => {
290
+ return { key: varName, pattern: "\\$ENV\\:\\:" + varName };
291
+ });
292
+ }
293
+ exports.getEnvironmentVarNames = getEnvironmentVarNames;
294
+ function generateStaticConfig(configPath, options) {
295
+ const prePath = (options && options.cwd) || "";
296
+ const staticConfigPath = configPath + ".tmp";
297
+ let staticConfig = fs.readFileSync(path.join(prePath, configPath), { encoding: "utf8" });
298
+ for (const envVar of getEnvironmentVarNames()) {
299
+ staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), exportedEnvVars[envVar.key]);
300
+ }
301
+ fs.writeFileSync(path.join(prePath, staticConfigPath), staticConfig);
302
+ return staticConfigPath;
303
+ }
304
+ exports.generateStaticConfig = generateStaticConfig;
305
+ const q = quote;
306
+ function make_path(folderName, filename) {
307
+ let s;
308
+ if (filename) {
309
+ s = path.join(path.normalize(folderName), filename);
310
+ }
311
+ else {
312
+ assert(folderName);
313
+ s = folderName;
314
+ }
315
+ s = s.replace(/\\/g, "/");
316
+ return s;
317
+ }
318
+ exports.make_path = make_path;
319
+ const n = make_path;
320
+ /**
321
+ * calculate the public key from private key
322
+ * openssl rsa -pubout -in private_key.pem
323
+ *
324
+ * @method getPublicKeyFromPrivateKey
325
+ * @param privateKeyFilename
326
+ * @param publicKeyFilename
327
+ * @param callback
328
+ */
329
+ function getPublicKeyFromPrivateKey(privateKeyFilename, publicKeyFilename, callback) {
330
+ assert(fs.existsSync(privateKeyFilename));
331
+ execute_openssl("rsa -pubout -in " + q(n(privateKeyFilename)) + " -out " + q(n(publicKeyFilename)), {}, callback);
332
+ }
333
+ exports.getPublicKeyFromPrivateKey = getPublicKeyFromPrivateKey;
334
+ /**
335
+ * extract public key from a certificate
336
+ * openssl x509 -pubkey -in certificate.pem -nottext
337
+ *
338
+ * @method getPublicKeyFromCertificate
339
+ * @param certificateFilename
340
+ * @param publicKeyFilename
341
+ * @param callback
342
+ */
343
+ function getPublicKeyFromCertificate(certificateFilename, publicKeyFilename, callback) {
344
+ assert(fs.existsSync(certificateFilename));
345
+ execute_openssl("x509 -pubkey -in " + q(n(certificateFilename)) + " > " + q(n(publicKeyFilename)), {}, callback);
346
+ }
347
+ exports.getPublicKeyFromCertificate = getPublicKeyFromCertificate;
348
+ /**
349
+ * create a RSA PRIVATE KEY
350
+ *
351
+ * @method createPrivateKey
352
+ *
353
+ * @param privateKeyFilename
354
+ * @param keyLength
355
+ * @param callback {Function}
356
+ */
357
+ function createPrivateKey(privateKeyFilename, keyLength, callback) {
358
+ // istanbul ignore next
359
+ if (useRandFile()) {
360
+ assert(hasEnv("RANDFILE"));
361
+ }
362
+ assert([1024, 2048, 3072, 4096].indexOf(keyLength) >= 0);
363
+ const randomFile = exportedEnvVars.RANDFILE ? n(exportedEnvVars.RANDFILE) : "random.rnd";
364
+ const tasks = [
365
+ (callback) => createRandomFileIfNotExist(randomFile, {}, callback),
366
+ // Note OpenSSL1 generates a -----BEGIN RSA PRIVATE KEY---- whereas
367
+ // OpenSSL3 generates a -----BEGIN PRIVATE KEY----- unless the new -traditional option is used
368
+ //
369
+ // a BEGIN PRIVATE KEY structure is
370
+ //
371
+ // SEQUENCE (3 elem)
372
+ // INTEGER 0
373
+ // SEQUENCE (2 elem)
374
+ // OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
375
+ // NULL
376
+ // OCTET STRING (609 byte) 3082025D02010002818100C5B53231183906122A5E3778736B05C095C75F1BB80D48B
377
+ // SEQUENCE (9 elem)
378
+ //
379
+ // a BEGIN RSA PRIVATE KEY structure is just
380
+ // SEQUENCE (9 elem)
381
+ //
382
+ (callback) => {
383
+ execute_openssl("genrsa " +
384
+ " -out " +
385
+ q(n(privateKeyFilename)) +
386
+ (useRandFile() ? " -rand " + q(randomFile) : "") +
387
+ " " +
388
+ keyLength, {}, (err) => {
389
+ callback(err ? err : undefined);
390
+ });
391
+ },
392
+ ];
393
+ async.series(tasks, callback);
394
+ }
395
+ exports.createPrivateKey = createPrivateKey;
396
+ function createRandomFile(randomFile, options, callback) {
397
+ // istanbul ignore next
398
+ if (!useRandFile()) {
399
+ return callback();
400
+ }
401
+ execute_openssl("rand " + " -out " + q(randomFile) + " -hex 256", options, (err) => {
402
+ callback(err ? err : undefined);
403
+ });
404
+ }
405
+ exports.createRandomFile = createRandomFile;
406
+ function createRandomFileIfNotExist(randomFile, options, callback) {
407
+ const randomFilePath = options.cwd ? path.join(options.cwd, randomFile) : randomFile;
408
+ if (fs.existsSync(randomFilePath)) {
409
+ if (doDebug) {
410
+ console.log(chalk.yellow(" randomFile"), chalk.cyan(randomFile), chalk.yellow(" already exists => skipping"));
411
+ }
412
+ return callback();
413
+ }
414
+ else {
415
+ createRandomFile(randomFile, options, callback);
416
+ }
417
+ }
418
+ exports.createRandomFileIfNotExist = createRandomFileIfNotExist;
419
+ /**
420
+ * create a certificate signing request
421
+ *
422
+ * @param certificateSigningRequestFilename
423
+ * @param params
424
+ * @param callback
425
+ */
426
+ function createCertificateSigningRequest(certificateSigningRequestFilename, params, callback) {
427
+ assert(params);
428
+ assert(params.rootDir);
429
+ assert(params.configFile);
430
+ assert(params.privateKey);
431
+ assert(typeof params.privateKey === "string");
432
+ assert(fs.existsSync(params.configFile), "config file must exist " + params.configFile);
433
+ assert(fs.existsSync(params.privateKey), "Private key must exist" + params.privateKey);
434
+ assert(fs.existsSync(params.rootDir), "RootDir key must exist");
435
+ assert(typeof certificateSigningRequestFilename === "string");
436
+ // note : this openssl command requires a config file
437
+ processAltNames(params);
438
+ const configFile = generateStaticConfig(params.configFile);
439
+ const options = { cwd: params.rootDir, openssl_conf: configFile };
440
+ const configOption = " -config " + q(n(configFile));
441
+ const subject = params.subject ? new subject_1.Subject(params.subject).toString() : undefined;
442
+ // process.env.OPENSSL_CONF ="";
443
+ const subjectOptions = subject ? " -subj \"" + subject + "\"" : "";
444
+ async.series([
445
+ (callback) => {
446
+ displaySubtitle("- Creating a Certificate Signing Request", callback);
447
+ },
448
+ (callback) => {
449
+ execute_openssl("req -new" +
450
+ " -sha256 " +
451
+ " -batch " +
452
+ " -text " +
453
+ configOption +
454
+ " -key " +
455
+ q(n(params.privateKey)) +
456
+ subjectOptions +
457
+ " -out " +
458
+ q(n(certificateSigningRequestFilename)), options, (err) => {
459
+ callback(err ? err : undefined);
460
+ });
461
+ },
462
+ ], (err) => callback(err));
463
+ }
464
+ exports.createCertificateSigningRequest = createCertificateSigningRequest;
465
+ function x509Date(date) {
466
+ date = date || new Date();
467
+ const Y = date.getUTCFullYear();
468
+ const M = date.getUTCMonth() + 1;
469
+ const D = date.getUTCDate();
470
+ const h = date.getUTCHours();
471
+ const m = date.getUTCMinutes();
472
+ const s = date.getUTCSeconds();
473
+ function w(s, l) {
474
+ return ("" + s).padStart(l, "0");
475
+ }
476
+ if (openssl_require2DigitYearInDate()) {
477
+ // for example: on MacOS , where openssl 0.98 is installed by default
478
+ return w(Y, 2) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
479
+ }
480
+ else {
481
+ // for instance when openssl version is greater than 1.0.0
482
+ return w(Y, 4) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2) + "Z";
483
+ }
484
+ }
485
+ exports.x509Date = x509Date;
486
+ // purpose of self-signed certificate
487
+ var CertificatePurpose;
488
+ (function (CertificatePurpose) {
489
+ CertificatePurpose[CertificatePurpose["NotSpecified"] = 0] = "NotSpecified";
490
+ CertificatePurpose[CertificatePurpose["ForCertificateAuthority"] = 1] = "ForCertificateAuthority";
491
+ CertificatePurpose[CertificatePurpose["ForApplication"] = 2] = "ForApplication";
492
+ CertificatePurpose[CertificatePurpose["ForUserAuthentication"] = 3] = "ForUserAuthentication";
493
+ })(CertificatePurpose = exports.CertificatePurpose || (exports.CertificatePurpose = {}));
494
+ function adjustDate(params) {
495
+ assert(params instanceof Object);
496
+ params.startDate = params.startDate || new Date();
497
+ assert(params.startDate instanceof Date);
498
+ params.validity = params.validity || 365; // one year
499
+ params.endDate = new Date(params.startDate.getTime());
500
+ params.endDate.setDate(params.startDate.getDate() + params.validity);
501
+ // xx params.endDate = x509Date(endDate);
502
+ // xx params.startDate = x509Date(startDate);
503
+ assert(params.endDate instanceof Date);
504
+ assert(params.startDate instanceof Date);
505
+ // istanbul ignore next
506
+ if (!exports.g_config.silent) {
507
+ console.log(" start Date ", params.startDate.toUTCString(), x509Date(params.startDate));
508
+ console.log(" end Date ", params.endDate.toUTCString(), x509Date(params.endDate));
509
+ }
510
+ }
511
+ exports.adjustDate = adjustDate;
512
+ function adjustApplicationUri(params) {
513
+ const applicationUri = params.applicationUri || "";
514
+ if (applicationUri.length > 200) {
515
+ throw new Error("Openssl doesn't support urn with length greater than 200" + applicationUri);
516
+ }
517
+ }
518
+ exports.adjustApplicationUri = adjustApplicationUri;
519
+ function certificateFileExist(certificateFile) {
520
+ assert(typeof certificateFile === "string");
521
+ // istanbul ignore next
522
+ if (fs.existsSync(certificateFile) && !exports.g_config.force) {
523
+ console.log(chalk.yellow(" certificate ") + chalk.cyan(certificateFile) + chalk.yellow(" already exists => do not overwrite"));
524
+ return false;
525
+ }
526
+ return true;
527
+ }
528
+ exports.certificateFileExist = certificateFileExist;
529
+ /**
530
+ *
531
+ * @param params
532
+ * @param params.applicationUri
533
+ * @param params.dns
534
+ * @param params.ip
535
+ * @private
536
+ */
537
+ function processAltNames(params) {
538
+ params.dns = params.dns || [];
539
+ params.ip = params.ip || [];
540
+ // construct subjectAtlName
541
+ let subjectAltName = [];
542
+ subjectAltName.push("URI:" + params.applicationUri);
543
+ subjectAltName = [].concat(subjectAltName, params.dns.map((d) => "DNS:" + d));
544
+ subjectAltName = [].concat(subjectAltName, params.ip.map((d) => "IP:" + d));
545
+ const subjectAltNameString = subjectAltName.join(", ");
546
+ setEnv("ALTNAME", subjectAltNameString);
547
+ }
548
+ exports.processAltNames = processAltNames;
549
+ /**
550
+ *
551
+ * @param certificate
552
+ * @param params
553
+ * @param params.configFile
554
+ * @param params.rootDir
555
+ * @param params.privateKey
556
+ * @param params.applicationUri
557
+ * @param params.dns
558
+ * @param params.ip
559
+ * @param params.validity certificate duration in days
560
+ * @param params.purpose
561
+ * @param [params.subject= "C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=ZZNodeOPCUA"]
562
+ * @param callback
563
+ */
564
+ function createSelfSignCertificate(certificate, params, callback) {
565
+ ensure_openssl_installed((err) => {
566
+ if (err) {
567
+ return callback(err);
568
+ }
569
+ try {
570
+ params.purpose = params.purpose || CertificatePurpose.ForApplication;
571
+ assert(params.purpose, "Please provide a Certificate Purpose");
572
+ /**
573
+ * note: due to a limitation of openssl ,
574
+ * it is not possible to control the startDate of the certificate validity
575
+ * to achieve this the certificateAuthority tool shall be used.
576
+ */
577
+ assert(fs.existsSync(params.configFile));
578
+ assert(fs.existsSync(params.rootDir));
579
+ assert(fs.existsSync(params.privateKey));
580
+ if (!params.subject) {
581
+ return callback(new Error("Missing subject"));
582
+ }
583
+ assert(typeof params.applicationUri === "string");
584
+ assert(params.dns instanceof Array);
585
+ // xx no key size in self-signed assert(params.keySize == 2048 || params.keySize == 4096);
586
+ processAltNames(params);
587
+ adjustDate(params);
588
+ assert(Object.prototype.hasOwnProperty.call(params, "validity"));
589
+ let subject = new subject_1.Subject(params.subject);
590
+ subject = subject.toString();
591
+ const certificateRequestFilename = certificate + ".csr";
592
+ const configFile = generateStaticConfig(params.configFile);
593
+ const configOption = " -config " + q(n(configFile));
594
+ let extension;
595
+ switch (params.purpose) {
596
+ case CertificatePurpose.ForApplication:
597
+ extension = "v3_selfsigned";
598
+ break;
599
+ case CertificatePurpose.ForCertificateAuthority:
600
+ extension = "v3_ca";
601
+ break;
602
+ case CertificatePurpose.ForUserAuthentication:
603
+ default:
604
+ extension = "v3_selfsigned";
605
+ }
606
+ const tasks = [
607
+ (callback) => {
608
+ displayTitle("Generate a certificate request", callback);
609
+ },
610
+ // Once the private key is generated a Certificate Signing Request can be generated.
611
+ // The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as
612
+ // Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.
613
+ // The second option is to self-sign the CSR, which will be demonstrated in the next section
614
+ (callback) => {
615
+ execute_openssl("req -new" +
616
+ " -sha256 " +
617
+ " -text " +
618
+ " -extensions " +
619
+ extension +
620
+ " " +
621
+ configOption +
622
+ " -key " +
623
+ q(n(params.privateKey)) +
624
+ " -out " +
625
+ q(n(certificateRequestFilename)) +
626
+ " -subj \"" +
627
+ subject +
628
+ "\"", {}, callback);
629
+ },
630
+ // Xx // Step 3: Remove Passphrase from Key
631
+ // Xx execute("cp private/cakey.pem private/cakey.pem.org");
632
+ // Xx execute(openssl_path + " rsa -in private/cakey.pem.org
633
+ // Xx -out private/cakey.pem -passin pass:"+paraphrase);
634
+ (callback) => {
635
+ displayTitle("Generate Certificate (self-signed)", callback);
636
+ },
637
+ (callback) => {
638
+ execute_openssl(" x509 -req " +
639
+ " -days " +
640
+ params.validity +
641
+ " -extensions " +
642
+ extension +
643
+ " " +
644
+ " -extfile " +
645
+ q(n(configFile)) +
646
+ " -in " +
647
+ q(n(certificateRequestFilename)) +
648
+ " -signkey " +
649
+ q(n(params.privateKey)) +
650
+ " -text " +
651
+ " -out " +
652
+ q(certificate) +
653
+ " -text ", {}, callback);
654
+ },
655
+ // remove unnecessary certificate request file
656
+ (callback) => {
657
+ fs.unlink(certificateRequestFilename, callback);
658
+ },
659
+ ];
660
+ async.series(tasks, callback);
661
+ }
662
+ catch (err) {
663
+ callback(err);
664
+ }
665
+ });
666
+ }
667
+ exports.createSelfSignCertificate = createSelfSignCertificate;
668
+ // tslint:disable-next-line:variable-name
669
+ exports.configurationFileTemplate = ca_config_template_cnf_1.default;
670
+ /**
671
+ *
672
+ * a minimalist config file for openssl that allows
673
+ * self-signed certificate to be generated.
674
+ *
675
+ */
676
+ // tslint:disable-next-line:variable-name
677
+ exports.configurationFileSimpleTemplate = simple_config_template_cnf_1.default;
678
+ /**
679
+ * @param certificate - the certificate file in PEM format, file must exist
680
+ * @param callback
681
+ */
682
+ function dumpCertificate(certificate, callback) {
683
+ assert(fs.existsSync(certificate));
684
+ assert(typeof callback === "function");
685
+ execute_openssl("x509 " + " -in " + q(n(certificate)) + " -text " + " -noout", {}, callback);
686
+ }
687
+ exports.dumpCertificate = dumpCertificate;
688
+ function toDer(certificatePem, callback) {
689
+ assert(fs.existsSync(certificatePem));
690
+ const certificateDer = certificatePem.replace(".pem", ".der");
691
+ execute_openssl("x509 " + " -outform der " + " -in " + certificatePem + " -out " + certificateDer, {}, callback);
692
+ }
693
+ exports.toDer = toDer;
694
+ function fingerprint(certificatePem, callback) {
695
+ // openssl x509 -in my_certificate.pem -hash -dates -noout -fingerprint
696
+ assert(fs.existsSync(certificatePem));
697
+ execute_openssl("x509 " + " -fingerprint " + " -noout " + " -in " + certificatePem, {}, callback);
698
+ }
699
+ exports.fingerprint = fingerprint;
700
700
  //# sourceMappingURL=toolbox.js.map