node-opcua-pki 6.10.2 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/pki.mjs +505 -119
- package/dist/bin/pki.mjs.map +1 -1
- package/dist/index.d.mts +217 -8
- package/dist/index.d.ts +217 -8
- package/dist/index.js +637 -261
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +642 -263
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/readme.md +27 -163
package/dist/index.js
CHANGED
|
@@ -51,13 +51,17 @@ __export(lib_exports, {
|
|
|
51
51
|
module.exports = __toCommonJS(lib_exports);
|
|
52
52
|
|
|
53
53
|
// packages/node-opcua-pki/lib/ca/certificate_authority.ts
|
|
54
|
-
var
|
|
55
|
-
var
|
|
54
|
+
var import_node_assert7 = __toESM(require("assert"));
|
|
55
|
+
var import_node_fs7 = __toESM(require("fs"));
|
|
56
56
|
var import_node_os3 = __toESM(require("os"));
|
|
57
57
|
var import_node_path5 = __toESM(require("path"));
|
|
58
58
|
var import_chalk5 = __toESM(require("chalk"));
|
|
59
59
|
var import_node_opcua_crypto2 = require("node-opcua-crypto");
|
|
60
60
|
|
|
61
|
+
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
62
|
+
var import_node_assert4 = __toESM(require("assert"));
|
|
63
|
+
var import_node_fs4 = __toESM(require("fs"));
|
|
64
|
+
|
|
61
65
|
// packages/node-opcua-pki/lib/toolbox/common.ts
|
|
62
66
|
var import_node_assert = __toESM(require("assert"));
|
|
63
67
|
function quote(str) {
|
|
@@ -134,30 +138,13 @@ function makePath(folderName, filename) {
|
|
|
134
138
|
return s;
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
// packages/node-opcua-pki/lib/toolbox/
|
|
138
|
-
var
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
function displaySubtitle(str) {
|
|
147
|
-
if (!g_config.silent) {
|
|
148
|
-
warningLog("");
|
|
149
|
-
warningLog(` ${import_chalk2.default.yellowBright(str)}`);
|
|
150
|
-
warningLog(` ${import_chalk2.default.white(new Array(str.length + 1).join("-"))}`, "\n");
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function display(str) {
|
|
154
|
-
if (!g_config.silent) {
|
|
155
|
-
warningLog(` ${str}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/index.ts
|
|
160
|
-
var import_node_constants = __toESM(require("constants"));
|
|
141
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/execute_openssl.ts
|
|
142
|
+
var import_node_assert3 = __toESM(require("assert"));
|
|
143
|
+
var import_node_child_process2 = __toESM(require("child_process"));
|
|
144
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
145
|
+
var import_node_os2 = __toESM(require("os"));
|
|
146
|
+
var import_byline2 = __toESM(require("byline"));
|
|
147
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
161
148
|
|
|
162
149
|
// packages/node-opcua-pki/lib/toolbox/with_openssl/_env.ts
|
|
163
150
|
var exportedEnvVars = {};
|
|
@@ -198,22 +185,6 @@ function processAltNames(params) {
|
|
|
198
185
|
setEnv("ALTNAME", subjectAltNameString);
|
|
199
186
|
}
|
|
200
187
|
|
|
201
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
202
|
-
var import_node_assert5 = __toESM(require("assert"));
|
|
203
|
-
var import_node_fs5 = __toESM(require("fs"));
|
|
204
|
-
var import_node_path4 = __toESM(require("path"));
|
|
205
|
-
|
|
206
|
-
// packages/node-opcua-pki/lib/misc/subject.ts
|
|
207
|
-
var import_node_opcua_crypto = require("node-opcua-crypto");
|
|
208
|
-
|
|
209
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/execute_openssl.ts
|
|
210
|
-
var import_node_assert3 = __toESM(require("assert"));
|
|
211
|
-
var import_node_child_process2 = __toESM(require("child_process"));
|
|
212
|
-
var import_node_fs3 = __toESM(require("fs"));
|
|
213
|
-
var import_node_os2 = __toESM(require("os"));
|
|
214
|
-
var import_byline2 = __toESM(require("byline"));
|
|
215
|
-
var import_chalk4 = __toESM(require("chalk"));
|
|
216
|
-
|
|
217
188
|
// packages/node-opcua-pki/lib/toolbox/with_openssl/install_prerequisite.ts
|
|
218
189
|
var import_node_child_process = __toESM(require("child_process"));
|
|
219
190
|
var import_node_fs2 = __toESM(require("fs"));
|
|
@@ -221,7 +192,7 @@ var import_node_os = __toESM(require("os"));
|
|
|
221
192
|
var import_node_path2 = __toESM(require("path"));
|
|
222
193
|
var import_node_url = __toESM(require("url"));
|
|
223
194
|
var import_byline = __toESM(require("byline"));
|
|
224
|
-
var
|
|
195
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
225
196
|
var import_progress = __toESM(require("progress"));
|
|
226
197
|
var import_wget_improved_2 = __toESM(require("wget-improved-2"));
|
|
227
198
|
var import_yauzl = __toESM(require("yauzl"));
|
|
@@ -239,7 +210,7 @@ function makeOptions() {
|
|
|
239
210
|
proxyAuth: auth
|
|
240
211
|
}
|
|
241
212
|
};
|
|
242
|
-
warningLog(
|
|
213
|
+
warningLog(import_chalk2.default.green("- using proxy "), proxy);
|
|
243
214
|
warningLog(options);
|
|
244
215
|
return options;
|
|
245
216
|
}
|
|
@@ -268,7 +239,7 @@ async function execute(cmd, cwd) {
|
|
|
268
239
|
output += `${line}
|
|
269
240
|
`;
|
|
270
241
|
if (doDebug2) {
|
|
271
|
-
process.stdout.write(` stdout ${
|
|
242
|
+
process.stdout.write(` stdout ${import_chalk2.default.yellow(line)}
|
|
272
243
|
`);
|
|
273
244
|
}
|
|
274
245
|
});
|
|
@@ -291,8 +262,8 @@ async function getopensslExecPath() {
|
|
|
291
262
|
const exitCode = result1?.exitCode;
|
|
292
263
|
const output = result1?.output;
|
|
293
264
|
if (exitCode !== 0) {
|
|
294
|
-
warningLog(
|
|
295
|
-
warningLog(
|
|
265
|
+
warningLog(import_chalk2.default.yellow(" it seems that ") + import_chalk2.default.cyan("openssl") + import_chalk2.default.yellow(" is not installed on your computer "));
|
|
266
|
+
warningLog(import_chalk2.default.yellow("Please install it before running this programs"));
|
|
296
267
|
throw new Error("Cannot find openssl");
|
|
297
268
|
}
|
|
298
269
|
const opensslExecPath = output.replace(/\n\r/g, "").trim();
|
|
@@ -302,7 +273,7 @@ async function check_system_openssl_version() {
|
|
|
302
273
|
const opensslExecPath = await getopensslExecPath();
|
|
303
274
|
const q_opensslExecPath = quote2(opensslExecPath);
|
|
304
275
|
if (doDebug2) {
|
|
305
|
-
warningLog(` OpenSSL found in : ${
|
|
276
|
+
warningLog(` OpenSSL found in : ${import_chalk2.default.yellow(opensslExecPath)}`);
|
|
306
277
|
}
|
|
307
278
|
const result = await execute(`${q_opensslExecPath} version`);
|
|
308
279
|
const exitCode = result?.exitCode;
|
|
@@ -310,9 +281,9 @@ async function check_system_openssl_version() {
|
|
|
310
281
|
const version = output.trim();
|
|
311
282
|
const versionOK = exitCode === 0 && is_expected_openssl_version(version);
|
|
312
283
|
if (!versionOK) {
|
|
313
|
-
let message =
|
|
284
|
+
let message = import_chalk2.default.whiteBright("Warning !!!!!!!!!!!! ") + "\nyour version of openssl is " + version + ". It doesn't match the expected version";
|
|
314
285
|
if (process.platform === "darwin") {
|
|
315
|
-
message +=
|
|
286
|
+
message += import_chalk2.default.cyan("\nplease refer to :") + import_chalk2.default.yellow(" https://github.com/node-opcua/node-opcua/wiki/installing-node-opcua-or-node-red-on-MacOS");
|
|
316
287
|
}
|
|
317
288
|
console.log(message);
|
|
318
289
|
}
|
|
@@ -338,7 +309,7 @@ async function install_and_check_win32_openssl_version() {
|
|
|
338
309
|
const exists = import_node_fs2.default.existsSync(opensslExecPath2);
|
|
339
310
|
if (!exists) {
|
|
340
311
|
warningLog("checking presence of ", opensslExecPath2);
|
|
341
|
-
warningLog(
|
|
312
|
+
warningLog(import_chalk2.default.red(" cannot find file ") + opensslExecPath2);
|
|
342
313
|
return {
|
|
343
314
|
opensslOk: false,
|
|
344
315
|
version: `cannot find file ${opensslExecPath2}`
|
|
@@ -372,12 +343,12 @@ async function install_and_check_win32_openssl_version() {
|
|
|
372
343
|
async function download_openssl() {
|
|
373
344
|
const url2 = win32or64() === 64 ? "https://github.com/node-opcua/node-opcua-pki/releases/download/2.14.2/openssl-1.0.2u-x64_86-win64.zip" : "https://github.com/node-opcua/node-opcua-pki/releases/download/2.14.2/openssl-1.0.2u-i386-win32.zip";
|
|
374
345
|
const outputFilename = import_node_path2.default.join(downloadFolder, import_node_path2.default.basename(url2));
|
|
375
|
-
warningLog(`downloading ${
|
|
346
|
+
warningLog(`downloading ${import_chalk2.default.yellow(url2)} to ${outputFilename}`);
|
|
376
347
|
if (import_node_fs2.default.existsSync(outputFilename)) {
|
|
377
348
|
return { downloadedFile: outputFilename };
|
|
378
349
|
}
|
|
379
350
|
const options = makeOptions();
|
|
380
|
-
const bar = new import_progress.default(
|
|
351
|
+
const bar = new import_progress.default(import_chalk2.default.cyan("[:bar]") + import_chalk2.default.cyan(" :percent ") + import_chalk2.default.white(":etas"), {
|
|
381
352
|
complete: "=",
|
|
382
353
|
incomplete: " ",
|
|
383
354
|
total: 100,
|
|
@@ -459,21 +430,21 @@ async function install_and_check_win32_openssl_version() {
|
|
|
459
430
|
}
|
|
460
431
|
const { opensslOk, version: _version } = await check_openssl_win32();
|
|
461
432
|
if (!opensslOk) {
|
|
462
|
-
warningLog(
|
|
433
|
+
warningLog(import_chalk2.default.yellow("openssl seems to be missing and need to be installed"));
|
|
463
434
|
const { downloadedFile } = await download_openssl();
|
|
464
435
|
if (doDebug2) {
|
|
465
|
-
warningLog("deflating ",
|
|
436
|
+
warningLog("deflating ", import_chalk2.default.yellow(downloadedFile));
|
|
466
437
|
}
|
|
467
438
|
await unzip_openssl(downloadedFile);
|
|
468
439
|
const opensslExists = !!import_node_fs2.default.existsSync(opensslExecPath);
|
|
469
440
|
if (doDebug2) {
|
|
470
|
-
warningLog("verifying ", opensslExists, opensslExists ?
|
|
441
|
+
warningLog("verifying ", opensslExists, opensslExists ? import_chalk2.default.green("OK ") : import_chalk2.default.red(" Error"), opensslExecPath);
|
|
471
442
|
}
|
|
472
443
|
const _opensslExecPath2 = await check_openssl_win32();
|
|
473
444
|
return opensslExecPath;
|
|
474
445
|
} else {
|
|
475
446
|
if (doDebug2) {
|
|
476
|
-
warningLog(
|
|
447
|
+
warningLog(import_chalk2.default.green("openssl is already installed and have the expected version."));
|
|
477
448
|
}
|
|
478
449
|
return opensslExecPath;
|
|
479
450
|
}
|
|
@@ -504,7 +475,7 @@ async function execute2(cmd, options) {
|
|
|
504
475
|
const from = new Error();
|
|
505
476
|
options.cwd = options.cwd || process.cwd();
|
|
506
477
|
if (!g_config.silent) {
|
|
507
|
-
warningLog(
|
|
478
|
+
warningLog(import_chalk3.default.cyan(" CWD "), options.cwd);
|
|
508
479
|
}
|
|
509
480
|
const outputs = [];
|
|
510
481
|
return await new Promise((resolve, reject) => {
|
|
@@ -518,10 +489,10 @@ async function execute2(cmd, options) {
|
|
|
518
489
|
if (err) {
|
|
519
490
|
if (!options.hideErrorMessage) {
|
|
520
491
|
const fence = "###########################################";
|
|
521
|
-
console.error(
|
|
522
|
-
console.error(
|
|
523
|
-
console.error(
|
|
524
|
-
console.error(
|
|
492
|
+
console.error(import_chalk3.default.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
493
|
+
console.error(import_chalk3.default.bgWhiteBright.redBright(`CWD = ${options.cwd}`));
|
|
494
|
+
console.error(import_chalk3.default.bgWhiteBright.redBright(err.message));
|
|
495
|
+
console.error(import_chalk3.default.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
525
496
|
console.error(from.stack);
|
|
526
497
|
}
|
|
527
498
|
reject(new Error(err.message));
|
|
@@ -540,7 +511,7 @@ async function execute2(cmd, options) {
|
|
|
540
511
|
stream2.on("data", (line) => {
|
|
541
512
|
line = line.toString();
|
|
542
513
|
if (doDebug) {
|
|
543
|
-
process.stdout.write(`${
|
|
514
|
+
process.stdout.write(`${import_chalk3.default.white(" stdout ") + import_chalk3.default.whiteBright(line)}
|
|
544
515
|
`);
|
|
545
516
|
}
|
|
546
517
|
});
|
|
@@ -552,7 +523,7 @@ async function execute2(cmd, options) {
|
|
|
552
523
|
stream1.on("data", (line) => {
|
|
553
524
|
line = line.toString();
|
|
554
525
|
if (displayError) {
|
|
555
|
-
process.stdout.write(`${
|
|
526
|
+
process.stdout.write(`${import_chalk3.default.white(" stderr ") + import_chalk3.default.red(line)}
|
|
556
527
|
`);
|
|
557
528
|
}
|
|
558
529
|
});
|
|
@@ -596,17 +567,107 @@ async function execute_openssl(cmd, options) {
|
|
|
596
567
|
(0, import_node_assert3.default)(options.openssl_conf);
|
|
597
568
|
setEnv("OPENSSL_CONF", options.openssl_conf);
|
|
598
569
|
if (!g_config.silent) {
|
|
599
|
-
warningLog(
|
|
600
|
-
warningLog(
|
|
601
|
-
warningLog(
|
|
570
|
+
warningLog(import_chalk3.default.cyan(" OPENSSL_CONF"), process.env.OPENSSL_CONF);
|
|
571
|
+
warningLog(import_chalk3.default.cyan(" RANDFILE "), process.env.RANDFILE);
|
|
572
|
+
warningLog(import_chalk3.default.cyan(" CMD openssl "), import_chalk3.default.cyanBright(cmd));
|
|
602
573
|
}
|
|
603
574
|
await ensure_openssl_installed();
|
|
604
575
|
return await execute2(`${quote(opensslPath)} ${cmd}`, options);
|
|
605
576
|
}
|
|
606
577
|
|
|
578
|
+
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
579
|
+
var q = quote;
|
|
580
|
+
var n2 = makePath;
|
|
581
|
+
async function createPFX(options) {
|
|
582
|
+
const { certificateFile, privateKeyFile, outputFile, passphrase = "", caCertificateFiles } = options;
|
|
583
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);
|
|
584
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);
|
|
585
|
+
let cmd = `pkcs12 -export`;
|
|
586
|
+
cmd += ` -in ${q(n2(certificateFile))}`;
|
|
587
|
+
cmd += ` -inkey ${q(n2(privateKeyFile))}`;
|
|
588
|
+
if (caCertificateFiles) {
|
|
589
|
+
for (const caFile of caCertificateFiles) {
|
|
590
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);
|
|
591
|
+
cmd += ` -certfile ${q(n2(caFile))}`;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
cmd += ` -out ${q(n2(outputFile))}`;
|
|
595
|
+
cmd += ` -passout pass:${passphrase}`;
|
|
596
|
+
await execute_openssl(cmd, {});
|
|
597
|
+
}
|
|
598
|
+
async function extractCertificateFromPFX(options) {
|
|
599
|
+
const { pfxFile, passphrase = "" } = options;
|
|
600
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
601
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -clcerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
602
|
+
return await execute_openssl(cmd, {});
|
|
603
|
+
}
|
|
604
|
+
async function extractPrivateKeyFromPFX(options) {
|
|
605
|
+
const { pfxFile, passphrase = "" } = options;
|
|
606
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
607
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -nocerts -nodes -passin pass:${passphrase}`;
|
|
608
|
+
return await execute_openssl(cmd, {});
|
|
609
|
+
}
|
|
610
|
+
async function extractCACertificatesFromPFX(options) {
|
|
611
|
+
const { pfxFile, passphrase = "" } = options;
|
|
612
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
613
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -cacerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
614
|
+
return await execute_openssl(cmd, {});
|
|
615
|
+
}
|
|
616
|
+
async function extractAllFromPFX(options) {
|
|
617
|
+
const [certificate, privateKey, caCertificates] = await Promise.all([
|
|
618
|
+
extractCertificateFromPFX(options),
|
|
619
|
+
extractPrivateKeyFromPFX(options),
|
|
620
|
+
extractCACertificatesFromPFX(options)
|
|
621
|
+
]);
|
|
622
|
+
return { certificate, privateKey, caCertificates };
|
|
623
|
+
}
|
|
624
|
+
async function convertPFXtoPEM(pfxFile, pemFile, passphrase = "") {
|
|
625
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
626
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -out ${q(n2(pemFile))} -nodes -passin pass:${passphrase}`;
|
|
627
|
+
await execute_openssl(cmd, {});
|
|
628
|
+
}
|
|
629
|
+
async function dumpPFX(pfxFile, passphrase = "") {
|
|
630
|
+
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
631
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -info -nodes -passin pass:${passphrase}`;
|
|
632
|
+
return await execute_openssl(cmd, {});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// packages/node-opcua-pki/lib/toolbox/display.ts
|
|
636
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
637
|
+
function displayTitle(str) {
|
|
638
|
+
if (!g_config.silent) {
|
|
639
|
+
warningLog("");
|
|
640
|
+
warningLog(import_chalk4.default.yellowBright(str));
|
|
641
|
+
warningLog(import_chalk4.default.yellow(new Array(str.length + 1).join("=")), "\n");
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function displaySubtitle(str) {
|
|
645
|
+
if (!g_config.silent) {
|
|
646
|
+
warningLog("");
|
|
647
|
+
warningLog(` ${import_chalk4.default.yellowBright(str)}`);
|
|
648
|
+
warningLog(` ${import_chalk4.default.white(new Array(str.length + 1).join("-"))}`, "\n");
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function display(str) {
|
|
652
|
+
if (!g_config.silent) {
|
|
653
|
+
warningLog(` ${str}`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/index.ts
|
|
658
|
+
var import_node_constants = __toESM(require("constants"));
|
|
659
|
+
|
|
660
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
661
|
+
var import_node_assert6 = __toESM(require("assert"));
|
|
662
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
663
|
+
var import_node_path4 = __toESM(require("path"));
|
|
664
|
+
|
|
665
|
+
// packages/node-opcua-pki/lib/misc/subject.ts
|
|
666
|
+
var import_node_opcua_crypto = require("node-opcua-crypto");
|
|
667
|
+
|
|
607
668
|
// packages/node-opcua-pki/lib/toolbox/with_openssl/toolbox.ts
|
|
608
|
-
var
|
|
609
|
-
var
|
|
669
|
+
var import_node_assert5 = __toESM(require("assert"));
|
|
670
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
610
671
|
var import_node_path3 = __toESM(require("path"));
|
|
611
672
|
function openssl_require2DigitYearInDate() {
|
|
612
673
|
if (!g_config.opensslVersion) {
|
|
@@ -621,13 +682,13 @@ var _counter = 0;
|
|
|
621
682
|
function generateStaticConfig(configPath, options) {
|
|
622
683
|
const prePath = options?.cwd || "";
|
|
623
684
|
const originalFilename = !import_node_path3.default.isAbsolute(configPath) ? import_node_path3.default.join(prePath, configPath) : configPath;
|
|
624
|
-
let staticConfig =
|
|
685
|
+
let staticConfig = import_node_fs5.default.readFileSync(originalFilename, { encoding: "utf8" });
|
|
625
686
|
for (const envVar of getEnvironmentVarNames()) {
|
|
626
687
|
staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), getEnv(envVar.key));
|
|
627
688
|
}
|
|
628
689
|
const staticConfigPath = `${configPath}.${process.pid}-${_counter++}.tmp`;
|
|
629
690
|
const temporaryConfigPath = !import_node_path3.default.isAbsolute(configPath) ? import_node_path3.default.join(prePath, staticConfigPath) : staticConfigPath;
|
|
630
|
-
|
|
691
|
+
import_node_fs5.default.writeFileSync(temporaryConfigPath, staticConfig);
|
|
631
692
|
if (options?.cwd) {
|
|
632
693
|
return import_node_path3.default.relative(options.cwd, temporaryConfigPath);
|
|
633
694
|
} else {
|
|
@@ -652,8 +713,38 @@ function x509Date(date) {
|
|
|
652
713
|
}
|
|
653
714
|
}
|
|
654
715
|
|
|
716
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
717
|
+
var q2 = quote;
|
|
718
|
+
var n3 = makePath;
|
|
719
|
+
async function createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFilename, params) {
|
|
720
|
+
(0, import_node_assert6.default)(params);
|
|
721
|
+
(0, import_node_assert6.default)(params.rootDir);
|
|
722
|
+
(0, import_node_assert6.default)(params.configFile);
|
|
723
|
+
(0, import_node_assert6.default)(params.privateKey);
|
|
724
|
+
(0, import_node_assert6.default)(typeof params.privateKey === "string");
|
|
725
|
+
(0, import_node_assert6.default)(import_node_fs6.default.existsSync(params.configFile), `config file must exist ${params.configFile}`);
|
|
726
|
+
(0, import_node_assert6.default)(import_node_fs6.default.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
|
|
727
|
+
(0, import_node_assert6.default)(import_node_fs6.default.existsSync(params.rootDir), "RootDir key must exist");
|
|
728
|
+
(0, import_node_assert6.default)(typeof certificateSigningRequestFilename === "string");
|
|
729
|
+
processAltNames(params);
|
|
730
|
+
const configFile = generateStaticConfig(params.configFile, { cwd: params.rootDir });
|
|
731
|
+
const options = { cwd: params.rootDir, openssl_conf: import_node_path4.default.relative(params.rootDir, configFile) };
|
|
732
|
+
const configOption = ` -config ${q2(n3(configFile))}`;
|
|
733
|
+
const subject = params.subject ? new import_node_opcua_crypto.Subject(params.subject).toString() : void 0;
|
|
734
|
+
const subjectOptions = subject ? ` -subj "${subject}"` : "";
|
|
735
|
+
displaySubtitle("- Creating a Certificate Signing Request with openssl");
|
|
736
|
+
await execute_openssl(
|
|
737
|
+
"req -new -sha256 -batch -text " + configOption + " -key " + q2(n3(params.privateKey)) + subjectOptions + " -out " + q2(n3(certificateSigningRequestFilename)),
|
|
738
|
+
options
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// packages/node-opcua-pki/lib/pki/templates/simple_config_template.cnf.ts
|
|
743
|
+
var config = '##################################################################################################\n## SIMPLE OPENSSL CONFIG FILE FOR SELF-SIGNED CERTIFICATE GENERATION\n################################################################################################################\n\ndistinguished_name = req_distinguished_name\ndefault_md = sha1\n\ndefault_md = sha256 # The default digest algorithm\n\n[ v3_ca ]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer:always\n\n# authorityKeyIdentifier = keyid\nbasicConstraints = CA:TRUE\nkeyUsage = critical, cRLSign, keyCertSign\nnsComment = "Self-signed Certificate for CA generated by Node-OPCUA Certificate utility"\n#nsCertType = sslCA, emailCA\n#subjectAltName = email:copy\n#issuerAltName = issuer:copy\n#obj = DER:02:03\n# crlDistributionPoints = @crl_info\n# [ crl_info ]\n# URI.0 = http://localhost:8900/crl.pem\nsubjectAltName = $ENV::ALTNAME\n\n[ req ]\ndays = 390\nreq_extensions = v3_req\nx509_extensions = v3_ca\n\n[v3_req]\nbasicConstraints = CA:false\nkeyUsage = critical, cRLSign, keyCertSign\nsubjectAltName = $ENV::ALTNAME\n\n[ v3_ca_signed]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid,issuer\nbasicConstraints = critical, CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign\nextendedKeyUsage = clientAuth,serverAuth \nnsComment = "certificate generated by Node-OPCUA Certificate utility and signed by a CA"\nsubjectAltName = $ENV::ALTNAME\n[ v3_selfsigned]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid,issuer\nbasicConstraints = critical, CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign\nextendedKeyUsage = clientAuth,serverAuth \nnsComment = "Self-signed certificate generated by Node-OPCUA Certificate utility"\nsubjectAltName = $ENV::ALTNAME\n[ req_distinguished_name ]\ncountryName = Country Name (2 letter code)\ncountryName_default = FR\ncountryName_min = 2\ncountryName_max = 2\n# stateOrProvinceName = State or Province Name (full name)\n# stateOrProvinceName_default = Ile de France\n# localityName = Locality Name (city, district)\n# localityName_default = Paris\norganizationName = Organization Name (company)\norganizationName_default = NodeOPCUA\n# organizationalUnitName = Organizational Unit Name (department, division)\n# organizationalUnitName_default = R&D\ncommonName = Common Name (hostname, FQDN, IP, or your name)\ncommonName_max = 256\ncommonName_default = NodeOPCUA\n# emailAddress = Email Address\n# emailAddress_max = 40\n# emailAddress_default = node-opcua (at) node-opcua (dot) com\nsubjectAltName = $ENV::ALTNAME';
|
|
744
|
+
var simple_config_template_cnf_default = config;
|
|
745
|
+
|
|
655
746
|
// packages/node-opcua-pki/lib/ca/templates/ca_config_template.cnf.ts
|
|
656
|
-
var
|
|
747
|
+
var config2 = `#.........DO NOT MODIFY BY HAND .........................
|
|
657
748
|
[ ca ]
|
|
658
749
|
default_ca = CA_default
|
|
659
750
|
[ CA_default ]
|
|
@@ -782,22 +873,23 @@ subjectAltName = $ENV::ALTNAME
|
|
|
782
873
|
#issuerAltName = issuer:copy
|
|
783
874
|
authorityKeyIdentifier = keyid:always,issuer:always
|
|
784
875
|
#authorityInfoAccess = @issuer_info`;
|
|
785
|
-
var ca_config_template_cnf_default =
|
|
876
|
+
var ca_config_template_cnf_default = config2;
|
|
786
877
|
|
|
787
878
|
// packages/node-opcua-pki/lib/ca/certificate_authority.ts
|
|
788
879
|
var defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
|
|
789
880
|
var configurationFileTemplate = ca_config_template_cnf_default;
|
|
790
|
-
var
|
|
881
|
+
var configurationFileSimpleTemplate = simple_config_template_cnf_default;
|
|
882
|
+
var config3 = {
|
|
791
883
|
certificateDir: "INVALID",
|
|
792
884
|
forceCA: false,
|
|
793
885
|
pkiDir: "INVALID"
|
|
794
886
|
};
|
|
795
|
-
var
|
|
796
|
-
var
|
|
887
|
+
var n4 = makePath;
|
|
888
|
+
var q3 = quote;
|
|
797
889
|
function octetStringToIpAddress(a) {
|
|
798
890
|
return parseInt(a.substring(0, 2), 16).toString() + "." + parseInt(a.substring(2, 4), 16).toString() + "." + parseInt(a.substring(4, 6), 16).toString() + "." + parseInt(a.substring(6, 8), 16).toString();
|
|
799
891
|
}
|
|
800
|
-
(0,
|
|
892
|
+
(0, import_node_assert7.default)(octetStringToIpAddress("c07b9179") === "192.123.145.121");
|
|
801
893
|
async function construct_CertificateAuthority(certificateAuthority) {
|
|
802
894
|
const subject = certificateAuthority.subject;
|
|
803
895
|
const caRootDir = import_node_path5.default.resolve(certificateAuthority.rootDir);
|
|
@@ -812,49 +904,49 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
812
904
|
await make_folders();
|
|
813
905
|
async function construct_default_files() {
|
|
814
906
|
const serial = import_node_path5.default.join(caRootDir, "serial");
|
|
815
|
-
if (!
|
|
816
|
-
await
|
|
907
|
+
if (!import_node_fs7.default.existsSync(serial)) {
|
|
908
|
+
await import_node_fs7.default.promises.writeFile(serial, "1000");
|
|
817
909
|
}
|
|
818
910
|
const crlNumber = import_node_path5.default.join(caRootDir, "crlnumber");
|
|
819
|
-
if (!
|
|
820
|
-
await
|
|
911
|
+
if (!import_node_fs7.default.existsSync(crlNumber)) {
|
|
912
|
+
await import_node_fs7.default.promises.writeFile(crlNumber, "1000");
|
|
821
913
|
}
|
|
822
914
|
const indexFile = import_node_path5.default.join(caRootDir, "index.txt");
|
|
823
|
-
if (!
|
|
824
|
-
await
|
|
915
|
+
if (!import_node_fs7.default.existsSync(indexFile)) {
|
|
916
|
+
await import_node_fs7.default.promises.writeFile(indexFile, "");
|
|
825
917
|
}
|
|
826
918
|
}
|
|
827
919
|
await construct_default_files();
|
|
828
|
-
const caKeyExists =
|
|
829
|
-
const caCertExists =
|
|
830
|
-
if (caKeyExists && caCertExists && !
|
|
920
|
+
const caKeyExists = import_node_fs7.default.existsSync(import_node_path5.default.join(caRootDir, "private/cakey.pem"));
|
|
921
|
+
const caCertExists = import_node_fs7.default.existsSync(import_node_path5.default.join(caRootDir, "public/cacert.pem"));
|
|
922
|
+
if (caKeyExists && caCertExists && !config3.forceCA) {
|
|
831
923
|
debugLog("CA private key and certificate already exist ... skipping");
|
|
832
924
|
return;
|
|
833
925
|
}
|
|
834
926
|
if (caKeyExists && !caCertExists) {
|
|
835
927
|
debugLog("CA private key exists but cacert.pem is missing \u2014 rebuilding CA");
|
|
836
|
-
|
|
928
|
+
import_node_fs7.default.unlinkSync(import_node_path5.default.join(caRootDir, "private/cakey.pem"));
|
|
837
929
|
const staleCsr = import_node_path5.default.join(caRootDir, "private/cakey.csr");
|
|
838
|
-
if (
|
|
839
|
-
|
|
930
|
+
if (import_node_fs7.default.existsSync(staleCsr)) {
|
|
931
|
+
import_node_fs7.default.unlinkSync(staleCsr);
|
|
840
932
|
}
|
|
841
933
|
}
|
|
842
934
|
displayTitle("Create Certificate Authority (CA)");
|
|
843
935
|
const indexFileAttr = import_node_path5.default.join(caRootDir, "index.txt.attr");
|
|
844
|
-
if (!
|
|
845
|
-
await
|
|
936
|
+
if (!import_node_fs7.default.existsSync(indexFileAttr)) {
|
|
937
|
+
await import_node_fs7.default.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
846
938
|
}
|
|
847
939
|
const caConfigFile = certificateAuthority.configFile;
|
|
848
940
|
if (1) {
|
|
849
941
|
let data = configurationFileTemplate;
|
|
850
942
|
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
851
|
-
await
|
|
943
|
+
await import_node_fs7.default.promises.writeFile(caConfigFile, data);
|
|
852
944
|
}
|
|
853
945
|
const subjectOpt = ` -subj "${subject.toString()}" `;
|
|
854
946
|
processAltNames({});
|
|
855
947
|
const options = { cwd: caRootDir };
|
|
856
948
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
857
|
-
const configOption = ` -config ${
|
|
949
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
858
950
|
const keySize = certificateAuthority.keySize;
|
|
859
951
|
const privateKeyFilename = import_node_path5.default.join(caRootDir, "private/cakey.pem");
|
|
860
952
|
const csrFilename = import_node_path5.default.join(caRootDir, "private/cakey.csr");
|
|
@@ -862,14 +954,26 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
862
954
|
await (0, import_node_opcua_crypto2.generatePrivateKeyFile)(privateKeyFilename, keySize);
|
|
863
955
|
displayTitle("Generate a certificate request for the CA key");
|
|
864
956
|
await execute_openssl(
|
|
865
|
-
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " +
|
|
866
|
-
options
|
|
867
|
-
);
|
|
868
|
-
displayTitle("Generate CA Certificate (self-signed)");
|
|
869
|
-
await execute_openssl(
|
|
870
|
-
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " + q(n2(configFile)) + " -in private/cakey.csr -signkey " + q(n2(privateKeyFilename)) + " -out public/cacert.pem",
|
|
957
|
+
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " + q3(n4(privateKeyFilename)) + " -out " + q3(n4(csrFilename)) + " " + subjectOpt,
|
|
871
958
|
options
|
|
872
959
|
);
|
|
960
|
+
const issuerCA = certificateAuthority._issuerCA;
|
|
961
|
+
if (issuerCA) {
|
|
962
|
+
displayTitle("Generate CA Certificate (signed by issuer CA)");
|
|
963
|
+
const issuerCert = import_node_path5.default.resolve(issuerCA.caCertificate);
|
|
964
|
+
const issuerKey = import_node_path5.default.resolve(issuerCA.rootDir, "private/cakey.pem");
|
|
965
|
+
const issuerSerial = import_node_path5.default.resolve(issuerCA.rootDir, "serial");
|
|
966
|
+
await execute_openssl(
|
|
967
|
+
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " + q3(n4(configFile)) + " -in private/cakey.csr -CA " + q3(n4(issuerCert)) + " -CAkey " + q3(n4(issuerKey)) + " -CAserial " + q3(n4(issuerSerial)) + " -out public/cacert.pem",
|
|
968
|
+
options
|
|
969
|
+
);
|
|
970
|
+
} else {
|
|
971
|
+
displayTitle("Generate CA Certificate (self-signed)");
|
|
972
|
+
await execute_openssl(
|
|
973
|
+
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " + q3(n4(configFile)) + " -in private/cakey.csr -signkey " + q3(n4(privateKeyFilename)) + " -out public/cacert.pem",
|
|
974
|
+
options
|
|
975
|
+
);
|
|
976
|
+
}
|
|
873
977
|
displaySubtitle("generate initial CRL (Certificate Revocation List)");
|
|
874
978
|
await regenerateCrl(certificateAuthority.revocationList, configOption, options);
|
|
875
979
|
displayTitle("Create Certificate Authority (CA) ---> DONE");
|
|
@@ -879,7 +983,7 @@ async function regenerateCrl(revocationList, configOption, options) {
|
|
|
879
983
|
await execute_openssl(`ca -gencrl ${configOption} -out crl/revocation_list.crl`, options);
|
|
880
984
|
await execute_openssl("crl -in crl/revocation_list.crl -out crl/revocation_list.der -outform der", options);
|
|
881
985
|
displaySubtitle("Display (Certificate Revocation List)");
|
|
882
|
-
await execute_openssl(`crl -in ${
|
|
986
|
+
await execute_openssl(`crl -in ${q3(n4(revocationList))} -text -noout`, options);
|
|
883
987
|
}
|
|
884
988
|
function parseOpenSSLDate(dateStr) {
|
|
885
989
|
const raw = dateStr?.split(",")[0] ?? "";
|
|
@@ -900,12 +1004,15 @@ var CertificateAuthority = class {
|
|
|
900
1004
|
location;
|
|
901
1005
|
/** X.500 subject of the CA certificate. */
|
|
902
1006
|
subject;
|
|
1007
|
+
/** @internal Parent CA (undefined for root CAs). */
|
|
1008
|
+
_issuerCA;
|
|
903
1009
|
constructor(options) {
|
|
904
|
-
(0,
|
|
905
|
-
(0,
|
|
1010
|
+
(0, import_node_assert7.default)(Object.prototype.hasOwnProperty.call(options, "location"));
|
|
1011
|
+
(0, import_node_assert7.default)(Object.prototype.hasOwnProperty.call(options, "keySize"));
|
|
906
1012
|
this.location = options.location;
|
|
907
1013
|
this.keySize = options.keySize || 2048;
|
|
908
1014
|
this.subject = new import_node_opcua_crypto2.Subject(options.subject || defaultSubject);
|
|
1015
|
+
this._issuerCA = options.issuerCA;
|
|
909
1016
|
}
|
|
910
1017
|
/** Absolute path to the CA root directory (alias for {@link location}). */
|
|
911
1018
|
get rootDir() {
|
|
@@ -919,6 +1026,18 @@ var CertificateAuthority = class {
|
|
|
919
1026
|
get caCertificate() {
|
|
920
1027
|
return makePath(this.rootDir, "./public/cacert.pem");
|
|
921
1028
|
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Path to the issuer certificate chain (`public/issuer_chain.pem`).
|
|
1031
|
+
*
|
|
1032
|
+
* This file is created by {@link installCACertificate} when the
|
|
1033
|
+
* provided cert file contains additional issuer certificates
|
|
1034
|
+
* (e.g. intermediate + root). It is appended to signed certs
|
|
1035
|
+
* by {@link constructCertificateChain} to produce a full chain
|
|
1036
|
+
* per OPC UA Part 6 §6.2.6.
|
|
1037
|
+
*/
|
|
1038
|
+
get issuerCertificateChain() {
|
|
1039
|
+
return makePath(this.rootDir, "./public/issuer_chain.pem");
|
|
1040
|
+
}
|
|
922
1041
|
/**
|
|
923
1042
|
* Path to the current Certificate Revocation List in DER format.
|
|
924
1043
|
* (`crl/revocation_list.der`)
|
|
@@ -976,10 +1095,10 @@ var CertificateAuthority = class {
|
|
|
976
1095
|
*/
|
|
977
1096
|
getCRLDER() {
|
|
978
1097
|
const crlPath = this.revocationListDER;
|
|
979
|
-
if (!
|
|
1098
|
+
if (!import_node_fs7.default.existsSync(crlPath)) {
|
|
980
1099
|
return Buffer.alloc(0);
|
|
981
1100
|
}
|
|
982
|
-
return
|
|
1101
|
+
return import_node_fs7.default.readFileSync(crlPath);
|
|
983
1102
|
}
|
|
984
1103
|
/**
|
|
985
1104
|
* Return the current Certificate Revocation List as a
|
|
@@ -989,10 +1108,10 @@ var CertificateAuthority = class {
|
|
|
989
1108
|
*/
|
|
990
1109
|
getCRLPEM() {
|
|
991
1110
|
const crlPath = this.revocationList;
|
|
992
|
-
if (!
|
|
1111
|
+
if (!import_node_fs7.default.existsSync(crlPath)) {
|
|
993
1112
|
return "";
|
|
994
1113
|
}
|
|
995
|
-
const raw =
|
|
1114
|
+
const raw = import_node_fs7.default.readFileSync(crlPath, "utf-8");
|
|
996
1115
|
const beginMarker = "-----BEGIN X509 CRL-----";
|
|
997
1116
|
const idx = raw.indexOf(beginMarker);
|
|
998
1117
|
if (idx > 0) {
|
|
@@ -1045,7 +1164,7 @@ var CertificateAuthority = class {
|
|
|
1045
1164
|
getCertificateBySerial(serial) {
|
|
1046
1165
|
const upper = serial.toUpperCase();
|
|
1047
1166
|
const certFile = import_node_path5.default.join(this.rootDir, "certs", `${upper}.pem`);
|
|
1048
|
-
if (!
|
|
1167
|
+
if (!import_node_fs7.default.existsSync(certFile)) {
|
|
1049
1168
|
return void 0;
|
|
1050
1169
|
}
|
|
1051
1170
|
const pem = (0, import_node_opcua_crypto2.readCertificatePEM)(certFile);
|
|
@@ -1074,10 +1193,10 @@ var CertificateAuthority = class {
|
|
|
1074
1193
|
*/
|
|
1075
1194
|
_parseIndexTxt() {
|
|
1076
1195
|
const indexPath = this.indexFile;
|
|
1077
|
-
if (!
|
|
1196
|
+
if (!import_node_fs7.default.existsSync(indexPath)) {
|
|
1078
1197
|
return [];
|
|
1079
1198
|
}
|
|
1080
|
-
const content =
|
|
1199
|
+
const content = import_node_fs7.default.readFileSync(indexPath, "utf-8");
|
|
1081
1200
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
1082
1201
|
const records = [];
|
|
1083
1202
|
for (const line of lines) {
|
|
@@ -1131,25 +1250,145 @@ var CertificateAuthority = class {
|
|
|
1131
1250
|
* internally so that callers can work with in-memory
|
|
1132
1251
|
* buffers only.
|
|
1133
1252
|
*
|
|
1253
|
+
* The CA can override fields from the CSR by passing
|
|
1254
|
+
* `options.dns`, `options.ip`, `options.applicationUri`,
|
|
1255
|
+
* `options.startDate`, or `options.subject`.
|
|
1256
|
+
*
|
|
1134
1257
|
* @param csrDer - the CSR as a DER-encoded buffer
|
|
1135
|
-
* @param options - signing options
|
|
1136
|
-
* @param options.validity - certificate validity in days
|
|
1137
|
-
* (default: 365)
|
|
1258
|
+
* @param options - signing options and CA overrides
|
|
1138
1259
|
* @returns the signed certificate as a DER-encoded buffer
|
|
1139
1260
|
*/
|
|
1140
1261
|
async signCertificateRequestFromDER(csrDer, options) {
|
|
1141
1262
|
const validity = options?.validity ?? 365;
|
|
1142
|
-
const tmpDir = await
|
|
1263
|
+
const tmpDir = await import_node_fs7.default.promises.mkdtemp(import_node_path5.default.join(import_node_os3.default.tmpdir(), "pki-sign-"));
|
|
1143
1264
|
try {
|
|
1144
1265
|
const csrFile = import_node_path5.default.join(tmpDir, "request.csr");
|
|
1145
1266
|
const certFile = import_node_path5.default.join(tmpDir, "certificate.pem");
|
|
1146
1267
|
const csrPem = (0, import_node_opcua_crypto2.toPem)(csrDer, "CERTIFICATE REQUEST");
|
|
1147
|
-
await
|
|
1148
|
-
|
|
1268
|
+
await import_node_fs7.default.promises.writeFile(csrFile, csrPem, "utf-8");
|
|
1269
|
+
const signingParams = { validity };
|
|
1270
|
+
if (options?.startDate) signingParams.startDate = options.startDate;
|
|
1271
|
+
if (options?.dns) signingParams.dns = options.dns;
|
|
1272
|
+
if (options?.ip) signingParams.ip = options.ip;
|
|
1273
|
+
if (options?.applicationUri) signingParams.applicationUri = options.applicationUri;
|
|
1274
|
+
if (options?.subject) signingParams.subject = options.subject;
|
|
1275
|
+
await this.signCertificateRequest(certFile, csrFile, signingParams);
|
|
1149
1276
|
const certPem = (0, import_node_opcua_crypto2.readCertificatePEM)(certFile);
|
|
1150
1277
|
return (0, import_node_opcua_crypto2.convertPEMtoDER)(certPem);
|
|
1151
1278
|
} finally {
|
|
1152
|
-
await
|
|
1279
|
+
await import_node_fs7.default.promises.rm(tmpDir, {
|
|
1280
|
+
recursive: true,
|
|
1281
|
+
force: true
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Generate a new RSA key pair, create an internal CSR, sign it
|
|
1287
|
+
* with this CA, and return both the certificate and private key
|
|
1288
|
+
* as DER-encoded buffers.
|
|
1289
|
+
*
|
|
1290
|
+
* The private key is **never stored** by the CA — it exists only
|
|
1291
|
+
* in a temporary directory that is cleaned up after the operation.
|
|
1292
|
+
*
|
|
1293
|
+
* This is used by `StartNewKeyPairRequest` (OPC UA Part 12) for
|
|
1294
|
+
* constrained devices that cannot generate their own keys.
|
|
1295
|
+
*
|
|
1296
|
+
* @param options - key generation and certificate parameters
|
|
1297
|
+
* @returns `{ certificateDer, privateKey }` — certificate as DER,
|
|
1298
|
+
* private key as a branded `PrivateKey` buffer
|
|
1299
|
+
*/
|
|
1300
|
+
async generateKeyPairAndSignDER(options) {
|
|
1301
|
+
const keySize = options.keySize ?? 2048;
|
|
1302
|
+
const validity = options.validity ?? 365;
|
|
1303
|
+
const startDate = options.startDate ?? /* @__PURE__ */ new Date();
|
|
1304
|
+
const tmpDir = await import_node_fs7.default.promises.mkdtemp(import_node_path5.default.join(import_node_os3.default.tmpdir(), "pki-keygen-"));
|
|
1305
|
+
try {
|
|
1306
|
+
const privateKeyFile = import_node_path5.default.join(tmpDir, "private_key.pem");
|
|
1307
|
+
await (0, import_node_opcua_crypto2.generatePrivateKeyFile)(privateKeyFile, keySize);
|
|
1308
|
+
const configFile = import_node_path5.default.join(tmpDir, "openssl.cnf");
|
|
1309
|
+
await import_node_fs7.default.promises.writeFile(configFile, configurationFileSimpleTemplate, "utf-8");
|
|
1310
|
+
const csrFile = import_node_path5.default.join(tmpDir, "request.csr");
|
|
1311
|
+
await createCertificateSigningRequestWithOpenSSL(csrFile, {
|
|
1312
|
+
rootDir: tmpDir,
|
|
1313
|
+
configFile,
|
|
1314
|
+
privateKey: privateKeyFile,
|
|
1315
|
+
applicationUri: options.applicationUri,
|
|
1316
|
+
subject: options.subject,
|
|
1317
|
+
dns: options.dns ?? [],
|
|
1318
|
+
ip: options.ip ?? [],
|
|
1319
|
+
purpose: import_node_opcua_crypto2.CertificatePurpose.ForApplication
|
|
1320
|
+
});
|
|
1321
|
+
const certFile = import_node_path5.default.join(tmpDir, "certificate.pem");
|
|
1322
|
+
await this.signCertificateRequest(certFile, csrFile, {
|
|
1323
|
+
applicationUri: options.applicationUri,
|
|
1324
|
+
dns: options.dns,
|
|
1325
|
+
ip: options.ip,
|
|
1326
|
+
startDate,
|
|
1327
|
+
validity
|
|
1328
|
+
});
|
|
1329
|
+
const certPem = (0, import_node_opcua_crypto2.readCertificatePEM)(certFile);
|
|
1330
|
+
const certificateDer = (0, import_node_opcua_crypto2.convertPEMtoDER)(certPem);
|
|
1331
|
+
const privateKey = (0, import_node_opcua_crypto2.readPrivateKey)(privateKeyFile);
|
|
1332
|
+
return { certificateDer, privateKey };
|
|
1333
|
+
} finally {
|
|
1334
|
+
await import_node_fs7.default.promises.rm(tmpDir, {
|
|
1335
|
+
recursive: true,
|
|
1336
|
+
force: true
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Generate a new RSA key pair, create an internal CSR, sign it
|
|
1342
|
+
* with this CA, and return the result as a PKCS#12 (PFX)
|
|
1343
|
+
* buffer bundling the certificate, private key, and CA chain.
|
|
1344
|
+
*
|
|
1345
|
+
* The private key is **never stored** by the CA — it exists only
|
|
1346
|
+
* in a temporary directory that is cleaned up after the operation.
|
|
1347
|
+
*
|
|
1348
|
+
* @param options - key generation, certificate, and PFX options
|
|
1349
|
+
* @returns the PFX as a `Buffer`
|
|
1350
|
+
*/
|
|
1351
|
+
async generateKeyPairAndSignPFX(options) {
|
|
1352
|
+
const keySize = options.keySize ?? 2048;
|
|
1353
|
+
const validity = options.validity ?? 365;
|
|
1354
|
+
const startDate = options.startDate ?? /* @__PURE__ */ new Date();
|
|
1355
|
+
const passphrase = options.passphrase ?? "";
|
|
1356
|
+
const tmpDir = await import_node_fs7.default.promises.mkdtemp(import_node_path5.default.join(import_node_os3.default.tmpdir(), "pki-keygen-pfx-"));
|
|
1357
|
+
try {
|
|
1358
|
+
const privateKeyFile = import_node_path5.default.join(tmpDir, "private_key.pem");
|
|
1359
|
+
await (0, import_node_opcua_crypto2.generatePrivateKeyFile)(privateKeyFile, keySize);
|
|
1360
|
+
const configFile = import_node_path5.default.join(tmpDir, "openssl.cnf");
|
|
1361
|
+
await import_node_fs7.default.promises.writeFile(configFile, configurationFileSimpleTemplate, "utf-8");
|
|
1362
|
+
const csrFile = import_node_path5.default.join(tmpDir, "request.csr");
|
|
1363
|
+
await createCertificateSigningRequestWithOpenSSL(csrFile, {
|
|
1364
|
+
rootDir: tmpDir,
|
|
1365
|
+
configFile,
|
|
1366
|
+
privateKey: privateKeyFile,
|
|
1367
|
+
applicationUri: options.applicationUri,
|
|
1368
|
+
subject: options.subject,
|
|
1369
|
+
dns: options.dns ?? [],
|
|
1370
|
+
ip: options.ip ?? [],
|
|
1371
|
+
purpose: import_node_opcua_crypto2.CertificatePurpose.ForApplication
|
|
1372
|
+
});
|
|
1373
|
+
const certFile = import_node_path5.default.join(tmpDir, "certificate.pem");
|
|
1374
|
+
await this.signCertificateRequest(certFile, csrFile, {
|
|
1375
|
+
applicationUri: options.applicationUri,
|
|
1376
|
+
dns: options.dns,
|
|
1377
|
+
ip: options.ip,
|
|
1378
|
+
startDate,
|
|
1379
|
+
validity
|
|
1380
|
+
});
|
|
1381
|
+
const pfxFile = import_node_path5.default.join(tmpDir, "bundle.pfx");
|
|
1382
|
+
await createPFX({
|
|
1383
|
+
certificateFile: certFile,
|
|
1384
|
+
privateKeyFile,
|
|
1385
|
+
outputFile: pfxFile,
|
|
1386
|
+
passphrase,
|
|
1387
|
+
caCertificateFiles: [this.caCertificate]
|
|
1388
|
+
});
|
|
1389
|
+
return await import_node_fs7.default.promises.readFile(pfxFile);
|
|
1390
|
+
} finally {
|
|
1391
|
+
await import_node_fs7.default.promises.rm(tmpDir, {
|
|
1153
1392
|
recursive: true,
|
|
1154
1393
|
force: true
|
|
1155
1394
|
});
|
|
@@ -1171,7 +1410,7 @@ var CertificateAuthority = class {
|
|
|
1171
1410
|
const info = (0, import_node_opcua_crypto2.exploreCertificate)(certDer);
|
|
1172
1411
|
const serial = info.tbsCertificate.serialNumber.replace(/:/g, "").toUpperCase();
|
|
1173
1412
|
const storedCertFile = import_node_path5.default.join(this.rootDir, "certs", `${serial}.pem`);
|
|
1174
|
-
if (!
|
|
1413
|
+
if (!import_node_fs7.default.existsSync(storedCertFile)) {
|
|
1175
1414
|
throw new Error(`Cannot revoke: no stored certificate found for serial ${serial} at ${storedCertFile}`);
|
|
1176
1415
|
}
|
|
1177
1416
|
await this.revokeCertificate(storedCertFile, {
|
|
@@ -1186,6 +1425,204 @@ var CertificateAuthority = class {
|
|
|
1186
1425
|
async initialize() {
|
|
1187
1426
|
await construct_CertificateAuthority(this);
|
|
1188
1427
|
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Initialize the CA directory structure and generate the
|
|
1430
|
+
* private key + CSR **without signing**.
|
|
1431
|
+
*
|
|
1432
|
+
* Use this when the CA certificate will be signed by an
|
|
1433
|
+
* external (third-party) root CA. After receiving the signed
|
|
1434
|
+
* certificate, call {@link installCACertificate} to complete
|
|
1435
|
+
* the setup.
|
|
1436
|
+
*
|
|
1437
|
+
* **Idempotent / restart-safe:**
|
|
1438
|
+
* - If the CA certificate exists and is valid → `{ status: "ready" }`
|
|
1439
|
+
* - If the CA certificate has expired → `{ status: "expired", csrPath, expiryDate }`
|
|
1440
|
+
* (a new CSR is generated, preserving the existing private key)
|
|
1441
|
+
* - If key + CSR exist but no cert (restart before install) →
|
|
1442
|
+
* `{ status: "pending", csrPath }` without regenerating
|
|
1443
|
+
* - Otherwise → generates key + CSR → `{ status: "created", csrPath }`
|
|
1444
|
+
*
|
|
1445
|
+
* @returns an {@link InitializeCSRResult} describing the CA state
|
|
1446
|
+
*/
|
|
1447
|
+
async initializeCSR() {
|
|
1448
|
+
const caRootDir = import_node_path5.default.resolve(this.rootDir);
|
|
1449
|
+
mkdirRecursiveSync(caRootDir);
|
|
1450
|
+
for (const dir of ["private", "public", "certs", "crl", "conf"]) {
|
|
1451
|
+
mkdirRecursiveSync(import_node_path5.default.join(caRootDir, dir));
|
|
1452
|
+
}
|
|
1453
|
+
const caCertFile = this.caCertificate;
|
|
1454
|
+
const privateKeyFile = import_node_path5.default.join(caRootDir, "private/cakey.pem");
|
|
1455
|
+
const csrFile = import_node_path5.default.join(caRootDir, "private/cakey.csr");
|
|
1456
|
+
if (import_node_fs7.default.existsSync(caCertFile)) {
|
|
1457
|
+
const certDer = (0, import_node_opcua_crypto2.convertPEMtoDER)((0, import_node_opcua_crypto2.readCertificatePEM)(caCertFile));
|
|
1458
|
+
const certInfo = (0, import_node_opcua_crypto2.exploreCertificate)(certDer);
|
|
1459
|
+
const notAfter = certInfo.tbsCertificate.validity.notAfter;
|
|
1460
|
+
if (notAfter.getTime() < Date.now()) {
|
|
1461
|
+
debugLog("CA certificate has expired \u2014 generating renewal CSR");
|
|
1462
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
1463
|
+
return { status: "expired", csrPath: csrFile, expiryDate: notAfter };
|
|
1464
|
+
}
|
|
1465
|
+
debugLog("CA certificate already exists and is valid \u2014 ready");
|
|
1466
|
+
return { status: "ready" };
|
|
1467
|
+
}
|
|
1468
|
+
if (import_node_fs7.default.existsSync(privateKeyFile) && import_node_fs7.default.existsSync(csrFile)) {
|
|
1469
|
+
debugLog("CA key + CSR already exist \u2014 pending external signing");
|
|
1470
|
+
return { status: "pending", csrPath: csrFile };
|
|
1471
|
+
}
|
|
1472
|
+
const serial = import_node_path5.default.join(caRootDir, "serial");
|
|
1473
|
+
if (!import_node_fs7.default.existsSync(serial)) {
|
|
1474
|
+
await import_node_fs7.default.promises.writeFile(serial, "1000");
|
|
1475
|
+
}
|
|
1476
|
+
const crlNumber = import_node_path5.default.join(caRootDir, "crlnumber");
|
|
1477
|
+
if (!import_node_fs7.default.existsSync(crlNumber)) {
|
|
1478
|
+
await import_node_fs7.default.promises.writeFile(crlNumber, "1000");
|
|
1479
|
+
}
|
|
1480
|
+
const indexFile = import_node_path5.default.join(caRootDir, "index.txt");
|
|
1481
|
+
if (!import_node_fs7.default.existsSync(indexFile)) {
|
|
1482
|
+
await import_node_fs7.default.promises.writeFile(indexFile, "");
|
|
1483
|
+
}
|
|
1484
|
+
const indexFileAttr = import_node_path5.default.join(caRootDir, "index.txt.attr");
|
|
1485
|
+
if (!import_node_fs7.default.existsSync(indexFileAttr)) {
|
|
1486
|
+
await import_node_fs7.default.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
1487
|
+
}
|
|
1488
|
+
const caConfigFile = this.configFile;
|
|
1489
|
+
let data = configurationFileTemplate;
|
|
1490
|
+
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
1491
|
+
await import_node_fs7.default.promises.writeFile(caConfigFile, data);
|
|
1492
|
+
if (!import_node_fs7.default.existsSync(privateKeyFile)) {
|
|
1493
|
+
await (0, import_node_opcua_crypto2.generatePrivateKeyFile)(privateKeyFile, this.keySize);
|
|
1494
|
+
}
|
|
1495
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
1496
|
+
return { status: "created", csrPath: csrFile };
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Check whether the CA certificate needs renewal and, if so,
|
|
1500
|
+
* generate a new CSR for re-signing by the external root CA.
|
|
1501
|
+
*
|
|
1502
|
+
* Use this while the CA is running to detect upcoming expiry
|
|
1503
|
+
* **before** it actually expires. The existing private key is
|
|
1504
|
+
* preserved so previously issued certs remain valid.
|
|
1505
|
+
*
|
|
1506
|
+
* @param thresholdDays - number of days before expiry at which
|
|
1507
|
+
* to trigger renewal (default: 30)
|
|
1508
|
+
* @returns an {@link InitializeCSRResult} — `"expired"` if
|
|
1509
|
+
* renewal is needed, `"ready"` if the cert is still valid
|
|
1510
|
+
*/
|
|
1511
|
+
async renewCSR(thresholdDays = 30) {
|
|
1512
|
+
const caRootDir = import_node_path5.default.resolve(this.rootDir);
|
|
1513
|
+
const caCertFile = this.caCertificate;
|
|
1514
|
+
const privateKeyFile = import_node_path5.default.join(caRootDir, "private/cakey.pem");
|
|
1515
|
+
const csrFile = import_node_path5.default.join(caRootDir, "private/cakey.csr");
|
|
1516
|
+
if (!import_node_fs7.default.existsSync(caCertFile)) {
|
|
1517
|
+
return this.initializeCSR();
|
|
1518
|
+
}
|
|
1519
|
+
const certDer = (0, import_node_opcua_crypto2.convertPEMtoDER)((0, import_node_opcua_crypto2.readCertificatePEM)(caCertFile));
|
|
1520
|
+
const certInfo = (0, import_node_opcua_crypto2.exploreCertificate)(certDer);
|
|
1521
|
+
const notAfter = certInfo.tbsCertificate.validity.notAfter;
|
|
1522
|
+
const thresholdMs = thresholdDays * 24 * 60 * 60 * 1e3;
|
|
1523
|
+
if (notAfter.getTime() - Date.now() < thresholdMs) {
|
|
1524
|
+
debugLog(`CA certificate expires within ${thresholdDays} days \u2014 generating renewal CSR`);
|
|
1525
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
1526
|
+
return { status: "expired", csrPath: csrFile, expiryDate: notAfter };
|
|
1527
|
+
}
|
|
1528
|
+
return { status: "ready" };
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Generate a CSR using the existing private key.
|
|
1532
|
+
* @internal
|
|
1533
|
+
*/
|
|
1534
|
+
async _generateCSR(caRootDir, privateKeyFile, csrFile) {
|
|
1535
|
+
const subjectOpt = ` -subj "${this.subject.toString()}" `;
|
|
1536
|
+
processAltNames({});
|
|
1537
|
+
const options = { cwd: caRootDir };
|
|
1538
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1539
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
1540
|
+
await execute_openssl(
|
|
1541
|
+
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " + q3(n4(privateKeyFile)) + " -out " + q3(n4(csrFile)) + " " + subjectOpt,
|
|
1542
|
+
options
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Install an externally-signed CA certificate and generate
|
|
1547
|
+
* the initial CRL.
|
|
1548
|
+
*
|
|
1549
|
+
* Call this after {@link initializeCSR} once the external
|
|
1550
|
+
* root CA has signed the CSR.
|
|
1551
|
+
*
|
|
1552
|
+
* **Safety checks:**
|
|
1553
|
+
* - Verifies that the certificate's public key matches the
|
|
1554
|
+
* CA private key before installing.
|
|
1555
|
+
*
|
|
1556
|
+
* @param signedCertFile - path to the PEM-encoded signed
|
|
1557
|
+
* CA certificate (issued by the external root CA)
|
|
1558
|
+
* @returns an {@link InstallCACertificateResult} with
|
|
1559
|
+
* `status: "success"` or `status: "error"` and a `reason`
|
|
1560
|
+
*/
|
|
1561
|
+
async installCACertificate(signedCertFile) {
|
|
1562
|
+
const caRootDir = import_node_path5.default.resolve(this.rootDir);
|
|
1563
|
+
const caCertFile = this.caCertificate;
|
|
1564
|
+
const privateKeyFile = import_node_path5.default.join(caRootDir, "private/cakey.pem");
|
|
1565
|
+
const fullPem = await import_node_fs7.default.promises.readFile(signedCertFile, "utf8");
|
|
1566
|
+
const pemBlocks = fullPem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
1567
|
+
if (!pemBlocks || pemBlocks.length === 0) {
|
|
1568
|
+
return {
|
|
1569
|
+
status: "error",
|
|
1570
|
+
reason: "no_certificate_found",
|
|
1571
|
+
message: "The provided file does not contain any PEM-encoded certificate."
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
const certDer = (0, import_node_opcua_crypto2.convertPEMtoDER)(pemBlocks[0]);
|
|
1575
|
+
const privateKey = (0, import_node_opcua_crypto2.readPrivateKey)(privateKeyFile);
|
|
1576
|
+
if (!(0, import_node_opcua_crypto2.certificateMatchesPrivateKey)(certDer, privateKey)) {
|
|
1577
|
+
return {
|
|
1578
|
+
status: "error",
|
|
1579
|
+
reason: "certificate_key_mismatch",
|
|
1580
|
+
message: "The provided certificate does not match the CA private key. Ensure the certificate was signed from the CSR generated by initializeCSR()."
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
await import_node_fs7.default.promises.writeFile(caCertFile, `${pemBlocks[0]}
|
|
1584
|
+
`);
|
|
1585
|
+
const issuerChainFile = this.issuerCertificateChain;
|
|
1586
|
+
if (pemBlocks.length > 1) {
|
|
1587
|
+
const issuerPem = `${pemBlocks.slice(1).join("\n")}
|
|
1588
|
+
`;
|
|
1589
|
+
await import_node_fs7.default.promises.writeFile(issuerChainFile, issuerPem);
|
|
1590
|
+
debugLog(`Stored ${pemBlocks.length - 1} issuer certificate(s) in issuer_chain.pem`);
|
|
1591
|
+
} else {
|
|
1592
|
+
if (import_node_fs7.default.existsSync(issuerChainFile)) {
|
|
1593
|
+
await import_node_fs7.default.promises.unlink(issuerChainFile);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
const options = { cwd: caRootDir };
|
|
1597
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1598
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
1599
|
+
await regenerateCrl(this.revocationList, configOption, options);
|
|
1600
|
+
return { status: "success" };
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Sign a CSR with CA extensions (`v3_ca`), producing a
|
|
1604
|
+
* subordinate CA certificate.
|
|
1605
|
+
*
|
|
1606
|
+
* Unlike {@link signCertificateRequest} which signs with
|
|
1607
|
+
* end-entity extensions (SANs, etc.), this method signs
|
|
1608
|
+
* with `basicConstraints = CA:TRUE` and `keyUsage =
|
|
1609
|
+
* keyCertSign, cRLSign`.
|
|
1610
|
+
*
|
|
1611
|
+
* @param certFile - output path for the signed CA cert (PEM)
|
|
1612
|
+
* @param csrFile - path to the subordinate CA's CSR
|
|
1613
|
+
* @param params - signing parameters
|
|
1614
|
+
*/
|
|
1615
|
+
async signCACertificateRequest(certFile, csrFile, params) {
|
|
1616
|
+
const caRootDir = import_node_path5.default.resolve(this.rootDir);
|
|
1617
|
+
const options = { cwd: caRootDir };
|
|
1618
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1619
|
+
const validity = params.validity ?? 3650;
|
|
1620
|
+
await execute_openssl(
|
|
1621
|
+
` x509 -sha256 -req -days ${validity} -text -extensions v3_ca -extfile ` + q3(n4(configFile)) + " -in " + q3(n4(csrFile)) + " -CA " + q3(n4(this.caCertificate)) + " -CAkey " + q3(n4(import_node_path5.default.join(caRootDir, "private/cakey.pem"))) + " -CAserial " + q3(n4(import_node_path5.default.join(caRootDir, "serial"))) + " -out " + q3(n4(certFile)),
|
|
1622
|
+
options
|
|
1623
|
+
);
|
|
1624
|
+
await this.constructCertificateChain(certFile);
|
|
1625
|
+
}
|
|
1189
1626
|
/**
|
|
1190
1627
|
* Rebuild the combined CA certificate + CRL file.
|
|
1191
1628
|
*
|
|
@@ -1195,13 +1632,13 @@ var CertificateAuthority = class {
|
|
|
1195
1632
|
*/
|
|
1196
1633
|
async constructCACertificateWithCRL() {
|
|
1197
1634
|
const cacertWithCRL = this.caCertificateWithCrl;
|
|
1198
|
-
if (
|
|
1199
|
-
await
|
|
1635
|
+
if (import_node_fs7.default.existsSync(this.revocationList)) {
|
|
1636
|
+
await import_node_fs7.default.promises.writeFile(
|
|
1200
1637
|
cacertWithCRL,
|
|
1201
|
-
|
|
1638
|
+
import_node_fs7.default.readFileSync(this.caCertificate, "utf8") + import_node_fs7.default.readFileSync(this.revocationList, "utf8")
|
|
1202
1639
|
);
|
|
1203
1640
|
} else {
|
|
1204
|
-
await
|
|
1641
|
+
await import_node_fs7.default.promises.writeFile(cacertWithCRL, import_node_fs7.default.readFileSync(this.caCertificate));
|
|
1205
1642
|
}
|
|
1206
1643
|
}
|
|
1207
1644
|
/**
|
|
@@ -1211,14 +1648,15 @@ var CertificateAuthority = class {
|
|
|
1211
1648
|
* @param certificate - path to the certificate file to extend
|
|
1212
1649
|
*/
|
|
1213
1650
|
async constructCertificateChain(certificate) {
|
|
1214
|
-
(0,
|
|
1215
|
-
(0,
|
|
1651
|
+
(0, import_node_assert7.default)(import_node_fs7.default.existsSync(certificate));
|
|
1652
|
+
(0, import_node_assert7.default)(import_node_fs7.default.existsSync(this.caCertificate));
|
|
1216
1653
|
debugLog(import_chalk5.default.yellow(" certificate file :"), import_chalk5.default.cyan(certificate));
|
|
1217
|
-
await
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1654
|
+
let chain = await import_node_fs7.default.promises.readFile(certificate, "utf8");
|
|
1655
|
+
chain += await import_node_fs7.default.promises.readFile(this.caCertificate, "utf8");
|
|
1656
|
+
if (import_node_fs7.default.existsSync(this.issuerCertificateChain)) {
|
|
1657
|
+
chain += await import_node_fs7.default.promises.readFile(this.issuerCertificateChain, "utf8");
|
|
1658
|
+
}
|
|
1659
|
+
await import_node_fs7.default.promises.writeFile(certificate, chain);
|
|
1222
1660
|
}
|
|
1223
1661
|
/**
|
|
1224
1662
|
* Create a self-signed certificate using OpenSSL.
|
|
@@ -1228,8 +1666,8 @@ var CertificateAuthority = class {
|
|
|
1228
1666
|
* @param params - certificate parameters (subject, validity, SANs)
|
|
1229
1667
|
*/
|
|
1230
1668
|
async createSelfSignedCertificate(certificateFile, privateKey, params) {
|
|
1231
|
-
(0,
|
|
1232
|
-
(0,
|
|
1669
|
+
(0, import_node_assert7.default)(typeof privateKey === "string");
|
|
1670
|
+
(0, import_node_assert7.default)(import_node_fs7.default.existsSync(privateKey));
|
|
1233
1671
|
if (!certificateFileExist(certificateFile)) {
|
|
1234
1672
|
return;
|
|
1235
1673
|
}
|
|
@@ -1237,7 +1675,7 @@ var CertificateAuthority = class {
|
|
|
1237
1675
|
adjustApplicationUri(params);
|
|
1238
1676
|
processAltNames(params);
|
|
1239
1677
|
const csrFile = `${certificateFile}_csr`;
|
|
1240
|
-
(0,
|
|
1678
|
+
(0, import_node_assert7.default)(csrFile);
|
|
1241
1679
|
const configFile = generateStaticConfig(this.configFile, { cwd: this.rootDir });
|
|
1242
1680
|
const options = {
|
|
1243
1681
|
cwd: this.rootDir,
|
|
@@ -1248,19 +1686,19 @@ var CertificateAuthority = class {
|
|
|
1248
1686
|
const subjectOptions = subject && subject.length > 1 ? ` -subj ${subject} ` : "";
|
|
1249
1687
|
displaySubtitle("- the certificate signing request");
|
|
1250
1688
|
await execute_openssl(
|
|
1251
|
-
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " +
|
|
1689
|
+
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " + q3(n4(privateKey)) + " -out " + q3(n4(csrFile)),
|
|
1252
1690
|
options
|
|
1253
1691
|
);
|
|
1254
1692
|
displaySubtitle("- creating the self-signed certificate");
|
|
1255
1693
|
await execute_openssl(
|
|
1256
|
-
"ca -selfsign -keyfile " +
|
|
1694
|
+
"ca -selfsign -keyfile " + q3(n4(privateKey)) + " -startdate " + x509Date(params.startDate) + " -enddate " + x509Date(params.endDate) + " -batch -out " + q3(n4(certificateFile)) + " -in " + q3(n4(csrFile)),
|
|
1257
1695
|
options
|
|
1258
1696
|
);
|
|
1259
1697
|
displaySubtitle("- dump the certificate for a check");
|
|
1260
|
-
await execute_openssl(`x509 -in ${
|
|
1698
|
+
await execute_openssl(`x509 -in ${q3(n4(certificateFile))} -dates -fingerprint -purpose -noout`, {});
|
|
1261
1699
|
displaySubtitle("- verify self-signed certificate");
|
|
1262
|
-
await execute_openssl_no_failure(`verify -verbose -CAfile ${
|
|
1263
|
-
await
|
|
1700
|
+
await execute_openssl_no_failure(`verify -verbose -CAfile ${q3(n4(certificateFile))} ${q3(n4(certificateFile))}`, options);
|
|
1701
|
+
await import_node_fs7.default.promises.unlink(csrFile);
|
|
1264
1702
|
}
|
|
1265
1703
|
/**
|
|
1266
1704
|
* Revoke a certificate and regenerate the CRL.
|
|
@@ -1289,22 +1727,22 @@ var CertificateAuthority = class {
|
|
|
1289
1727
|
setEnv("ALTNAME", "");
|
|
1290
1728
|
const randomFile = import_node_path5.default.join(this.rootDir, "random.rnd");
|
|
1291
1729
|
setEnv("RANDFILE", randomFile);
|
|
1292
|
-
const configOption = ` -config ${
|
|
1730
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
1293
1731
|
const reason = params.reason || "keyCompromise";
|
|
1294
|
-
(0,
|
|
1732
|
+
(0, import_node_assert7.default)(crlReasons.indexOf(reason) >= 0);
|
|
1295
1733
|
displayTitle(`Revoking certificate ${certificate}`);
|
|
1296
1734
|
displaySubtitle("Revoke certificate");
|
|
1297
|
-
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${
|
|
1735
|
+
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${q3(certificate)} -crl_reason ${reason}`, options);
|
|
1298
1736
|
await regenerateCrl(this.revocationList, configOption, options);
|
|
1299
1737
|
displaySubtitle("Verify that certificate is revoked");
|
|
1300
1738
|
await execute_openssl_no_failure(
|
|
1301
|
-
"verify -verbose -CRLfile " +
|
|
1739
|
+
"verify -verbose -CRLfile " + q3(n4(this.revocationList)) + " -CAfile " + q3(n4(this.caCertificate)) + " -crl_check " + q3(n4(certificate)),
|
|
1302
1740
|
options
|
|
1303
1741
|
);
|
|
1304
1742
|
displaySubtitle("Produce CRL in DER form ");
|
|
1305
|
-
await execute_openssl(`crl -in ${
|
|
1743
|
+
await execute_openssl(`crl -in ${q3(n4(this.revocationList))} -out crl/revocation_list.der -outform der`, options);
|
|
1306
1744
|
displaySubtitle("Produce CRL in PEM form ");
|
|
1307
|
-
await execute_openssl(`crl -in ${
|
|
1745
|
+
await execute_openssl(`crl -in ${q3(n4(this.revocationList))} -out crl/revocation_list.pem -outform pem -text `, options);
|
|
1308
1746
|
}
|
|
1309
1747
|
/**
|
|
1310
1748
|
* Sign a Certificate Signing Request (CSR) with this CA.
|
|
@@ -1320,7 +1758,7 @@ var CertificateAuthority = class {
|
|
|
1320
1758
|
*/
|
|
1321
1759
|
async signCertificateRequest(certificate, certificateSigningRequestFilename, params1) {
|
|
1322
1760
|
await ensure_openssl_installed();
|
|
1323
|
-
(0,
|
|
1761
|
+
(0, import_node_assert7.default)(import_node_fs7.default.existsSync(certificateSigningRequestFilename));
|
|
1324
1762
|
if (!certificateFileExist(certificate)) {
|
|
1325
1763
|
return "";
|
|
1326
1764
|
}
|
|
@@ -1347,11 +1785,11 @@ var CertificateAuthority = class {
|
|
|
1347
1785
|
displaySubtitle("- then we ask the authority to sign the certificate signing request");
|
|
1348
1786
|
const configOption = ` -config ${configFile}`;
|
|
1349
1787
|
await execute_openssl(
|
|
1350
|
-
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " +
|
|
1788
|
+
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " + q3(n4(certificate)) + " -in " + q3(n4(certificateSigningRequestFilename)),
|
|
1351
1789
|
options
|
|
1352
1790
|
);
|
|
1353
1791
|
displaySubtitle("- dump the certificate for a check");
|
|
1354
|
-
await execute_openssl(`x509 -in ${
|
|
1792
|
+
await execute_openssl(`x509 -in ${q3(n4(certificate))} -dates -fingerprint -purpose -noout`, options);
|
|
1355
1793
|
displaySubtitle("- construct CA certificate with CRL");
|
|
1356
1794
|
await this.constructCACertificateWithCRL();
|
|
1357
1795
|
displaySubtitle("- construct certificate chain");
|
|
@@ -1374,7 +1812,7 @@ var CertificateAuthority = class {
|
|
|
1374
1812
|
const _configOption = ` -config ${configFile}`;
|
|
1375
1813
|
_configOption;
|
|
1376
1814
|
await execute_openssl_no_failure(
|
|
1377
|
-
`verify -verbose -CAfile ${
|
|
1815
|
+
`verify -verbose -CAfile ${q3(n4(this.caCertificateWithCrl))} ${q3(n4(certificate))}`,
|
|
1378
1816
|
options
|
|
1379
1817
|
);
|
|
1380
1818
|
}
|
|
@@ -1383,7 +1821,7 @@ var CertificateAuthority = class {
|
|
|
1383
1821
|
|
|
1384
1822
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1385
1823
|
var import_node_events = require("events");
|
|
1386
|
-
var
|
|
1824
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
1387
1825
|
var import_node_path6 = __toESM(require("path"));
|
|
1388
1826
|
var import_global_mutex = require("@ster5/global-mutex");
|
|
1389
1827
|
var import_chalk6 = __toESM(require("chalk"));
|
|
@@ -1391,21 +1829,21 @@ var import_chokidar = __toESM(require("chokidar"));
|
|
|
1391
1829
|
var import_node_opcua_crypto5 = require("node-opcua-crypto");
|
|
1392
1830
|
|
|
1393
1831
|
// packages/node-opcua-pki/lib/toolbox/without_openssl/create_certificate_signing_request.ts
|
|
1394
|
-
var
|
|
1395
|
-
var
|
|
1832
|
+
var import_node_assert8 = __toESM(require("assert"));
|
|
1833
|
+
var import_node_fs8 = __toESM(require("fs"));
|
|
1396
1834
|
var import_node_opcua_crypto3 = require("node-opcua-crypto");
|
|
1397
1835
|
async function createCertificateSigningRequestAsync(certificateSigningRequestFilename, params) {
|
|
1398
|
-
(0,
|
|
1399
|
-
(0,
|
|
1400
|
-
(0,
|
|
1401
|
-
(0,
|
|
1402
|
-
(0,
|
|
1403
|
-
(0,
|
|
1404
|
-
(0,
|
|
1405
|
-
(0,
|
|
1836
|
+
(0, import_node_assert8.default)(params);
|
|
1837
|
+
(0, import_node_assert8.default)(params.rootDir);
|
|
1838
|
+
(0, import_node_assert8.default)(params.configFile);
|
|
1839
|
+
(0, import_node_assert8.default)(params.privateKey);
|
|
1840
|
+
(0, import_node_assert8.default)(typeof params.privateKey === "string");
|
|
1841
|
+
(0, import_node_assert8.default)(import_node_fs8.default.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
|
|
1842
|
+
(0, import_node_assert8.default)(import_node_fs8.default.existsSync(params.rootDir), "RootDir key must exist");
|
|
1843
|
+
(0, import_node_assert8.default)(typeof certificateSigningRequestFilename === "string");
|
|
1406
1844
|
const subject = params.subject ? new import_node_opcua_crypto3.Subject(params.subject).toString() : void 0;
|
|
1407
1845
|
displaySubtitle("- Creating a Certificate Signing Request with subtile");
|
|
1408
|
-
const privateKeyPem = await
|
|
1846
|
+
const privateKeyPem = await import_node_fs8.default.promises.readFile(params.privateKey, "utf-8");
|
|
1409
1847
|
const privateKey = await (0, import_node_opcua_crypto3.pemToPrivateKey)(privateKeyPem);
|
|
1410
1848
|
const { csr } = await (0, import_node_opcua_crypto3.createCertificateSigningRequest)({
|
|
1411
1849
|
privateKey,
|
|
@@ -1415,33 +1853,33 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
1415
1853
|
applicationUri: params.applicationUri,
|
|
1416
1854
|
purpose: params.purpose
|
|
1417
1855
|
});
|
|
1418
|
-
await
|
|
1856
|
+
await import_node_fs8.default.promises.writeFile(certificateSigningRequestFilename, csr, "utf-8");
|
|
1419
1857
|
display(`- privateKey ${params.privateKey}`);
|
|
1420
1858
|
display(`- certificateSigningRequestFilename ${certificateSigningRequestFilename}`);
|
|
1421
1859
|
}
|
|
1422
1860
|
|
|
1423
1861
|
// packages/node-opcua-pki/lib/toolbox/without_openssl/create_self_signed_certificate.ts
|
|
1424
|
-
var
|
|
1425
|
-
var
|
|
1862
|
+
var import_node_assert9 = __toESM(require("assert"));
|
|
1863
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
1426
1864
|
var import_node_opcua_crypto4 = require("node-opcua-crypto");
|
|
1427
1865
|
async function createSelfSignedCertificateAsync(certificate, params) {
|
|
1428
1866
|
params.purpose = params.purpose || import_node_opcua_crypto4.CertificatePurpose.ForApplication;
|
|
1429
|
-
(0,
|
|
1430
|
-
(0,
|
|
1431
|
-
(0,
|
|
1432
|
-
(0,
|
|
1867
|
+
(0, import_node_assert9.default)(params.purpose, "Please provide a Certificate Purpose");
|
|
1868
|
+
(0, import_node_assert9.default)(import_node_fs9.default.existsSync(params.configFile));
|
|
1869
|
+
(0, import_node_assert9.default)(import_node_fs9.default.existsSync(params.rootDir));
|
|
1870
|
+
(0, import_node_assert9.default)(import_node_fs9.default.existsSync(params.privateKey));
|
|
1433
1871
|
if (!params.subject) {
|
|
1434
1872
|
throw Error("Missing subject");
|
|
1435
1873
|
}
|
|
1436
|
-
(0,
|
|
1437
|
-
(0,
|
|
1874
|
+
(0, import_node_assert9.default)(typeof params.applicationUri === "string");
|
|
1875
|
+
(0, import_node_assert9.default)(Array.isArray(params.dns));
|
|
1438
1876
|
adjustDate(params);
|
|
1439
|
-
(0,
|
|
1877
|
+
(0, import_node_assert9.default)(Object.prototype.hasOwnProperty.call(params, "validity"));
|
|
1440
1878
|
let subject = new import_node_opcua_crypto4.Subject(params.subject);
|
|
1441
1879
|
subject = subject.toString();
|
|
1442
1880
|
const purpose = params.purpose;
|
|
1443
1881
|
displayTitle("Generate a certificate request");
|
|
1444
|
-
const privateKeyPem = await
|
|
1882
|
+
const privateKeyPem = await import_node_fs9.default.promises.readFile(params.privateKey, "utf-8");
|
|
1445
1883
|
const privateKey = await (0, import_node_opcua_crypto4.pemToPrivateKey)(privateKeyPem);
|
|
1446
1884
|
const { cert } = await (0, import_node_opcua_crypto4.createSelfSignedCertificate)({
|
|
1447
1885
|
privateKey,
|
|
@@ -1454,19 +1892,15 @@ async function createSelfSignedCertificateAsync(certificate, params) {
|
|
|
1454
1892
|
applicationUri: params.applicationUri,
|
|
1455
1893
|
purpose
|
|
1456
1894
|
});
|
|
1457
|
-
await
|
|
1895
|
+
await import_node_fs9.default.promises.writeFile(certificate, cert, "utf-8");
|
|
1458
1896
|
}
|
|
1459
1897
|
async function createSelfSignedCertificate(certificate, params) {
|
|
1460
1898
|
await createSelfSignedCertificateAsync(certificate, params);
|
|
1461
1899
|
}
|
|
1462
1900
|
|
|
1463
|
-
// packages/node-opcua-pki/lib/pki/templates/simple_config_template.cnf.ts
|
|
1464
|
-
var config3 = '##################################################################################################\n## SIMPLE OPENSSL CONFIG FILE FOR SELF-SIGNED CERTIFICATE GENERATION\n################################################################################################################\n\ndistinguished_name = req_distinguished_name\ndefault_md = sha1\n\ndefault_md = sha256 # The default digest algorithm\n\n[ v3_ca ]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer:always\n\n# authorityKeyIdentifier = keyid\nbasicConstraints = CA:TRUE\nkeyUsage = critical, cRLSign, keyCertSign\nnsComment = "Self-signed Certificate for CA generated by Node-OPCUA Certificate utility"\n#nsCertType = sslCA, emailCA\n#subjectAltName = email:copy\n#issuerAltName = issuer:copy\n#obj = DER:02:03\n# crlDistributionPoints = @crl_info\n# [ crl_info ]\n# URI.0 = http://localhost:8900/crl.pem\nsubjectAltName = $ENV::ALTNAME\n\n[ req ]\ndays = 390\nreq_extensions = v3_req\nx509_extensions = v3_ca\n\n[v3_req]\nbasicConstraints = CA:false\nkeyUsage = critical, cRLSign, keyCertSign\nsubjectAltName = $ENV::ALTNAME\n\n[ v3_ca_signed]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid,issuer\nbasicConstraints = critical, CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign\nextendedKeyUsage = clientAuth,serverAuth \nnsComment = "certificate generated by Node-OPCUA Certificate utility and signed by a CA"\nsubjectAltName = $ENV::ALTNAME\n[ v3_selfsigned]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid,issuer\nbasicConstraints = critical, CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign\nextendedKeyUsage = clientAuth,serverAuth \nnsComment = "Self-signed certificate generated by Node-OPCUA Certificate utility"\nsubjectAltName = $ENV::ALTNAME\n[ req_distinguished_name ]\ncountryName = Country Name (2 letter code)\ncountryName_default = FR\ncountryName_min = 2\ncountryName_max = 2\n# stateOrProvinceName = State or Province Name (full name)\n# stateOrProvinceName_default = Ile de France\n# localityName = Locality Name (city, district)\n# localityName_default = Paris\norganizationName = Organization Name (company)\norganizationName_default = NodeOPCUA\n# organizationalUnitName = Organizational Unit Name (department, division)\n# organizationalUnitName_default = R&D\ncommonName = Common Name (hostname, FQDN, IP, or your name)\ncommonName_max = 256\ncommonName_default = NodeOPCUA\n# emailAddress = Email Address\n# emailAddress_max = 40\n# emailAddress_default = node-opcua (at) node-opcua (dot) com\nsubjectAltName = $ENV::ALTNAME';
|
|
1465
|
-
var simple_config_template_cnf_default = config3;
|
|
1466
|
-
|
|
1467
1901
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1468
|
-
var
|
|
1469
|
-
var fsWriteFile =
|
|
1902
|
+
var configurationFileSimpleTemplate2 = simple_config_template_cnf_default;
|
|
1903
|
+
var fsWriteFile = import_node_fs10.default.promises.writeFile;
|
|
1470
1904
|
function getOrComputeInfo(entry) {
|
|
1471
1905
|
if (!entry.info) {
|
|
1472
1906
|
entry.info = exploreCertificateCached(entry.certificate);
|
|
@@ -1693,7 +2127,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
1693
2127
|
this.#location = makePath(options.location, "");
|
|
1694
2128
|
this.keySize = options.keySize;
|
|
1695
2129
|
mkdirRecursiveSync(options.location);
|
|
1696
|
-
if (!
|
|
2130
|
+
if (!import_node_fs10.default.existsSync(this.#location)) {
|
|
1697
2131
|
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
1698
2132
|
}
|
|
1699
2133
|
}
|
|
@@ -2006,15 +2440,15 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2006
2440
|
mkdirRecursiveSync(import_node_path6.default.join(pkiDir, "issuers"));
|
|
2007
2441
|
mkdirRecursiveSync(import_node_path6.default.join(pkiDir, "issuers/certs"));
|
|
2008
2442
|
mkdirRecursiveSync(import_node_path6.default.join(pkiDir, "issuers/crl"));
|
|
2009
|
-
if (!
|
|
2443
|
+
if (!import_node_fs10.default.existsSync(this.configFile) || !import_node_fs10.default.existsSync(this.privateKey)) {
|
|
2010
2444
|
return await this.withLock2(async () => {
|
|
2011
2445
|
if (this.state === 3 /* Disposing */ || this.state === 4 /* Disposed */) {
|
|
2012
2446
|
return;
|
|
2013
2447
|
}
|
|
2014
|
-
if (!
|
|
2015
|
-
|
|
2448
|
+
if (!import_node_fs10.default.existsSync(this.configFile)) {
|
|
2449
|
+
import_node_fs10.default.writeFileSync(this.configFile, configurationFileSimpleTemplate2);
|
|
2016
2450
|
}
|
|
2017
|
-
if (!
|
|
2451
|
+
if (!import_node_fs10.default.existsSync(this.privateKey)) {
|
|
2018
2452
|
debugLog("generating private key ...");
|
|
2019
2453
|
await (0, import_node_opcua_crypto5.generatePrivateKeyFile)(this.privateKey, this.keySize);
|
|
2020
2454
|
await this.#readCertificates();
|
|
@@ -2104,7 +2538,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2104
2538
|
if (typeof params.applicationUri !== "string") {
|
|
2105
2539
|
throw new Error("createSelfSignedCertificate: expecting applicationUri to be a string");
|
|
2106
2540
|
}
|
|
2107
|
-
if (!
|
|
2541
|
+
if (!import_node_fs10.default.existsSync(this.privateKey)) {
|
|
2108
2542
|
throw new Error(`Cannot find private key ${this.privateKey}`);
|
|
2109
2543
|
}
|
|
2110
2544
|
let certificateFilename = import_node_path6.default.join(this.rootDir, "own/certs/self_signed_certificate.pem");
|
|
@@ -2168,7 +2602,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2168
2602
|
return "Good" /* Good */;
|
|
2169
2603
|
}
|
|
2170
2604
|
const filename = import_node_path6.default.join(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
|
|
2171
|
-
await
|
|
2605
|
+
await import_node_fs10.default.promises.writeFile(filename, pemCertificate, "ascii");
|
|
2172
2606
|
this.#thumbs.issuers.certs.set(fingerprint, { certificate, filename });
|
|
2173
2607
|
if (addInTrustList) {
|
|
2174
2608
|
await this.trustCertificate(certificate);
|
|
@@ -2191,8 +2625,9 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2191
2625
|
index.set(key, { crls: [], serialNumbers: {} });
|
|
2192
2626
|
}
|
|
2193
2627
|
const pemCertificate = (0, import_node_opcua_crypto5.toPem)(crl, "X509 CRL");
|
|
2194
|
-
const
|
|
2195
|
-
|
|
2628
|
+
const sanitizedKey = key.replace(/:/g, "");
|
|
2629
|
+
const filename = import_node_path6.default.join(folder, `crl_[${sanitizedKey}].pem`);
|
|
2630
|
+
await import_node_fs10.default.promises.writeFile(filename, pemCertificate, "ascii");
|
|
2196
2631
|
await this.#onCrlFileAdded(index, filename);
|
|
2197
2632
|
await this.#waitAndCheckCRLProcessingStatus();
|
|
2198
2633
|
return "Good" /* Good */;
|
|
@@ -2211,11 +2646,11 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2211
2646
|
async clearRevocationLists(target) {
|
|
2212
2647
|
const clearFolder = async (folder, index) => {
|
|
2213
2648
|
try {
|
|
2214
|
-
const files = await
|
|
2649
|
+
const files = await import_node_fs10.default.promises.readdir(folder);
|
|
2215
2650
|
for (const file of files) {
|
|
2216
2651
|
const ext = import_node_path6.default.extname(file).toLowerCase();
|
|
2217
2652
|
if (ext === ".crl" || ext === ".pem" || ext === ".der") {
|
|
2218
|
-
await
|
|
2653
|
+
await import_node_fs10.default.promises.unlink(import_node_path6.default.join(folder, file));
|
|
2219
2654
|
}
|
|
2220
2655
|
}
|
|
2221
2656
|
} catch (err) {
|
|
@@ -2257,7 +2692,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2257
2692
|
return null;
|
|
2258
2693
|
}
|
|
2259
2694
|
try {
|
|
2260
|
-
await
|
|
2695
|
+
await import_node_fs10.default.promises.unlink(entry.filename);
|
|
2261
2696
|
} catch (err) {
|
|
2262
2697
|
if (err.code !== "ENOENT") {
|
|
2263
2698
|
throw err;
|
|
@@ -2281,7 +2716,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2281
2716
|
return null;
|
|
2282
2717
|
}
|
|
2283
2718
|
try {
|
|
2284
|
-
await
|
|
2719
|
+
await import_node_fs10.default.promises.unlink(entry.filename);
|
|
2285
2720
|
} catch (err) {
|
|
2286
2721
|
if (err.code !== "ENOENT") {
|
|
2287
2722
|
throw err;
|
|
@@ -2304,7 +2739,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2304
2739
|
if (!crlData) return;
|
|
2305
2740
|
for (const crlEntry of crlData.crls) {
|
|
2306
2741
|
try {
|
|
2307
|
-
await
|
|
2742
|
+
await import_node_fs10.default.promises.unlink(crlEntry.filename);
|
|
2308
2743
|
} catch (err) {
|
|
2309
2744
|
if (err.code !== "ENOENT") {
|
|
2310
2745
|
throw err;
|
|
@@ -2457,7 +2892,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2457
2892
|
if (status === "unknown") {
|
|
2458
2893
|
const pem = (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE");
|
|
2459
2894
|
const filename = import_node_path6.default.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
2460
|
-
await
|
|
2895
|
+
await import_node_fs10.default.promises.writeFile(filename, pem);
|
|
2461
2896
|
this.#thumbs.rejected.set(fingerprint, { certificate, filename });
|
|
2462
2897
|
status = "rejected";
|
|
2463
2898
|
}
|
|
@@ -2476,7 +2911,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2476
2911
|
const certificateDest = import_node_path6.default.join(destFolder, import_node_path6.default.basename(srcEntry.filename));
|
|
2477
2912
|
debugLog("#moveCertificate", fingerprint.substring(0, 10), "old name", srcEntry.filename);
|
|
2478
2913
|
debugLog("#moveCertificate", fingerprint.substring(0, 10), "new name", certificateDest);
|
|
2479
|
-
await
|
|
2914
|
+
await import_node_fs10.default.promises.rename(srcEntry.filename, certificateDest);
|
|
2480
2915
|
indexSrc.delete(fingerprint);
|
|
2481
2916
|
const indexDest = newStatus === "trusted" ? this.#thumbs.trusted : this.#thumbs.rejected;
|
|
2482
2917
|
indexDest.set(fingerprint, { certificate, filename: certificateDest });
|
|
@@ -2586,11 +3021,11 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2586
3021
|
persistent: false
|
|
2587
3022
|
};
|
|
2588
3023
|
const allCapturedHandles = [];
|
|
2589
|
-
const origWatch =
|
|
3024
|
+
const origWatch = import_node_fs10.default.watch;
|
|
2590
3025
|
let watcherReadyCount = 0;
|
|
2591
3026
|
const totalWatchers = 5;
|
|
2592
|
-
|
|
2593
|
-
const handle = origWatch.apply(
|
|
3027
|
+
import_node_fs10.default.watch = ((...args) => {
|
|
3028
|
+
const handle = origWatch.apply(import_node_fs10.default, args);
|
|
2594
3029
|
handle.setMaxListeners(handle.getMaxListeners() + 1);
|
|
2595
3030
|
handle.on("error", () => {
|
|
2596
3031
|
});
|
|
@@ -2606,7 +3041,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2606
3041
|
}
|
|
2607
3042
|
watcherReadyCount++;
|
|
2608
3043
|
if (watcherReadyCount >= totalWatchers) {
|
|
2609
|
-
|
|
3044
|
+
import_node_fs10.default.watch = origWatch;
|
|
2610
3045
|
}
|
|
2611
3046
|
};
|
|
2612
3047
|
return { w, capturedHandles: allCapturedHandles.slice(startIdx), unreffAll };
|
|
@@ -2630,12 +3065,12 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2630
3065
|
* file reads, preventing main-loop stalls with large folders.
|
|
2631
3066
|
*/
|
|
2632
3067
|
async #scanCertFolder(folder, index) {
|
|
2633
|
-
if (!
|
|
2634
|
-
const files = await
|
|
3068
|
+
if (!import_node_fs10.default.existsSync(folder)) return;
|
|
3069
|
+
const files = await import_node_fs10.default.promises.readdir(folder);
|
|
2635
3070
|
for (const file of files) {
|
|
2636
3071
|
const filename = import_node_path6.default.join(folder, file);
|
|
2637
3072
|
try {
|
|
2638
|
-
const stat = await
|
|
3073
|
+
const stat = await import_node_fs10.default.promises.stat(filename);
|
|
2639
3074
|
if (!stat.isFile()) continue;
|
|
2640
3075
|
const certificate = await (0, import_node_opcua_crypto5.readCertificateAsync)(filename);
|
|
2641
3076
|
const info = exploreCertificateCached(certificate);
|
|
@@ -2651,12 +3086,12 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2651
3086
|
* Scan a CRL folder and populate the in-memory CRL index.
|
|
2652
3087
|
*/
|
|
2653
3088
|
async #scanCrlFolder(folder, index) {
|
|
2654
|
-
if (!
|
|
2655
|
-
const files = await
|
|
3089
|
+
if (!import_node_fs10.default.existsSync(folder)) return;
|
|
3090
|
+
const files = await import_node_fs10.default.promises.readdir(folder);
|
|
2656
3091
|
for (const file of files) {
|
|
2657
3092
|
const filename = import_node_path6.default.join(folder, file);
|
|
2658
3093
|
try {
|
|
2659
|
-
const stat = await
|
|
3094
|
+
const stat = await import_node_fs10.default.promises.stat(filename);
|
|
2660
3095
|
if (!stat.isFile()) continue;
|
|
2661
3096
|
this.#onCrlFileAdded(index, filename);
|
|
2662
3097
|
} catch (err) {
|
|
@@ -2781,65 +3216,6 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
2781
3216
|
});
|
|
2782
3217
|
}
|
|
2783
3218
|
};
|
|
2784
|
-
|
|
2785
|
-
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
2786
|
-
var import_node_assert9 = __toESM(require("assert"));
|
|
2787
|
-
var import_node_fs10 = __toESM(require("fs"));
|
|
2788
|
-
var q2 = quote;
|
|
2789
|
-
var n3 = makePath;
|
|
2790
|
-
async function createPFX(options) {
|
|
2791
|
-
const { certificateFile, privateKeyFile, outputFile, passphrase = "", caCertificateFiles } = options;
|
|
2792
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);
|
|
2793
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);
|
|
2794
|
-
let cmd = `pkcs12 -export`;
|
|
2795
|
-
cmd += ` -in ${q2(n3(certificateFile))}`;
|
|
2796
|
-
cmd += ` -inkey ${q2(n3(privateKeyFile))}`;
|
|
2797
|
-
if (caCertificateFiles) {
|
|
2798
|
-
for (const caFile of caCertificateFiles) {
|
|
2799
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);
|
|
2800
|
-
cmd += ` -certfile ${q2(n3(caFile))}`;
|
|
2801
|
-
}
|
|
2802
|
-
}
|
|
2803
|
-
cmd += ` -out ${q2(n3(outputFile))}`;
|
|
2804
|
-
cmd += ` -passout pass:${passphrase}`;
|
|
2805
|
-
await execute_openssl(cmd, {});
|
|
2806
|
-
}
|
|
2807
|
-
async function extractCertificateFromPFX(options) {
|
|
2808
|
-
const { pfxFile, passphrase = "" } = options;
|
|
2809
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2810
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -clcerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
2811
|
-
return await execute_openssl(cmd, {});
|
|
2812
|
-
}
|
|
2813
|
-
async function extractPrivateKeyFromPFX(options) {
|
|
2814
|
-
const { pfxFile, passphrase = "" } = options;
|
|
2815
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2816
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -nocerts -nodes -passin pass:${passphrase}`;
|
|
2817
|
-
return await execute_openssl(cmd, {});
|
|
2818
|
-
}
|
|
2819
|
-
async function extractCACertificatesFromPFX(options) {
|
|
2820
|
-
const { pfxFile, passphrase = "" } = options;
|
|
2821
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2822
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -cacerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
2823
|
-
return await execute_openssl(cmd, {});
|
|
2824
|
-
}
|
|
2825
|
-
async function extractAllFromPFX(options) {
|
|
2826
|
-
const [certificate, privateKey, caCertificates] = await Promise.all([
|
|
2827
|
-
extractCertificateFromPFX(options),
|
|
2828
|
-
extractPrivateKeyFromPFX(options),
|
|
2829
|
-
extractCACertificatesFromPFX(options)
|
|
2830
|
-
]);
|
|
2831
|
-
return { certificate, privateKey, caCertificates };
|
|
2832
|
-
}
|
|
2833
|
-
async function convertPFXtoPEM(pfxFile, pemFile, passphrase = "") {
|
|
2834
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2835
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -out ${q2(n3(pemFile))} -nodes -passin pass:${passphrase}`;
|
|
2836
|
-
await execute_openssl(cmd, {});
|
|
2837
|
-
}
|
|
2838
|
-
async function dumpPFX(pfxFile, passphrase = "") {
|
|
2839
|
-
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2840
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -info -nodes -passin pass:${passphrase}`;
|
|
2841
|
-
return await execute_openssl(cmd, {});
|
|
2842
|
-
}
|
|
2843
3219
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2844
3220
|
0 && (module.exports = {
|
|
2845
3221
|
CertificateAuthority,
|