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.mjs
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
// packages/node-opcua-pki/lib/ca/certificate_authority.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import assert7 from "assert";
|
|
3
|
+
import fs7 from "fs";
|
|
4
4
|
import os3 from "os";
|
|
5
5
|
import path5 from "path";
|
|
6
6
|
import chalk5 from "chalk";
|
|
7
7
|
import {
|
|
8
|
+
CertificatePurpose,
|
|
9
|
+
certificateMatchesPrivateKey,
|
|
8
10
|
convertPEMtoDER,
|
|
9
11
|
exploreCertificate,
|
|
10
12
|
exploreCertificateSigningRequest,
|
|
11
13
|
generatePrivateKeyFile,
|
|
12
14
|
readCertificatePEM,
|
|
13
15
|
readCertificateSigningRequest,
|
|
16
|
+
readPrivateKey,
|
|
14
17
|
Subject as Subject2,
|
|
15
18
|
toPem
|
|
16
19
|
} from "node-opcua-crypto";
|
|
17
20
|
|
|
21
|
+
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
22
|
+
import assert4 from "assert";
|
|
23
|
+
import fs4 from "fs";
|
|
24
|
+
|
|
18
25
|
// packages/node-opcua-pki/lib/toolbox/common.ts
|
|
19
26
|
import assert from "assert";
|
|
20
27
|
function quote(str) {
|
|
@@ -91,30 +98,13 @@ function makePath(folderName, filename) {
|
|
|
91
98
|
return s;
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
// packages/node-opcua-pki/lib/toolbox/
|
|
95
|
-
import
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function displaySubtitle(str) {
|
|
104
|
-
if (!g_config.silent) {
|
|
105
|
-
warningLog("");
|
|
106
|
-
warningLog(` ${chalk2.yellowBright(str)}`);
|
|
107
|
-
warningLog(` ${chalk2.white(new Array(str.length + 1).join("-"))}`, "\n");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function display(str) {
|
|
111
|
-
if (!g_config.silent) {
|
|
112
|
-
warningLog(` ${str}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/index.ts
|
|
117
|
-
import exp from "constants";
|
|
101
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/execute_openssl.ts
|
|
102
|
+
import assert3 from "assert";
|
|
103
|
+
import child_process2 from "child_process";
|
|
104
|
+
import fs3 from "fs";
|
|
105
|
+
import os2 from "os";
|
|
106
|
+
import byline2 from "byline";
|
|
107
|
+
import chalk3 from "chalk";
|
|
118
108
|
|
|
119
109
|
// packages/node-opcua-pki/lib/toolbox/with_openssl/_env.ts
|
|
120
110
|
var exportedEnvVars = {};
|
|
@@ -155,22 +145,6 @@ function processAltNames(params) {
|
|
|
155
145
|
setEnv("ALTNAME", subjectAltNameString);
|
|
156
146
|
}
|
|
157
147
|
|
|
158
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
159
|
-
import assert5 from "assert";
|
|
160
|
-
import fs5 from "fs";
|
|
161
|
-
import path4 from "path";
|
|
162
|
-
|
|
163
|
-
// packages/node-opcua-pki/lib/misc/subject.ts
|
|
164
|
-
import { Subject } from "node-opcua-crypto";
|
|
165
|
-
|
|
166
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/execute_openssl.ts
|
|
167
|
-
import assert3 from "assert";
|
|
168
|
-
import child_process2 from "child_process";
|
|
169
|
-
import fs3 from "fs";
|
|
170
|
-
import os2 from "os";
|
|
171
|
-
import byline2 from "byline";
|
|
172
|
-
import chalk4 from "chalk";
|
|
173
|
-
|
|
174
148
|
// packages/node-opcua-pki/lib/toolbox/with_openssl/install_prerequisite.ts
|
|
175
149
|
import child_process from "child_process";
|
|
176
150
|
import fs2 from "fs";
|
|
@@ -178,7 +152,7 @@ import os from "os";
|
|
|
178
152
|
import path2 from "path";
|
|
179
153
|
import url from "url";
|
|
180
154
|
import byline from "byline";
|
|
181
|
-
import
|
|
155
|
+
import chalk2 from "chalk";
|
|
182
156
|
import ProgressBar from "progress";
|
|
183
157
|
import wget from "wget-improved-2";
|
|
184
158
|
import yauzl from "yauzl";
|
|
@@ -196,7 +170,7 @@ function makeOptions() {
|
|
|
196
170
|
proxyAuth: auth
|
|
197
171
|
}
|
|
198
172
|
};
|
|
199
|
-
warningLog(
|
|
173
|
+
warningLog(chalk2.green("- using proxy "), proxy);
|
|
200
174
|
warningLog(options);
|
|
201
175
|
return options;
|
|
202
176
|
}
|
|
@@ -225,7 +199,7 @@ async function execute(cmd, cwd) {
|
|
|
225
199
|
output += `${line}
|
|
226
200
|
`;
|
|
227
201
|
if (doDebug2) {
|
|
228
|
-
process.stdout.write(` stdout ${
|
|
202
|
+
process.stdout.write(` stdout ${chalk2.yellow(line)}
|
|
229
203
|
`);
|
|
230
204
|
}
|
|
231
205
|
});
|
|
@@ -248,8 +222,8 @@ async function getopensslExecPath() {
|
|
|
248
222
|
const exitCode = result1?.exitCode;
|
|
249
223
|
const output = result1?.output;
|
|
250
224
|
if (exitCode !== 0) {
|
|
251
|
-
warningLog(
|
|
252
|
-
warningLog(
|
|
225
|
+
warningLog(chalk2.yellow(" it seems that ") + chalk2.cyan("openssl") + chalk2.yellow(" is not installed on your computer "));
|
|
226
|
+
warningLog(chalk2.yellow("Please install it before running this programs"));
|
|
253
227
|
throw new Error("Cannot find openssl");
|
|
254
228
|
}
|
|
255
229
|
const opensslExecPath = output.replace(/\n\r/g, "").trim();
|
|
@@ -259,7 +233,7 @@ async function check_system_openssl_version() {
|
|
|
259
233
|
const opensslExecPath = await getopensslExecPath();
|
|
260
234
|
const q_opensslExecPath = quote2(opensslExecPath);
|
|
261
235
|
if (doDebug2) {
|
|
262
|
-
warningLog(` OpenSSL found in : ${
|
|
236
|
+
warningLog(` OpenSSL found in : ${chalk2.yellow(opensslExecPath)}`);
|
|
263
237
|
}
|
|
264
238
|
const result = await execute(`${q_opensslExecPath} version`);
|
|
265
239
|
const exitCode = result?.exitCode;
|
|
@@ -267,9 +241,9 @@ async function check_system_openssl_version() {
|
|
|
267
241
|
const version = output.trim();
|
|
268
242
|
const versionOK = exitCode === 0 && is_expected_openssl_version(version);
|
|
269
243
|
if (!versionOK) {
|
|
270
|
-
let message =
|
|
244
|
+
let message = chalk2.whiteBright("Warning !!!!!!!!!!!! ") + "\nyour version of openssl is " + version + ". It doesn't match the expected version";
|
|
271
245
|
if (process.platform === "darwin") {
|
|
272
|
-
message +=
|
|
246
|
+
message += chalk2.cyan("\nplease refer to :") + chalk2.yellow(" https://github.com/node-opcua/node-opcua/wiki/installing-node-opcua-or-node-red-on-MacOS");
|
|
273
247
|
}
|
|
274
248
|
console.log(message);
|
|
275
249
|
}
|
|
@@ -295,7 +269,7 @@ async function install_and_check_win32_openssl_version() {
|
|
|
295
269
|
const exists = fs2.existsSync(opensslExecPath2);
|
|
296
270
|
if (!exists) {
|
|
297
271
|
warningLog("checking presence of ", opensslExecPath2);
|
|
298
|
-
warningLog(
|
|
272
|
+
warningLog(chalk2.red(" cannot find file ") + opensslExecPath2);
|
|
299
273
|
return {
|
|
300
274
|
opensslOk: false,
|
|
301
275
|
version: `cannot find file ${opensslExecPath2}`
|
|
@@ -329,12 +303,12 @@ async function install_and_check_win32_openssl_version() {
|
|
|
329
303
|
async function download_openssl() {
|
|
330
304
|
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";
|
|
331
305
|
const outputFilename = path2.join(downloadFolder, path2.basename(url2));
|
|
332
|
-
warningLog(`downloading ${
|
|
306
|
+
warningLog(`downloading ${chalk2.yellow(url2)} to ${outputFilename}`);
|
|
333
307
|
if (fs2.existsSync(outputFilename)) {
|
|
334
308
|
return { downloadedFile: outputFilename };
|
|
335
309
|
}
|
|
336
310
|
const options = makeOptions();
|
|
337
|
-
const bar = new ProgressBar(
|
|
311
|
+
const bar = new ProgressBar(chalk2.cyan("[:bar]") + chalk2.cyan(" :percent ") + chalk2.white(":etas"), {
|
|
338
312
|
complete: "=",
|
|
339
313
|
incomplete: " ",
|
|
340
314
|
total: 100,
|
|
@@ -416,21 +390,21 @@ async function install_and_check_win32_openssl_version() {
|
|
|
416
390
|
}
|
|
417
391
|
const { opensslOk, version: _version } = await check_openssl_win32();
|
|
418
392
|
if (!opensslOk) {
|
|
419
|
-
warningLog(
|
|
393
|
+
warningLog(chalk2.yellow("openssl seems to be missing and need to be installed"));
|
|
420
394
|
const { downloadedFile } = await download_openssl();
|
|
421
395
|
if (doDebug2) {
|
|
422
|
-
warningLog("deflating ",
|
|
396
|
+
warningLog("deflating ", chalk2.yellow(downloadedFile));
|
|
423
397
|
}
|
|
424
398
|
await unzip_openssl(downloadedFile);
|
|
425
399
|
const opensslExists = !!fs2.existsSync(opensslExecPath);
|
|
426
400
|
if (doDebug2) {
|
|
427
|
-
warningLog("verifying ", opensslExists, opensslExists ?
|
|
401
|
+
warningLog("verifying ", opensslExists, opensslExists ? chalk2.green("OK ") : chalk2.red(" Error"), opensslExecPath);
|
|
428
402
|
}
|
|
429
403
|
const _opensslExecPath2 = await check_openssl_win32();
|
|
430
404
|
return opensslExecPath;
|
|
431
405
|
} else {
|
|
432
406
|
if (doDebug2) {
|
|
433
|
-
warningLog(
|
|
407
|
+
warningLog(chalk2.green("openssl is already installed and have the expected version."));
|
|
434
408
|
}
|
|
435
409
|
return opensslExecPath;
|
|
436
410
|
}
|
|
@@ -461,7 +435,7 @@ async function execute2(cmd, options) {
|
|
|
461
435
|
const from = new Error();
|
|
462
436
|
options.cwd = options.cwd || process.cwd();
|
|
463
437
|
if (!g_config.silent) {
|
|
464
|
-
warningLog(
|
|
438
|
+
warningLog(chalk3.cyan(" CWD "), options.cwd);
|
|
465
439
|
}
|
|
466
440
|
const outputs = [];
|
|
467
441
|
return await new Promise((resolve, reject) => {
|
|
@@ -475,10 +449,10 @@ async function execute2(cmd, options) {
|
|
|
475
449
|
if (err) {
|
|
476
450
|
if (!options.hideErrorMessage) {
|
|
477
451
|
const fence = "###########################################";
|
|
478
|
-
console.error(
|
|
479
|
-
console.error(
|
|
480
|
-
console.error(
|
|
481
|
-
console.error(
|
|
452
|
+
console.error(chalk3.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
453
|
+
console.error(chalk3.bgWhiteBright.redBright(`CWD = ${options.cwd}`));
|
|
454
|
+
console.error(chalk3.bgWhiteBright.redBright(err.message));
|
|
455
|
+
console.error(chalk3.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));
|
|
482
456
|
console.error(from.stack);
|
|
483
457
|
}
|
|
484
458
|
reject(new Error(err.message));
|
|
@@ -497,7 +471,7 @@ async function execute2(cmd, options) {
|
|
|
497
471
|
stream2.on("data", (line) => {
|
|
498
472
|
line = line.toString();
|
|
499
473
|
if (doDebug) {
|
|
500
|
-
process.stdout.write(`${
|
|
474
|
+
process.stdout.write(`${chalk3.white(" stdout ") + chalk3.whiteBright(line)}
|
|
501
475
|
`);
|
|
502
476
|
}
|
|
503
477
|
});
|
|
@@ -509,7 +483,7 @@ async function execute2(cmd, options) {
|
|
|
509
483
|
stream1.on("data", (line) => {
|
|
510
484
|
line = line.toString();
|
|
511
485
|
if (displayError) {
|
|
512
|
-
process.stdout.write(`${
|
|
486
|
+
process.stdout.write(`${chalk3.white(" stderr ") + chalk3.red(line)}
|
|
513
487
|
`);
|
|
514
488
|
}
|
|
515
489
|
});
|
|
@@ -553,17 +527,107 @@ async function execute_openssl(cmd, options) {
|
|
|
553
527
|
assert3(options.openssl_conf);
|
|
554
528
|
setEnv("OPENSSL_CONF", options.openssl_conf);
|
|
555
529
|
if (!g_config.silent) {
|
|
556
|
-
warningLog(
|
|
557
|
-
warningLog(
|
|
558
|
-
warningLog(
|
|
530
|
+
warningLog(chalk3.cyan(" OPENSSL_CONF"), process.env.OPENSSL_CONF);
|
|
531
|
+
warningLog(chalk3.cyan(" RANDFILE "), process.env.RANDFILE);
|
|
532
|
+
warningLog(chalk3.cyan(" CMD openssl "), chalk3.cyanBright(cmd));
|
|
559
533
|
}
|
|
560
534
|
await ensure_openssl_installed();
|
|
561
535
|
return await execute2(`${quote(opensslPath)} ${cmd}`, options);
|
|
562
536
|
}
|
|
563
537
|
|
|
538
|
+
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
539
|
+
var q = quote;
|
|
540
|
+
var n2 = makePath;
|
|
541
|
+
async function createPFX(options) {
|
|
542
|
+
const { certificateFile, privateKeyFile, outputFile, passphrase = "", caCertificateFiles } = options;
|
|
543
|
+
assert4(fs4.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);
|
|
544
|
+
assert4(fs4.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);
|
|
545
|
+
let cmd = `pkcs12 -export`;
|
|
546
|
+
cmd += ` -in ${q(n2(certificateFile))}`;
|
|
547
|
+
cmd += ` -inkey ${q(n2(privateKeyFile))}`;
|
|
548
|
+
if (caCertificateFiles) {
|
|
549
|
+
for (const caFile of caCertificateFiles) {
|
|
550
|
+
assert4(fs4.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);
|
|
551
|
+
cmd += ` -certfile ${q(n2(caFile))}`;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
cmd += ` -out ${q(n2(outputFile))}`;
|
|
555
|
+
cmd += ` -passout pass:${passphrase}`;
|
|
556
|
+
await execute_openssl(cmd, {});
|
|
557
|
+
}
|
|
558
|
+
async function extractCertificateFromPFX(options) {
|
|
559
|
+
const { pfxFile, passphrase = "" } = options;
|
|
560
|
+
assert4(fs4.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
561
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -clcerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
562
|
+
return await execute_openssl(cmd, {});
|
|
563
|
+
}
|
|
564
|
+
async function extractPrivateKeyFromPFX(options) {
|
|
565
|
+
const { pfxFile, passphrase = "" } = options;
|
|
566
|
+
assert4(fs4.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
567
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -nocerts -nodes -passin pass:${passphrase}`;
|
|
568
|
+
return await execute_openssl(cmd, {});
|
|
569
|
+
}
|
|
570
|
+
async function extractCACertificatesFromPFX(options) {
|
|
571
|
+
const { pfxFile, passphrase = "" } = options;
|
|
572
|
+
assert4(fs4.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
573
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -cacerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
574
|
+
return await execute_openssl(cmd, {});
|
|
575
|
+
}
|
|
576
|
+
async function extractAllFromPFX(options) {
|
|
577
|
+
const [certificate, privateKey, caCertificates] = await Promise.all([
|
|
578
|
+
extractCertificateFromPFX(options),
|
|
579
|
+
extractPrivateKeyFromPFX(options),
|
|
580
|
+
extractCACertificatesFromPFX(options)
|
|
581
|
+
]);
|
|
582
|
+
return { certificate, privateKey, caCertificates };
|
|
583
|
+
}
|
|
584
|
+
async function convertPFXtoPEM(pfxFile, pemFile, passphrase = "") {
|
|
585
|
+
assert4(fs4.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
586
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -out ${q(n2(pemFile))} -nodes -passin pass:${passphrase}`;
|
|
587
|
+
await execute_openssl(cmd, {});
|
|
588
|
+
}
|
|
589
|
+
async function dumpPFX(pfxFile, passphrase = "") {
|
|
590
|
+
assert4(fs4.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
591
|
+
const cmd = `pkcs12 -in ${q(n2(pfxFile))} -info -nodes -passin pass:${passphrase}`;
|
|
592
|
+
return await execute_openssl(cmd, {});
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// packages/node-opcua-pki/lib/toolbox/display.ts
|
|
596
|
+
import chalk4 from "chalk";
|
|
597
|
+
function displayTitle(str) {
|
|
598
|
+
if (!g_config.silent) {
|
|
599
|
+
warningLog("");
|
|
600
|
+
warningLog(chalk4.yellowBright(str));
|
|
601
|
+
warningLog(chalk4.yellow(new Array(str.length + 1).join("=")), "\n");
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function displaySubtitle(str) {
|
|
605
|
+
if (!g_config.silent) {
|
|
606
|
+
warningLog("");
|
|
607
|
+
warningLog(` ${chalk4.yellowBright(str)}`);
|
|
608
|
+
warningLog(` ${chalk4.white(new Array(str.length + 1).join("-"))}`, "\n");
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function display(str) {
|
|
612
|
+
if (!g_config.silent) {
|
|
613
|
+
warningLog(` ${str}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/index.ts
|
|
618
|
+
import exp from "constants";
|
|
619
|
+
|
|
620
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
621
|
+
import assert6 from "assert";
|
|
622
|
+
import fs6 from "fs";
|
|
623
|
+
import path4 from "path";
|
|
624
|
+
|
|
625
|
+
// packages/node-opcua-pki/lib/misc/subject.ts
|
|
626
|
+
import { Subject } from "node-opcua-crypto";
|
|
627
|
+
|
|
564
628
|
// packages/node-opcua-pki/lib/toolbox/with_openssl/toolbox.ts
|
|
565
|
-
import
|
|
566
|
-
import
|
|
629
|
+
import assert5 from "assert";
|
|
630
|
+
import fs5 from "fs";
|
|
567
631
|
import path3 from "path";
|
|
568
632
|
function openssl_require2DigitYearInDate() {
|
|
569
633
|
if (!g_config.opensslVersion) {
|
|
@@ -578,13 +642,13 @@ var _counter = 0;
|
|
|
578
642
|
function generateStaticConfig(configPath, options) {
|
|
579
643
|
const prePath = options?.cwd || "";
|
|
580
644
|
const originalFilename = !path3.isAbsolute(configPath) ? path3.join(prePath, configPath) : configPath;
|
|
581
|
-
let staticConfig =
|
|
645
|
+
let staticConfig = fs5.readFileSync(originalFilename, { encoding: "utf8" });
|
|
582
646
|
for (const envVar of getEnvironmentVarNames()) {
|
|
583
647
|
staticConfig = staticConfig.replace(new RegExp(envVar.pattern, "gi"), getEnv(envVar.key));
|
|
584
648
|
}
|
|
585
649
|
const staticConfigPath = `${configPath}.${process.pid}-${_counter++}.tmp`;
|
|
586
650
|
const temporaryConfigPath = !path3.isAbsolute(configPath) ? path3.join(prePath, staticConfigPath) : staticConfigPath;
|
|
587
|
-
|
|
651
|
+
fs5.writeFileSync(temporaryConfigPath, staticConfig);
|
|
588
652
|
if (options?.cwd) {
|
|
589
653
|
return path3.relative(options.cwd, temporaryConfigPath);
|
|
590
654
|
} else {
|
|
@@ -609,8 +673,38 @@ function x509Date(date) {
|
|
|
609
673
|
}
|
|
610
674
|
}
|
|
611
675
|
|
|
676
|
+
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
677
|
+
var q2 = quote;
|
|
678
|
+
var n3 = makePath;
|
|
679
|
+
async function createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFilename, params) {
|
|
680
|
+
assert6(params);
|
|
681
|
+
assert6(params.rootDir);
|
|
682
|
+
assert6(params.configFile);
|
|
683
|
+
assert6(params.privateKey);
|
|
684
|
+
assert6(typeof params.privateKey === "string");
|
|
685
|
+
assert6(fs6.existsSync(params.configFile), `config file must exist ${params.configFile}`);
|
|
686
|
+
assert6(fs6.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
|
|
687
|
+
assert6(fs6.existsSync(params.rootDir), "RootDir key must exist");
|
|
688
|
+
assert6(typeof certificateSigningRequestFilename === "string");
|
|
689
|
+
processAltNames(params);
|
|
690
|
+
const configFile = generateStaticConfig(params.configFile, { cwd: params.rootDir });
|
|
691
|
+
const options = { cwd: params.rootDir, openssl_conf: path4.relative(params.rootDir, configFile) };
|
|
692
|
+
const configOption = ` -config ${q2(n3(configFile))}`;
|
|
693
|
+
const subject = params.subject ? new Subject(params.subject).toString() : void 0;
|
|
694
|
+
const subjectOptions = subject ? ` -subj "${subject}"` : "";
|
|
695
|
+
displaySubtitle("- Creating a Certificate Signing Request with openssl");
|
|
696
|
+
await execute_openssl(
|
|
697
|
+
"req -new -sha256 -batch -text " + configOption + " -key " + q2(n3(params.privateKey)) + subjectOptions + " -out " + q2(n3(certificateSigningRequestFilename)),
|
|
698
|
+
options
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// packages/node-opcua-pki/lib/pki/templates/simple_config_template.cnf.ts
|
|
703
|
+
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';
|
|
704
|
+
var simple_config_template_cnf_default = config;
|
|
705
|
+
|
|
612
706
|
// packages/node-opcua-pki/lib/ca/templates/ca_config_template.cnf.ts
|
|
613
|
-
var
|
|
707
|
+
var config2 = `#.........DO NOT MODIFY BY HAND .........................
|
|
614
708
|
[ ca ]
|
|
615
709
|
default_ca = CA_default
|
|
616
710
|
[ CA_default ]
|
|
@@ -739,22 +833,23 @@ subjectAltName = $ENV::ALTNAME
|
|
|
739
833
|
#issuerAltName = issuer:copy
|
|
740
834
|
authorityKeyIdentifier = keyid:always,issuer:always
|
|
741
835
|
#authorityInfoAccess = @issuer_info`;
|
|
742
|
-
var ca_config_template_cnf_default =
|
|
836
|
+
var ca_config_template_cnf_default = config2;
|
|
743
837
|
|
|
744
838
|
// packages/node-opcua-pki/lib/ca/certificate_authority.ts
|
|
745
839
|
var defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
|
|
746
840
|
var configurationFileTemplate = ca_config_template_cnf_default;
|
|
747
|
-
var
|
|
841
|
+
var configurationFileSimpleTemplate = simple_config_template_cnf_default;
|
|
842
|
+
var config3 = {
|
|
748
843
|
certificateDir: "INVALID",
|
|
749
844
|
forceCA: false,
|
|
750
845
|
pkiDir: "INVALID"
|
|
751
846
|
};
|
|
752
|
-
var
|
|
753
|
-
var
|
|
847
|
+
var n4 = makePath;
|
|
848
|
+
var q3 = quote;
|
|
754
849
|
function octetStringToIpAddress(a) {
|
|
755
850
|
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();
|
|
756
851
|
}
|
|
757
|
-
|
|
852
|
+
assert7(octetStringToIpAddress("c07b9179") === "192.123.145.121");
|
|
758
853
|
async function construct_CertificateAuthority(certificateAuthority) {
|
|
759
854
|
const subject = certificateAuthority.subject;
|
|
760
855
|
const caRootDir = path5.resolve(certificateAuthority.rootDir);
|
|
@@ -769,49 +864,49 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
769
864
|
await make_folders();
|
|
770
865
|
async function construct_default_files() {
|
|
771
866
|
const serial = path5.join(caRootDir, "serial");
|
|
772
|
-
if (!
|
|
773
|
-
await
|
|
867
|
+
if (!fs7.existsSync(serial)) {
|
|
868
|
+
await fs7.promises.writeFile(serial, "1000");
|
|
774
869
|
}
|
|
775
870
|
const crlNumber = path5.join(caRootDir, "crlnumber");
|
|
776
|
-
if (!
|
|
777
|
-
await
|
|
871
|
+
if (!fs7.existsSync(crlNumber)) {
|
|
872
|
+
await fs7.promises.writeFile(crlNumber, "1000");
|
|
778
873
|
}
|
|
779
874
|
const indexFile = path5.join(caRootDir, "index.txt");
|
|
780
|
-
if (!
|
|
781
|
-
await
|
|
875
|
+
if (!fs7.existsSync(indexFile)) {
|
|
876
|
+
await fs7.promises.writeFile(indexFile, "");
|
|
782
877
|
}
|
|
783
878
|
}
|
|
784
879
|
await construct_default_files();
|
|
785
|
-
const caKeyExists =
|
|
786
|
-
const caCertExists =
|
|
787
|
-
if (caKeyExists && caCertExists && !
|
|
880
|
+
const caKeyExists = fs7.existsSync(path5.join(caRootDir, "private/cakey.pem"));
|
|
881
|
+
const caCertExists = fs7.existsSync(path5.join(caRootDir, "public/cacert.pem"));
|
|
882
|
+
if (caKeyExists && caCertExists && !config3.forceCA) {
|
|
788
883
|
debugLog("CA private key and certificate already exist ... skipping");
|
|
789
884
|
return;
|
|
790
885
|
}
|
|
791
886
|
if (caKeyExists && !caCertExists) {
|
|
792
887
|
debugLog("CA private key exists but cacert.pem is missing \u2014 rebuilding CA");
|
|
793
|
-
|
|
888
|
+
fs7.unlinkSync(path5.join(caRootDir, "private/cakey.pem"));
|
|
794
889
|
const staleCsr = path5.join(caRootDir, "private/cakey.csr");
|
|
795
|
-
if (
|
|
796
|
-
|
|
890
|
+
if (fs7.existsSync(staleCsr)) {
|
|
891
|
+
fs7.unlinkSync(staleCsr);
|
|
797
892
|
}
|
|
798
893
|
}
|
|
799
894
|
displayTitle("Create Certificate Authority (CA)");
|
|
800
895
|
const indexFileAttr = path5.join(caRootDir, "index.txt.attr");
|
|
801
|
-
if (!
|
|
802
|
-
await
|
|
896
|
+
if (!fs7.existsSync(indexFileAttr)) {
|
|
897
|
+
await fs7.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
803
898
|
}
|
|
804
899
|
const caConfigFile = certificateAuthority.configFile;
|
|
805
900
|
if (1) {
|
|
806
901
|
let data = configurationFileTemplate;
|
|
807
902
|
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
808
|
-
await
|
|
903
|
+
await fs7.promises.writeFile(caConfigFile, data);
|
|
809
904
|
}
|
|
810
905
|
const subjectOpt = ` -subj "${subject.toString()}" `;
|
|
811
906
|
processAltNames({});
|
|
812
907
|
const options = { cwd: caRootDir };
|
|
813
908
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
814
|
-
const configOption = ` -config ${
|
|
909
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
815
910
|
const keySize = certificateAuthority.keySize;
|
|
816
911
|
const privateKeyFilename = path5.join(caRootDir, "private/cakey.pem");
|
|
817
912
|
const csrFilename = path5.join(caRootDir, "private/cakey.csr");
|
|
@@ -819,14 +914,26 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
819
914
|
await generatePrivateKeyFile(privateKeyFilename, keySize);
|
|
820
915
|
displayTitle("Generate a certificate request for the CA key");
|
|
821
916
|
await execute_openssl(
|
|
822
|
-
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " +
|
|
823
|
-
options
|
|
824
|
-
);
|
|
825
|
-
displayTitle("Generate CA Certificate (self-signed)");
|
|
826
|
-
await execute_openssl(
|
|
827
|
-
" 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",
|
|
917
|
+
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " + q3(n4(privateKeyFilename)) + " -out " + q3(n4(csrFilename)) + " " + subjectOpt,
|
|
828
918
|
options
|
|
829
919
|
);
|
|
920
|
+
const issuerCA = certificateAuthority._issuerCA;
|
|
921
|
+
if (issuerCA) {
|
|
922
|
+
displayTitle("Generate CA Certificate (signed by issuer CA)");
|
|
923
|
+
const issuerCert = path5.resolve(issuerCA.caCertificate);
|
|
924
|
+
const issuerKey = path5.resolve(issuerCA.rootDir, "private/cakey.pem");
|
|
925
|
+
const issuerSerial = path5.resolve(issuerCA.rootDir, "serial");
|
|
926
|
+
await execute_openssl(
|
|
927
|
+
" 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",
|
|
928
|
+
options
|
|
929
|
+
);
|
|
930
|
+
} else {
|
|
931
|
+
displayTitle("Generate CA Certificate (self-signed)");
|
|
932
|
+
await execute_openssl(
|
|
933
|
+
" 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",
|
|
934
|
+
options
|
|
935
|
+
);
|
|
936
|
+
}
|
|
830
937
|
displaySubtitle("generate initial CRL (Certificate Revocation List)");
|
|
831
938
|
await regenerateCrl(certificateAuthority.revocationList, configOption, options);
|
|
832
939
|
displayTitle("Create Certificate Authority (CA) ---> DONE");
|
|
@@ -836,7 +943,7 @@ async function regenerateCrl(revocationList, configOption, options) {
|
|
|
836
943
|
await execute_openssl(`ca -gencrl ${configOption} -out crl/revocation_list.crl`, options);
|
|
837
944
|
await execute_openssl("crl -in crl/revocation_list.crl -out crl/revocation_list.der -outform der", options);
|
|
838
945
|
displaySubtitle("Display (Certificate Revocation List)");
|
|
839
|
-
await execute_openssl(`crl -in ${
|
|
946
|
+
await execute_openssl(`crl -in ${q3(n4(revocationList))} -text -noout`, options);
|
|
840
947
|
}
|
|
841
948
|
function parseOpenSSLDate(dateStr) {
|
|
842
949
|
const raw = dateStr?.split(",")[0] ?? "";
|
|
@@ -857,12 +964,15 @@ var CertificateAuthority = class {
|
|
|
857
964
|
location;
|
|
858
965
|
/** X.500 subject of the CA certificate. */
|
|
859
966
|
subject;
|
|
967
|
+
/** @internal Parent CA (undefined for root CAs). */
|
|
968
|
+
_issuerCA;
|
|
860
969
|
constructor(options) {
|
|
861
|
-
|
|
862
|
-
|
|
970
|
+
assert7(Object.prototype.hasOwnProperty.call(options, "location"));
|
|
971
|
+
assert7(Object.prototype.hasOwnProperty.call(options, "keySize"));
|
|
863
972
|
this.location = options.location;
|
|
864
973
|
this.keySize = options.keySize || 2048;
|
|
865
974
|
this.subject = new Subject2(options.subject || defaultSubject);
|
|
975
|
+
this._issuerCA = options.issuerCA;
|
|
866
976
|
}
|
|
867
977
|
/** Absolute path to the CA root directory (alias for {@link location}). */
|
|
868
978
|
get rootDir() {
|
|
@@ -876,6 +986,18 @@ var CertificateAuthority = class {
|
|
|
876
986
|
get caCertificate() {
|
|
877
987
|
return makePath(this.rootDir, "./public/cacert.pem");
|
|
878
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Path to the issuer certificate chain (`public/issuer_chain.pem`).
|
|
991
|
+
*
|
|
992
|
+
* This file is created by {@link installCACertificate} when the
|
|
993
|
+
* provided cert file contains additional issuer certificates
|
|
994
|
+
* (e.g. intermediate + root). It is appended to signed certs
|
|
995
|
+
* by {@link constructCertificateChain} to produce a full chain
|
|
996
|
+
* per OPC UA Part 6 §6.2.6.
|
|
997
|
+
*/
|
|
998
|
+
get issuerCertificateChain() {
|
|
999
|
+
return makePath(this.rootDir, "./public/issuer_chain.pem");
|
|
1000
|
+
}
|
|
879
1001
|
/**
|
|
880
1002
|
* Path to the current Certificate Revocation List in DER format.
|
|
881
1003
|
* (`crl/revocation_list.der`)
|
|
@@ -933,10 +1055,10 @@ var CertificateAuthority = class {
|
|
|
933
1055
|
*/
|
|
934
1056
|
getCRLDER() {
|
|
935
1057
|
const crlPath = this.revocationListDER;
|
|
936
|
-
if (!
|
|
1058
|
+
if (!fs7.existsSync(crlPath)) {
|
|
937
1059
|
return Buffer.alloc(0);
|
|
938
1060
|
}
|
|
939
|
-
return
|
|
1061
|
+
return fs7.readFileSync(crlPath);
|
|
940
1062
|
}
|
|
941
1063
|
/**
|
|
942
1064
|
* Return the current Certificate Revocation List as a
|
|
@@ -946,10 +1068,10 @@ var CertificateAuthority = class {
|
|
|
946
1068
|
*/
|
|
947
1069
|
getCRLPEM() {
|
|
948
1070
|
const crlPath = this.revocationList;
|
|
949
|
-
if (!
|
|
1071
|
+
if (!fs7.existsSync(crlPath)) {
|
|
950
1072
|
return "";
|
|
951
1073
|
}
|
|
952
|
-
const raw =
|
|
1074
|
+
const raw = fs7.readFileSync(crlPath, "utf-8");
|
|
953
1075
|
const beginMarker = "-----BEGIN X509 CRL-----";
|
|
954
1076
|
const idx = raw.indexOf(beginMarker);
|
|
955
1077
|
if (idx > 0) {
|
|
@@ -1002,7 +1124,7 @@ var CertificateAuthority = class {
|
|
|
1002
1124
|
getCertificateBySerial(serial) {
|
|
1003
1125
|
const upper = serial.toUpperCase();
|
|
1004
1126
|
const certFile = path5.join(this.rootDir, "certs", `${upper}.pem`);
|
|
1005
|
-
if (!
|
|
1127
|
+
if (!fs7.existsSync(certFile)) {
|
|
1006
1128
|
return void 0;
|
|
1007
1129
|
}
|
|
1008
1130
|
const pem = readCertificatePEM(certFile);
|
|
@@ -1031,10 +1153,10 @@ var CertificateAuthority = class {
|
|
|
1031
1153
|
*/
|
|
1032
1154
|
_parseIndexTxt() {
|
|
1033
1155
|
const indexPath = this.indexFile;
|
|
1034
|
-
if (!
|
|
1156
|
+
if (!fs7.existsSync(indexPath)) {
|
|
1035
1157
|
return [];
|
|
1036
1158
|
}
|
|
1037
|
-
const content =
|
|
1159
|
+
const content = fs7.readFileSync(indexPath, "utf-8");
|
|
1038
1160
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
1039
1161
|
const records = [];
|
|
1040
1162
|
for (const line of lines) {
|
|
@@ -1088,25 +1210,145 @@ var CertificateAuthority = class {
|
|
|
1088
1210
|
* internally so that callers can work with in-memory
|
|
1089
1211
|
* buffers only.
|
|
1090
1212
|
*
|
|
1213
|
+
* The CA can override fields from the CSR by passing
|
|
1214
|
+
* `options.dns`, `options.ip`, `options.applicationUri`,
|
|
1215
|
+
* `options.startDate`, or `options.subject`.
|
|
1216
|
+
*
|
|
1091
1217
|
* @param csrDer - the CSR as a DER-encoded buffer
|
|
1092
|
-
* @param options - signing options
|
|
1093
|
-
* @param options.validity - certificate validity in days
|
|
1094
|
-
* (default: 365)
|
|
1218
|
+
* @param options - signing options and CA overrides
|
|
1095
1219
|
* @returns the signed certificate as a DER-encoded buffer
|
|
1096
1220
|
*/
|
|
1097
1221
|
async signCertificateRequestFromDER(csrDer, options) {
|
|
1098
1222
|
const validity = options?.validity ?? 365;
|
|
1099
|
-
const tmpDir = await
|
|
1223
|
+
const tmpDir = await fs7.promises.mkdtemp(path5.join(os3.tmpdir(), "pki-sign-"));
|
|
1100
1224
|
try {
|
|
1101
1225
|
const csrFile = path5.join(tmpDir, "request.csr");
|
|
1102
1226
|
const certFile = path5.join(tmpDir, "certificate.pem");
|
|
1103
1227
|
const csrPem = toPem(csrDer, "CERTIFICATE REQUEST");
|
|
1104
|
-
await
|
|
1105
|
-
|
|
1228
|
+
await fs7.promises.writeFile(csrFile, csrPem, "utf-8");
|
|
1229
|
+
const signingParams = { validity };
|
|
1230
|
+
if (options?.startDate) signingParams.startDate = options.startDate;
|
|
1231
|
+
if (options?.dns) signingParams.dns = options.dns;
|
|
1232
|
+
if (options?.ip) signingParams.ip = options.ip;
|
|
1233
|
+
if (options?.applicationUri) signingParams.applicationUri = options.applicationUri;
|
|
1234
|
+
if (options?.subject) signingParams.subject = options.subject;
|
|
1235
|
+
await this.signCertificateRequest(certFile, csrFile, signingParams);
|
|
1106
1236
|
const certPem = readCertificatePEM(certFile);
|
|
1107
1237
|
return convertPEMtoDER(certPem);
|
|
1108
1238
|
} finally {
|
|
1109
|
-
await
|
|
1239
|
+
await fs7.promises.rm(tmpDir, {
|
|
1240
|
+
recursive: true,
|
|
1241
|
+
force: true
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Generate a new RSA key pair, create an internal CSR, sign it
|
|
1247
|
+
* with this CA, and return both the certificate and private key
|
|
1248
|
+
* as DER-encoded buffers.
|
|
1249
|
+
*
|
|
1250
|
+
* The private key is **never stored** by the CA — it exists only
|
|
1251
|
+
* in a temporary directory that is cleaned up after the operation.
|
|
1252
|
+
*
|
|
1253
|
+
* This is used by `StartNewKeyPairRequest` (OPC UA Part 12) for
|
|
1254
|
+
* constrained devices that cannot generate their own keys.
|
|
1255
|
+
*
|
|
1256
|
+
* @param options - key generation and certificate parameters
|
|
1257
|
+
* @returns `{ certificateDer, privateKey }` — certificate as DER,
|
|
1258
|
+
* private key as a branded `PrivateKey` buffer
|
|
1259
|
+
*/
|
|
1260
|
+
async generateKeyPairAndSignDER(options) {
|
|
1261
|
+
const keySize = options.keySize ?? 2048;
|
|
1262
|
+
const validity = options.validity ?? 365;
|
|
1263
|
+
const startDate = options.startDate ?? /* @__PURE__ */ new Date();
|
|
1264
|
+
const tmpDir = await fs7.promises.mkdtemp(path5.join(os3.tmpdir(), "pki-keygen-"));
|
|
1265
|
+
try {
|
|
1266
|
+
const privateKeyFile = path5.join(tmpDir, "private_key.pem");
|
|
1267
|
+
await generatePrivateKeyFile(privateKeyFile, keySize);
|
|
1268
|
+
const configFile = path5.join(tmpDir, "openssl.cnf");
|
|
1269
|
+
await fs7.promises.writeFile(configFile, configurationFileSimpleTemplate, "utf-8");
|
|
1270
|
+
const csrFile = path5.join(tmpDir, "request.csr");
|
|
1271
|
+
await createCertificateSigningRequestWithOpenSSL(csrFile, {
|
|
1272
|
+
rootDir: tmpDir,
|
|
1273
|
+
configFile,
|
|
1274
|
+
privateKey: privateKeyFile,
|
|
1275
|
+
applicationUri: options.applicationUri,
|
|
1276
|
+
subject: options.subject,
|
|
1277
|
+
dns: options.dns ?? [],
|
|
1278
|
+
ip: options.ip ?? [],
|
|
1279
|
+
purpose: CertificatePurpose.ForApplication
|
|
1280
|
+
});
|
|
1281
|
+
const certFile = path5.join(tmpDir, "certificate.pem");
|
|
1282
|
+
await this.signCertificateRequest(certFile, csrFile, {
|
|
1283
|
+
applicationUri: options.applicationUri,
|
|
1284
|
+
dns: options.dns,
|
|
1285
|
+
ip: options.ip,
|
|
1286
|
+
startDate,
|
|
1287
|
+
validity
|
|
1288
|
+
});
|
|
1289
|
+
const certPem = readCertificatePEM(certFile);
|
|
1290
|
+
const certificateDer = convertPEMtoDER(certPem);
|
|
1291
|
+
const privateKey = readPrivateKey(privateKeyFile);
|
|
1292
|
+
return { certificateDer, privateKey };
|
|
1293
|
+
} finally {
|
|
1294
|
+
await fs7.promises.rm(tmpDir, {
|
|
1295
|
+
recursive: true,
|
|
1296
|
+
force: true
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Generate a new RSA key pair, create an internal CSR, sign it
|
|
1302
|
+
* with this CA, and return the result as a PKCS#12 (PFX)
|
|
1303
|
+
* buffer bundling the certificate, private key, and CA chain.
|
|
1304
|
+
*
|
|
1305
|
+
* The private key is **never stored** by the CA — it exists only
|
|
1306
|
+
* in a temporary directory that is cleaned up after the operation.
|
|
1307
|
+
*
|
|
1308
|
+
* @param options - key generation, certificate, and PFX options
|
|
1309
|
+
* @returns the PFX as a `Buffer`
|
|
1310
|
+
*/
|
|
1311
|
+
async generateKeyPairAndSignPFX(options) {
|
|
1312
|
+
const keySize = options.keySize ?? 2048;
|
|
1313
|
+
const validity = options.validity ?? 365;
|
|
1314
|
+
const startDate = options.startDate ?? /* @__PURE__ */ new Date();
|
|
1315
|
+
const passphrase = options.passphrase ?? "";
|
|
1316
|
+
const tmpDir = await fs7.promises.mkdtemp(path5.join(os3.tmpdir(), "pki-keygen-pfx-"));
|
|
1317
|
+
try {
|
|
1318
|
+
const privateKeyFile = path5.join(tmpDir, "private_key.pem");
|
|
1319
|
+
await generatePrivateKeyFile(privateKeyFile, keySize);
|
|
1320
|
+
const configFile = path5.join(tmpDir, "openssl.cnf");
|
|
1321
|
+
await fs7.promises.writeFile(configFile, configurationFileSimpleTemplate, "utf-8");
|
|
1322
|
+
const csrFile = path5.join(tmpDir, "request.csr");
|
|
1323
|
+
await createCertificateSigningRequestWithOpenSSL(csrFile, {
|
|
1324
|
+
rootDir: tmpDir,
|
|
1325
|
+
configFile,
|
|
1326
|
+
privateKey: privateKeyFile,
|
|
1327
|
+
applicationUri: options.applicationUri,
|
|
1328
|
+
subject: options.subject,
|
|
1329
|
+
dns: options.dns ?? [],
|
|
1330
|
+
ip: options.ip ?? [],
|
|
1331
|
+
purpose: CertificatePurpose.ForApplication
|
|
1332
|
+
});
|
|
1333
|
+
const certFile = path5.join(tmpDir, "certificate.pem");
|
|
1334
|
+
await this.signCertificateRequest(certFile, csrFile, {
|
|
1335
|
+
applicationUri: options.applicationUri,
|
|
1336
|
+
dns: options.dns,
|
|
1337
|
+
ip: options.ip,
|
|
1338
|
+
startDate,
|
|
1339
|
+
validity
|
|
1340
|
+
});
|
|
1341
|
+
const pfxFile = path5.join(tmpDir, "bundle.pfx");
|
|
1342
|
+
await createPFX({
|
|
1343
|
+
certificateFile: certFile,
|
|
1344
|
+
privateKeyFile,
|
|
1345
|
+
outputFile: pfxFile,
|
|
1346
|
+
passphrase,
|
|
1347
|
+
caCertificateFiles: [this.caCertificate]
|
|
1348
|
+
});
|
|
1349
|
+
return await fs7.promises.readFile(pfxFile);
|
|
1350
|
+
} finally {
|
|
1351
|
+
await fs7.promises.rm(tmpDir, {
|
|
1110
1352
|
recursive: true,
|
|
1111
1353
|
force: true
|
|
1112
1354
|
});
|
|
@@ -1128,7 +1370,7 @@ var CertificateAuthority = class {
|
|
|
1128
1370
|
const info = exploreCertificate(certDer);
|
|
1129
1371
|
const serial = info.tbsCertificate.serialNumber.replace(/:/g, "").toUpperCase();
|
|
1130
1372
|
const storedCertFile = path5.join(this.rootDir, "certs", `${serial}.pem`);
|
|
1131
|
-
if (!
|
|
1373
|
+
if (!fs7.existsSync(storedCertFile)) {
|
|
1132
1374
|
throw new Error(`Cannot revoke: no stored certificate found for serial ${serial} at ${storedCertFile}`);
|
|
1133
1375
|
}
|
|
1134
1376
|
await this.revokeCertificate(storedCertFile, {
|
|
@@ -1143,6 +1385,204 @@ var CertificateAuthority = class {
|
|
|
1143
1385
|
async initialize() {
|
|
1144
1386
|
await construct_CertificateAuthority(this);
|
|
1145
1387
|
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Initialize the CA directory structure and generate the
|
|
1390
|
+
* private key + CSR **without signing**.
|
|
1391
|
+
*
|
|
1392
|
+
* Use this when the CA certificate will be signed by an
|
|
1393
|
+
* external (third-party) root CA. After receiving the signed
|
|
1394
|
+
* certificate, call {@link installCACertificate} to complete
|
|
1395
|
+
* the setup.
|
|
1396
|
+
*
|
|
1397
|
+
* **Idempotent / restart-safe:**
|
|
1398
|
+
* - If the CA certificate exists and is valid → `{ status: "ready" }`
|
|
1399
|
+
* - If the CA certificate has expired → `{ status: "expired", csrPath, expiryDate }`
|
|
1400
|
+
* (a new CSR is generated, preserving the existing private key)
|
|
1401
|
+
* - If key + CSR exist but no cert (restart before install) →
|
|
1402
|
+
* `{ status: "pending", csrPath }` without regenerating
|
|
1403
|
+
* - Otherwise → generates key + CSR → `{ status: "created", csrPath }`
|
|
1404
|
+
*
|
|
1405
|
+
* @returns an {@link InitializeCSRResult} describing the CA state
|
|
1406
|
+
*/
|
|
1407
|
+
async initializeCSR() {
|
|
1408
|
+
const caRootDir = path5.resolve(this.rootDir);
|
|
1409
|
+
mkdirRecursiveSync(caRootDir);
|
|
1410
|
+
for (const dir of ["private", "public", "certs", "crl", "conf"]) {
|
|
1411
|
+
mkdirRecursiveSync(path5.join(caRootDir, dir));
|
|
1412
|
+
}
|
|
1413
|
+
const caCertFile = this.caCertificate;
|
|
1414
|
+
const privateKeyFile = path5.join(caRootDir, "private/cakey.pem");
|
|
1415
|
+
const csrFile = path5.join(caRootDir, "private/cakey.csr");
|
|
1416
|
+
if (fs7.existsSync(caCertFile)) {
|
|
1417
|
+
const certDer = convertPEMtoDER(readCertificatePEM(caCertFile));
|
|
1418
|
+
const certInfo = exploreCertificate(certDer);
|
|
1419
|
+
const notAfter = certInfo.tbsCertificate.validity.notAfter;
|
|
1420
|
+
if (notAfter.getTime() < Date.now()) {
|
|
1421
|
+
debugLog("CA certificate has expired \u2014 generating renewal CSR");
|
|
1422
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
1423
|
+
return { status: "expired", csrPath: csrFile, expiryDate: notAfter };
|
|
1424
|
+
}
|
|
1425
|
+
debugLog("CA certificate already exists and is valid \u2014 ready");
|
|
1426
|
+
return { status: "ready" };
|
|
1427
|
+
}
|
|
1428
|
+
if (fs7.existsSync(privateKeyFile) && fs7.existsSync(csrFile)) {
|
|
1429
|
+
debugLog("CA key + CSR already exist \u2014 pending external signing");
|
|
1430
|
+
return { status: "pending", csrPath: csrFile };
|
|
1431
|
+
}
|
|
1432
|
+
const serial = path5.join(caRootDir, "serial");
|
|
1433
|
+
if (!fs7.existsSync(serial)) {
|
|
1434
|
+
await fs7.promises.writeFile(serial, "1000");
|
|
1435
|
+
}
|
|
1436
|
+
const crlNumber = path5.join(caRootDir, "crlnumber");
|
|
1437
|
+
if (!fs7.existsSync(crlNumber)) {
|
|
1438
|
+
await fs7.promises.writeFile(crlNumber, "1000");
|
|
1439
|
+
}
|
|
1440
|
+
const indexFile = path5.join(caRootDir, "index.txt");
|
|
1441
|
+
if (!fs7.existsSync(indexFile)) {
|
|
1442
|
+
await fs7.promises.writeFile(indexFile, "");
|
|
1443
|
+
}
|
|
1444
|
+
const indexFileAttr = path5.join(caRootDir, "index.txt.attr");
|
|
1445
|
+
if (!fs7.existsSync(indexFileAttr)) {
|
|
1446
|
+
await fs7.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
1447
|
+
}
|
|
1448
|
+
const caConfigFile = this.configFile;
|
|
1449
|
+
let data = configurationFileTemplate;
|
|
1450
|
+
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
1451
|
+
await fs7.promises.writeFile(caConfigFile, data);
|
|
1452
|
+
if (!fs7.existsSync(privateKeyFile)) {
|
|
1453
|
+
await generatePrivateKeyFile(privateKeyFile, this.keySize);
|
|
1454
|
+
}
|
|
1455
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
1456
|
+
return { status: "created", csrPath: csrFile };
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Check whether the CA certificate needs renewal and, if so,
|
|
1460
|
+
* generate a new CSR for re-signing by the external root CA.
|
|
1461
|
+
*
|
|
1462
|
+
* Use this while the CA is running to detect upcoming expiry
|
|
1463
|
+
* **before** it actually expires. The existing private key is
|
|
1464
|
+
* preserved so previously issued certs remain valid.
|
|
1465
|
+
*
|
|
1466
|
+
* @param thresholdDays - number of days before expiry at which
|
|
1467
|
+
* to trigger renewal (default: 30)
|
|
1468
|
+
* @returns an {@link InitializeCSRResult} — `"expired"` if
|
|
1469
|
+
* renewal is needed, `"ready"` if the cert is still valid
|
|
1470
|
+
*/
|
|
1471
|
+
async renewCSR(thresholdDays = 30) {
|
|
1472
|
+
const caRootDir = path5.resolve(this.rootDir);
|
|
1473
|
+
const caCertFile = this.caCertificate;
|
|
1474
|
+
const privateKeyFile = path5.join(caRootDir, "private/cakey.pem");
|
|
1475
|
+
const csrFile = path5.join(caRootDir, "private/cakey.csr");
|
|
1476
|
+
if (!fs7.existsSync(caCertFile)) {
|
|
1477
|
+
return this.initializeCSR();
|
|
1478
|
+
}
|
|
1479
|
+
const certDer = convertPEMtoDER(readCertificatePEM(caCertFile));
|
|
1480
|
+
const certInfo = exploreCertificate(certDer);
|
|
1481
|
+
const notAfter = certInfo.tbsCertificate.validity.notAfter;
|
|
1482
|
+
const thresholdMs = thresholdDays * 24 * 60 * 60 * 1e3;
|
|
1483
|
+
if (notAfter.getTime() - Date.now() < thresholdMs) {
|
|
1484
|
+
debugLog(`CA certificate expires within ${thresholdDays} days \u2014 generating renewal CSR`);
|
|
1485
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
1486
|
+
return { status: "expired", csrPath: csrFile, expiryDate: notAfter };
|
|
1487
|
+
}
|
|
1488
|
+
return { status: "ready" };
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Generate a CSR using the existing private key.
|
|
1492
|
+
* @internal
|
|
1493
|
+
*/
|
|
1494
|
+
async _generateCSR(caRootDir, privateKeyFile, csrFile) {
|
|
1495
|
+
const subjectOpt = ` -subj "${this.subject.toString()}" `;
|
|
1496
|
+
processAltNames({});
|
|
1497
|
+
const options = { cwd: caRootDir };
|
|
1498
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1499
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
1500
|
+
await execute_openssl(
|
|
1501
|
+
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " + q3(n4(privateKeyFile)) + " -out " + q3(n4(csrFile)) + " " + subjectOpt,
|
|
1502
|
+
options
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Install an externally-signed CA certificate and generate
|
|
1507
|
+
* the initial CRL.
|
|
1508
|
+
*
|
|
1509
|
+
* Call this after {@link initializeCSR} once the external
|
|
1510
|
+
* root CA has signed the CSR.
|
|
1511
|
+
*
|
|
1512
|
+
* **Safety checks:**
|
|
1513
|
+
* - Verifies that the certificate's public key matches the
|
|
1514
|
+
* CA private key before installing.
|
|
1515
|
+
*
|
|
1516
|
+
* @param signedCertFile - path to the PEM-encoded signed
|
|
1517
|
+
* CA certificate (issued by the external root CA)
|
|
1518
|
+
* @returns an {@link InstallCACertificateResult} with
|
|
1519
|
+
* `status: "success"` or `status: "error"` and a `reason`
|
|
1520
|
+
*/
|
|
1521
|
+
async installCACertificate(signedCertFile) {
|
|
1522
|
+
const caRootDir = path5.resolve(this.rootDir);
|
|
1523
|
+
const caCertFile = this.caCertificate;
|
|
1524
|
+
const privateKeyFile = path5.join(caRootDir, "private/cakey.pem");
|
|
1525
|
+
const fullPem = await fs7.promises.readFile(signedCertFile, "utf8");
|
|
1526
|
+
const pemBlocks = fullPem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
1527
|
+
if (!pemBlocks || pemBlocks.length === 0) {
|
|
1528
|
+
return {
|
|
1529
|
+
status: "error",
|
|
1530
|
+
reason: "no_certificate_found",
|
|
1531
|
+
message: "The provided file does not contain any PEM-encoded certificate."
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
const certDer = convertPEMtoDER(pemBlocks[0]);
|
|
1535
|
+
const privateKey = readPrivateKey(privateKeyFile);
|
|
1536
|
+
if (!certificateMatchesPrivateKey(certDer, privateKey)) {
|
|
1537
|
+
return {
|
|
1538
|
+
status: "error",
|
|
1539
|
+
reason: "certificate_key_mismatch",
|
|
1540
|
+
message: "The provided certificate does not match the CA private key. Ensure the certificate was signed from the CSR generated by initializeCSR()."
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
await fs7.promises.writeFile(caCertFile, `${pemBlocks[0]}
|
|
1544
|
+
`);
|
|
1545
|
+
const issuerChainFile = this.issuerCertificateChain;
|
|
1546
|
+
if (pemBlocks.length > 1) {
|
|
1547
|
+
const issuerPem = `${pemBlocks.slice(1).join("\n")}
|
|
1548
|
+
`;
|
|
1549
|
+
await fs7.promises.writeFile(issuerChainFile, issuerPem);
|
|
1550
|
+
debugLog(`Stored ${pemBlocks.length - 1} issuer certificate(s) in issuer_chain.pem`);
|
|
1551
|
+
} else {
|
|
1552
|
+
if (fs7.existsSync(issuerChainFile)) {
|
|
1553
|
+
await fs7.promises.unlink(issuerChainFile);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
const options = { cwd: caRootDir };
|
|
1557
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1558
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
1559
|
+
await regenerateCrl(this.revocationList, configOption, options);
|
|
1560
|
+
return { status: "success" };
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Sign a CSR with CA extensions (`v3_ca`), producing a
|
|
1564
|
+
* subordinate CA certificate.
|
|
1565
|
+
*
|
|
1566
|
+
* Unlike {@link signCertificateRequest} which signs with
|
|
1567
|
+
* end-entity extensions (SANs, etc.), this method signs
|
|
1568
|
+
* with `basicConstraints = CA:TRUE` and `keyUsage =
|
|
1569
|
+
* keyCertSign, cRLSign`.
|
|
1570
|
+
*
|
|
1571
|
+
* @param certFile - output path for the signed CA cert (PEM)
|
|
1572
|
+
* @param csrFile - path to the subordinate CA's CSR
|
|
1573
|
+
* @param params - signing parameters
|
|
1574
|
+
*/
|
|
1575
|
+
async signCACertificateRequest(certFile, csrFile, params) {
|
|
1576
|
+
const caRootDir = path5.resolve(this.rootDir);
|
|
1577
|
+
const options = { cwd: caRootDir };
|
|
1578
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
1579
|
+
const validity = params.validity ?? 3650;
|
|
1580
|
+
await execute_openssl(
|
|
1581
|
+
` 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(path5.join(caRootDir, "private/cakey.pem"))) + " -CAserial " + q3(n4(path5.join(caRootDir, "serial"))) + " -out " + q3(n4(certFile)),
|
|
1582
|
+
options
|
|
1583
|
+
);
|
|
1584
|
+
await this.constructCertificateChain(certFile);
|
|
1585
|
+
}
|
|
1146
1586
|
/**
|
|
1147
1587
|
* Rebuild the combined CA certificate + CRL file.
|
|
1148
1588
|
*
|
|
@@ -1152,13 +1592,13 @@ var CertificateAuthority = class {
|
|
|
1152
1592
|
*/
|
|
1153
1593
|
async constructCACertificateWithCRL() {
|
|
1154
1594
|
const cacertWithCRL = this.caCertificateWithCrl;
|
|
1155
|
-
if (
|
|
1156
|
-
await
|
|
1595
|
+
if (fs7.existsSync(this.revocationList)) {
|
|
1596
|
+
await fs7.promises.writeFile(
|
|
1157
1597
|
cacertWithCRL,
|
|
1158
|
-
|
|
1598
|
+
fs7.readFileSync(this.caCertificate, "utf8") + fs7.readFileSync(this.revocationList, "utf8")
|
|
1159
1599
|
);
|
|
1160
1600
|
} else {
|
|
1161
|
-
await
|
|
1601
|
+
await fs7.promises.writeFile(cacertWithCRL, fs7.readFileSync(this.caCertificate));
|
|
1162
1602
|
}
|
|
1163
1603
|
}
|
|
1164
1604
|
/**
|
|
@@ -1168,14 +1608,15 @@ var CertificateAuthority = class {
|
|
|
1168
1608
|
* @param certificate - path to the certificate file to extend
|
|
1169
1609
|
*/
|
|
1170
1610
|
async constructCertificateChain(certificate) {
|
|
1171
|
-
|
|
1172
|
-
|
|
1611
|
+
assert7(fs7.existsSync(certificate));
|
|
1612
|
+
assert7(fs7.existsSync(this.caCertificate));
|
|
1173
1613
|
debugLog(chalk5.yellow(" certificate file :"), chalk5.cyan(certificate));
|
|
1174
|
-
await
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1614
|
+
let chain = await fs7.promises.readFile(certificate, "utf8");
|
|
1615
|
+
chain += await fs7.promises.readFile(this.caCertificate, "utf8");
|
|
1616
|
+
if (fs7.existsSync(this.issuerCertificateChain)) {
|
|
1617
|
+
chain += await fs7.promises.readFile(this.issuerCertificateChain, "utf8");
|
|
1618
|
+
}
|
|
1619
|
+
await fs7.promises.writeFile(certificate, chain);
|
|
1179
1620
|
}
|
|
1180
1621
|
/**
|
|
1181
1622
|
* Create a self-signed certificate using OpenSSL.
|
|
@@ -1185,8 +1626,8 @@ var CertificateAuthority = class {
|
|
|
1185
1626
|
* @param params - certificate parameters (subject, validity, SANs)
|
|
1186
1627
|
*/
|
|
1187
1628
|
async createSelfSignedCertificate(certificateFile, privateKey, params) {
|
|
1188
|
-
|
|
1189
|
-
|
|
1629
|
+
assert7(typeof privateKey === "string");
|
|
1630
|
+
assert7(fs7.existsSync(privateKey));
|
|
1190
1631
|
if (!certificateFileExist(certificateFile)) {
|
|
1191
1632
|
return;
|
|
1192
1633
|
}
|
|
@@ -1194,7 +1635,7 @@ var CertificateAuthority = class {
|
|
|
1194
1635
|
adjustApplicationUri(params);
|
|
1195
1636
|
processAltNames(params);
|
|
1196
1637
|
const csrFile = `${certificateFile}_csr`;
|
|
1197
|
-
|
|
1638
|
+
assert7(csrFile);
|
|
1198
1639
|
const configFile = generateStaticConfig(this.configFile, { cwd: this.rootDir });
|
|
1199
1640
|
const options = {
|
|
1200
1641
|
cwd: this.rootDir,
|
|
@@ -1205,19 +1646,19 @@ var CertificateAuthority = class {
|
|
|
1205
1646
|
const subjectOptions = subject && subject.length > 1 ? ` -subj ${subject} ` : "";
|
|
1206
1647
|
displaySubtitle("- the certificate signing request");
|
|
1207
1648
|
await execute_openssl(
|
|
1208
|
-
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " +
|
|
1649
|
+
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " + q3(n4(privateKey)) + " -out " + q3(n4(csrFile)),
|
|
1209
1650
|
options
|
|
1210
1651
|
);
|
|
1211
1652
|
displaySubtitle("- creating the self-signed certificate");
|
|
1212
1653
|
await execute_openssl(
|
|
1213
|
-
"ca -selfsign -keyfile " +
|
|
1654
|
+
"ca -selfsign -keyfile " + q3(n4(privateKey)) + " -startdate " + x509Date(params.startDate) + " -enddate " + x509Date(params.endDate) + " -batch -out " + q3(n4(certificateFile)) + " -in " + q3(n4(csrFile)),
|
|
1214
1655
|
options
|
|
1215
1656
|
);
|
|
1216
1657
|
displaySubtitle("- dump the certificate for a check");
|
|
1217
|
-
await execute_openssl(`x509 -in ${
|
|
1658
|
+
await execute_openssl(`x509 -in ${q3(n4(certificateFile))} -dates -fingerprint -purpose -noout`, {});
|
|
1218
1659
|
displaySubtitle("- verify self-signed certificate");
|
|
1219
|
-
await execute_openssl_no_failure(`verify -verbose -CAfile ${
|
|
1220
|
-
await
|
|
1660
|
+
await execute_openssl_no_failure(`verify -verbose -CAfile ${q3(n4(certificateFile))} ${q3(n4(certificateFile))}`, options);
|
|
1661
|
+
await fs7.promises.unlink(csrFile);
|
|
1221
1662
|
}
|
|
1222
1663
|
/**
|
|
1223
1664
|
* Revoke a certificate and regenerate the CRL.
|
|
@@ -1246,22 +1687,22 @@ var CertificateAuthority = class {
|
|
|
1246
1687
|
setEnv("ALTNAME", "");
|
|
1247
1688
|
const randomFile = path5.join(this.rootDir, "random.rnd");
|
|
1248
1689
|
setEnv("RANDFILE", randomFile);
|
|
1249
|
-
const configOption = ` -config ${
|
|
1690
|
+
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
1250
1691
|
const reason = params.reason || "keyCompromise";
|
|
1251
|
-
|
|
1692
|
+
assert7(crlReasons.indexOf(reason) >= 0);
|
|
1252
1693
|
displayTitle(`Revoking certificate ${certificate}`);
|
|
1253
1694
|
displaySubtitle("Revoke certificate");
|
|
1254
|
-
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${
|
|
1695
|
+
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${q3(certificate)} -crl_reason ${reason}`, options);
|
|
1255
1696
|
await regenerateCrl(this.revocationList, configOption, options);
|
|
1256
1697
|
displaySubtitle("Verify that certificate is revoked");
|
|
1257
1698
|
await execute_openssl_no_failure(
|
|
1258
|
-
"verify -verbose -CRLfile " +
|
|
1699
|
+
"verify -verbose -CRLfile " + q3(n4(this.revocationList)) + " -CAfile " + q3(n4(this.caCertificate)) + " -crl_check " + q3(n4(certificate)),
|
|
1259
1700
|
options
|
|
1260
1701
|
);
|
|
1261
1702
|
displaySubtitle("Produce CRL in DER form ");
|
|
1262
|
-
await execute_openssl(`crl -in ${
|
|
1703
|
+
await execute_openssl(`crl -in ${q3(n4(this.revocationList))} -out crl/revocation_list.der -outform der`, options);
|
|
1263
1704
|
displaySubtitle("Produce CRL in PEM form ");
|
|
1264
|
-
await execute_openssl(`crl -in ${
|
|
1705
|
+
await execute_openssl(`crl -in ${q3(n4(this.revocationList))} -out crl/revocation_list.pem -outform pem -text `, options);
|
|
1265
1706
|
}
|
|
1266
1707
|
/**
|
|
1267
1708
|
* Sign a Certificate Signing Request (CSR) with this CA.
|
|
@@ -1277,7 +1718,7 @@ var CertificateAuthority = class {
|
|
|
1277
1718
|
*/
|
|
1278
1719
|
async signCertificateRequest(certificate, certificateSigningRequestFilename, params1) {
|
|
1279
1720
|
await ensure_openssl_installed();
|
|
1280
|
-
|
|
1721
|
+
assert7(fs7.existsSync(certificateSigningRequestFilename));
|
|
1281
1722
|
if (!certificateFileExist(certificate)) {
|
|
1282
1723
|
return "";
|
|
1283
1724
|
}
|
|
@@ -1304,11 +1745,11 @@ var CertificateAuthority = class {
|
|
|
1304
1745
|
displaySubtitle("- then we ask the authority to sign the certificate signing request");
|
|
1305
1746
|
const configOption = ` -config ${configFile}`;
|
|
1306
1747
|
await execute_openssl(
|
|
1307
|
-
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " +
|
|
1748
|
+
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " + q3(n4(certificate)) + " -in " + q3(n4(certificateSigningRequestFilename)),
|
|
1308
1749
|
options
|
|
1309
1750
|
);
|
|
1310
1751
|
displaySubtitle("- dump the certificate for a check");
|
|
1311
|
-
await execute_openssl(`x509 -in ${
|
|
1752
|
+
await execute_openssl(`x509 -in ${q3(n4(certificate))} -dates -fingerprint -purpose -noout`, options);
|
|
1312
1753
|
displaySubtitle("- construct CA certificate with CRL");
|
|
1313
1754
|
await this.constructCACertificateWithCRL();
|
|
1314
1755
|
displaySubtitle("- construct certificate chain");
|
|
@@ -1331,7 +1772,7 @@ var CertificateAuthority = class {
|
|
|
1331
1772
|
const _configOption = ` -config ${configFile}`;
|
|
1332
1773
|
_configOption;
|
|
1333
1774
|
await execute_openssl_no_failure(
|
|
1334
|
-
`verify -verbose -CAfile ${
|
|
1775
|
+
`verify -verbose -CAfile ${q3(n4(this.caCertificateWithCrl))} ${q3(n4(certificate))}`,
|
|
1335
1776
|
options
|
|
1336
1777
|
);
|
|
1337
1778
|
}
|
|
@@ -1340,7 +1781,7 @@ var CertificateAuthority = class {
|
|
|
1340
1781
|
|
|
1341
1782
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1342
1783
|
import { EventEmitter } from "events";
|
|
1343
|
-
import
|
|
1784
|
+
import fs10 from "fs";
|
|
1344
1785
|
import path6 from "path";
|
|
1345
1786
|
import { drainPendingLocks, withLock } from "@ster5/global-mutex";
|
|
1346
1787
|
import chalk6 from "chalk";
|
|
@@ -1361,21 +1802,21 @@ import {
|
|
|
1361
1802
|
} from "node-opcua-crypto";
|
|
1362
1803
|
|
|
1363
1804
|
// packages/node-opcua-pki/lib/toolbox/without_openssl/create_certificate_signing_request.ts
|
|
1364
|
-
import
|
|
1365
|
-
import
|
|
1805
|
+
import assert8 from "assert";
|
|
1806
|
+
import fs8 from "fs";
|
|
1366
1807
|
import { createCertificateSigningRequest, pemToPrivateKey, Subject as Subject3 } from "node-opcua-crypto";
|
|
1367
1808
|
async function createCertificateSigningRequestAsync(certificateSigningRequestFilename, params) {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1809
|
+
assert8(params);
|
|
1810
|
+
assert8(params.rootDir);
|
|
1811
|
+
assert8(params.configFile);
|
|
1812
|
+
assert8(params.privateKey);
|
|
1813
|
+
assert8(typeof params.privateKey === "string");
|
|
1814
|
+
assert8(fs8.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
|
|
1815
|
+
assert8(fs8.existsSync(params.rootDir), "RootDir key must exist");
|
|
1816
|
+
assert8(typeof certificateSigningRequestFilename === "string");
|
|
1376
1817
|
const subject = params.subject ? new Subject3(params.subject).toString() : void 0;
|
|
1377
1818
|
displaySubtitle("- Creating a Certificate Signing Request with subtile");
|
|
1378
|
-
const privateKeyPem = await
|
|
1819
|
+
const privateKeyPem = await fs8.promises.readFile(params.privateKey, "utf-8");
|
|
1379
1820
|
const privateKey = await pemToPrivateKey(privateKeyPem);
|
|
1380
1821
|
const { csr } = await createCertificateSigningRequest({
|
|
1381
1822
|
privateKey,
|
|
@@ -1385,38 +1826,38 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
1385
1826
|
applicationUri: params.applicationUri,
|
|
1386
1827
|
purpose: params.purpose
|
|
1387
1828
|
});
|
|
1388
|
-
await
|
|
1829
|
+
await fs8.promises.writeFile(certificateSigningRequestFilename, csr, "utf-8");
|
|
1389
1830
|
display(`- privateKey ${params.privateKey}`);
|
|
1390
1831
|
display(`- certificateSigningRequestFilename ${certificateSigningRequestFilename}`);
|
|
1391
1832
|
}
|
|
1392
1833
|
|
|
1393
1834
|
// packages/node-opcua-pki/lib/toolbox/without_openssl/create_self_signed_certificate.ts
|
|
1394
|
-
import
|
|
1395
|
-
import
|
|
1835
|
+
import assert9 from "assert";
|
|
1836
|
+
import fs9 from "fs";
|
|
1396
1837
|
import {
|
|
1397
|
-
CertificatePurpose,
|
|
1838
|
+
CertificatePurpose as CertificatePurpose2,
|
|
1398
1839
|
createSelfSignedCertificate as createSelfSignedCertificate1,
|
|
1399
1840
|
pemToPrivateKey as pemToPrivateKey2,
|
|
1400
1841
|
Subject as Subject4
|
|
1401
1842
|
} from "node-opcua-crypto";
|
|
1402
1843
|
async function createSelfSignedCertificateAsync(certificate, params) {
|
|
1403
|
-
params.purpose = params.purpose ||
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1844
|
+
params.purpose = params.purpose || CertificatePurpose2.ForApplication;
|
|
1845
|
+
assert9(params.purpose, "Please provide a Certificate Purpose");
|
|
1846
|
+
assert9(fs9.existsSync(params.configFile));
|
|
1847
|
+
assert9(fs9.existsSync(params.rootDir));
|
|
1848
|
+
assert9(fs9.existsSync(params.privateKey));
|
|
1408
1849
|
if (!params.subject) {
|
|
1409
1850
|
throw Error("Missing subject");
|
|
1410
1851
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1852
|
+
assert9(typeof params.applicationUri === "string");
|
|
1853
|
+
assert9(Array.isArray(params.dns));
|
|
1413
1854
|
adjustDate(params);
|
|
1414
|
-
|
|
1855
|
+
assert9(Object.prototype.hasOwnProperty.call(params, "validity"));
|
|
1415
1856
|
let subject = new Subject4(params.subject);
|
|
1416
1857
|
subject = subject.toString();
|
|
1417
1858
|
const purpose = params.purpose;
|
|
1418
1859
|
displayTitle("Generate a certificate request");
|
|
1419
|
-
const privateKeyPem = await
|
|
1860
|
+
const privateKeyPem = await fs9.promises.readFile(params.privateKey, "utf-8");
|
|
1420
1861
|
const privateKey = await pemToPrivateKey2(privateKeyPem);
|
|
1421
1862
|
const { cert } = await createSelfSignedCertificate1({
|
|
1422
1863
|
privateKey,
|
|
@@ -1429,19 +1870,15 @@ async function createSelfSignedCertificateAsync(certificate, params) {
|
|
|
1429
1870
|
applicationUri: params.applicationUri,
|
|
1430
1871
|
purpose
|
|
1431
1872
|
});
|
|
1432
|
-
await
|
|
1873
|
+
await fs9.promises.writeFile(certificate, cert, "utf-8");
|
|
1433
1874
|
}
|
|
1434
1875
|
async function createSelfSignedCertificate(certificate, params) {
|
|
1435
1876
|
await createSelfSignedCertificateAsync(certificate, params);
|
|
1436
1877
|
}
|
|
1437
1878
|
|
|
1438
|
-
// packages/node-opcua-pki/lib/pki/templates/simple_config_template.cnf.ts
|
|
1439
|
-
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';
|
|
1440
|
-
var simple_config_template_cnf_default = config3;
|
|
1441
|
-
|
|
1442
1879
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1443
|
-
var
|
|
1444
|
-
var fsWriteFile =
|
|
1880
|
+
var configurationFileSimpleTemplate2 = simple_config_template_cnf_default;
|
|
1881
|
+
var fsWriteFile = fs10.promises.writeFile;
|
|
1445
1882
|
function getOrComputeInfo(entry) {
|
|
1446
1883
|
if (!entry.info) {
|
|
1447
1884
|
entry.info = exploreCertificateCached(entry.certificate);
|
|
@@ -1668,7 +2105,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1668
2105
|
this.#location = makePath(options.location, "");
|
|
1669
2106
|
this.keySize = options.keySize;
|
|
1670
2107
|
mkdirRecursiveSync(options.location);
|
|
1671
|
-
if (!
|
|
2108
|
+
if (!fs10.existsSync(this.#location)) {
|
|
1672
2109
|
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
1673
2110
|
}
|
|
1674
2111
|
}
|
|
@@ -1981,15 +2418,15 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1981
2418
|
mkdirRecursiveSync(path6.join(pkiDir, "issuers"));
|
|
1982
2419
|
mkdirRecursiveSync(path6.join(pkiDir, "issuers/certs"));
|
|
1983
2420
|
mkdirRecursiveSync(path6.join(pkiDir, "issuers/crl"));
|
|
1984
|
-
if (!
|
|
2421
|
+
if (!fs10.existsSync(this.configFile) || !fs10.existsSync(this.privateKey)) {
|
|
1985
2422
|
return await this.withLock2(async () => {
|
|
1986
2423
|
if (this.state === 3 /* Disposing */ || this.state === 4 /* Disposed */) {
|
|
1987
2424
|
return;
|
|
1988
2425
|
}
|
|
1989
|
-
if (!
|
|
1990
|
-
|
|
2426
|
+
if (!fs10.existsSync(this.configFile)) {
|
|
2427
|
+
fs10.writeFileSync(this.configFile, configurationFileSimpleTemplate2);
|
|
1991
2428
|
}
|
|
1992
|
-
if (!
|
|
2429
|
+
if (!fs10.existsSync(this.privateKey)) {
|
|
1993
2430
|
debugLog("generating private key ...");
|
|
1994
2431
|
await generatePrivateKeyFile2(this.privateKey, this.keySize);
|
|
1995
2432
|
await this.#readCertificates();
|
|
@@ -2079,7 +2516,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2079
2516
|
if (typeof params.applicationUri !== "string") {
|
|
2080
2517
|
throw new Error("createSelfSignedCertificate: expecting applicationUri to be a string");
|
|
2081
2518
|
}
|
|
2082
|
-
if (!
|
|
2519
|
+
if (!fs10.existsSync(this.privateKey)) {
|
|
2083
2520
|
throw new Error(`Cannot find private key ${this.privateKey}`);
|
|
2084
2521
|
}
|
|
2085
2522
|
let certificateFilename = path6.join(this.rootDir, "own/certs/self_signed_certificate.pem");
|
|
@@ -2143,7 +2580,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2143
2580
|
return "Good" /* Good */;
|
|
2144
2581
|
}
|
|
2145
2582
|
const filename = path6.join(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
|
|
2146
|
-
await
|
|
2583
|
+
await fs10.promises.writeFile(filename, pemCertificate, "ascii");
|
|
2147
2584
|
this.#thumbs.issuers.certs.set(fingerprint, { certificate, filename });
|
|
2148
2585
|
if (addInTrustList) {
|
|
2149
2586
|
await this.trustCertificate(certificate);
|
|
@@ -2166,8 +2603,9 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2166
2603
|
index.set(key, { crls: [], serialNumbers: {} });
|
|
2167
2604
|
}
|
|
2168
2605
|
const pemCertificate = toPem2(crl, "X509 CRL");
|
|
2169
|
-
const
|
|
2170
|
-
|
|
2606
|
+
const sanitizedKey = key.replace(/:/g, "");
|
|
2607
|
+
const filename = path6.join(folder, `crl_[${sanitizedKey}].pem`);
|
|
2608
|
+
await fs10.promises.writeFile(filename, pemCertificate, "ascii");
|
|
2171
2609
|
await this.#onCrlFileAdded(index, filename);
|
|
2172
2610
|
await this.#waitAndCheckCRLProcessingStatus();
|
|
2173
2611
|
return "Good" /* Good */;
|
|
@@ -2186,11 +2624,11 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2186
2624
|
async clearRevocationLists(target) {
|
|
2187
2625
|
const clearFolder = async (folder, index) => {
|
|
2188
2626
|
try {
|
|
2189
|
-
const files = await
|
|
2627
|
+
const files = await fs10.promises.readdir(folder);
|
|
2190
2628
|
for (const file of files) {
|
|
2191
2629
|
const ext = path6.extname(file).toLowerCase();
|
|
2192
2630
|
if (ext === ".crl" || ext === ".pem" || ext === ".der") {
|
|
2193
|
-
await
|
|
2631
|
+
await fs10.promises.unlink(path6.join(folder, file));
|
|
2194
2632
|
}
|
|
2195
2633
|
}
|
|
2196
2634
|
} catch (err) {
|
|
@@ -2232,7 +2670,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2232
2670
|
return null;
|
|
2233
2671
|
}
|
|
2234
2672
|
try {
|
|
2235
|
-
await
|
|
2673
|
+
await fs10.promises.unlink(entry.filename);
|
|
2236
2674
|
} catch (err) {
|
|
2237
2675
|
if (err.code !== "ENOENT") {
|
|
2238
2676
|
throw err;
|
|
@@ -2256,7 +2694,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2256
2694
|
return null;
|
|
2257
2695
|
}
|
|
2258
2696
|
try {
|
|
2259
|
-
await
|
|
2697
|
+
await fs10.promises.unlink(entry.filename);
|
|
2260
2698
|
} catch (err) {
|
|
2261
2699
|
if (err.code !== "ENOENT") {
|
|
2262
2700
|
throw err;
|
|
@@ -2279,7 +2717,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2279
2717
|
if (!crlData) return;
|
|
2280
2718
|
for (const crlEntry of crlData.crls) {
|
|
2281
2719
|
try {
|
|
2282
|
-
await
|
|
2720
|
+
await fs10.promises.unlink(crlEntry.filename);
|
|
2283
2721
|
} catch (err) {
|
|
2284
2722
|
if (err.code !== "ENOENT") {
|
|
2285
2723
|
throw err;
|
|
@@ -2432,7 +2870,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2432
2870
|
if (status === "unknown") {
|
|
2433
2871
|
const pem = toPem2(certificate, "CERTIFICATE");
|
|
2434
2872
|
const filename = path6.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
2435
|
-
await
|
|
2873
|
+
await fs10.promises.writeFile(filename, pem);
|
|
2436
2874
|
this.#thumbs.rejected.set(fingerprint, { certificate, filename });
|
|
2437
2875
|
status = "rejected";
|
|
2438
2876
|
}
|
|
@@ -2451,7 +2889,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2451
2889
|
const certificateDest = path6.join(destFolder, path6.basename(srcEntry.filename));
|
|
2452
2890
|
debugLog("#moveCertificate", fingerprint.substring(0, 10), "old name", srcEntry.filename);
|
|
2453
2891
|
debugLog("#moveCertificate", fingerprint.substring(0, 10), "new name", certificateDest);
|
|
2454
|
-
await
|
|
2892
|
+
await fs10.promises.rename(srcEntry.filename, certificateDest);
|
|
2455
2893
|
indexSrc.delete(fingerprint);
|
|
2456
2894
|
const indexDest = newStatus === "trusted" ? this.#thumbs.trusted : this.#thumbs.rejected;
|
|
2457
2895
|
indexDest.set(fingerprint, { certificate, filename: certificateDest });
|
|
@@ -2561,11 +2999,11 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2561
2999
|
persistent: false
|
|
2562
3000
|
};
|
|
2563
3001
|
const allCapturedHandles = [];
|
|
2564
|
-
const origWatch =
|
|
3002
|
+
const origWatch = fs10.watch;
|
|
2565
3003
|
let watcherReadyCount = 0;
|
|
2566
3004
|
const totalWatchers = 5;
|
|
2567
|
-
|
|
2568
|
-
const handle = origWatch.apply(
|
|
3005
|
+
fs10.watch = ((...args) => {
|
|
3006
|
+
const handle = origWatch.apply(fs10, args);
|
|
2569
3007
|
handle.setMaxListeners(handle.getMaxListeners() + 1);
|
|
2570
3008
|
handle.on("error", () => {
|
|
2571
3009
|
});
|
|
@@ -2581,7 +3019,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2581
3019
|
}
|
|
2582
3020
|
watcherReadyCount++;
|
|
2583
3021
|
if (watcherReadyCount >= totalWatchers) {
|
|
2584
|
-
|
|
3022
|
+
fs10.watch = origWatch;
|
|
2585
3023
|
}
|
|
2586
3024
|
};
|
|
2587
3025
|
return { w, capturedHandles: allCapturedHandles.slice(startIdx), unreffAll };
|
|
@@ -2605,12 +3043,12 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2605
3043
|
* file reads, preventing main-loop stalls with large folders.
|
|
2606
3044
|
*/
|
|
2607
3045
|
async #scanCertFolder(folder, index) {
|
|
2608
|
-
if (!
|
|
2609
|
-
const files = await
|
|
3046
|
+
if (!fs10.existsSync(folder)) return;
|
|
3047
|
+
const files = await fs10.promises.readdir(folder);
|
|
2610
3048
|
for (const file of files) {
|
|
2611
3049
|
const filename = path6.join(folder, file);
|
|
2612
3050
|
try {
|
|
2613
|
-
const stat = await
|
|
3051
|
+
const stat = await fs10.promises.stat(filename);
|
|
2614
3052
|
if (!stat.isFile()) continue;
|
|
2615
3053
|
const certificate = await readCertificateAsync(filename);
|
|
2616
3054
|
const info = exploreCertificateCached(certificate);
|
|
@@ -2626,12 +3064,12 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2626
3064
|
* Scan a CRL folder and populate the in-memory CRL index.
|
|
2627
3065
|
*/
|
|
2628
3066
|
async #scanCrlFolder(folder, index) {
|
|
2629
|
-
if (!
|
|
2630
|
-
const files = await
|
|
3067
|
+
if (!fs10.existsSync(folder)) return;
|
|
3068
|
+
const files = await fs10.promises.readdir(folder);
|
|
2631
3069
|
for (const file of files) {
|
|
2632
3070
|
const filename = path6.join(folder, file);
|
|
2633
3071
|
try {
|
|
2634
|
-
const stat = await
|
|
3072
|
+
const stat = await fs10.promises.stat(filename);
|
|
2635
3073
|
if (!stat.isFile()) continue;
|
|
2636
3074
|
this.#onCrlFileAdded(index, filename);
|
|
2637
3075
|
} catch (err) {
|
|
@@ -2756,65 +3194,6 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2756
3194
|
});
|
|
2757
3195
|
}
|
|
2758
3196
|
};
|
|
2759
|
-
|
|
2760
|
-
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
2761
|
-
import assert9 from "assert";
|
|
2762
|
-
import fs10 from "fs";
|
|
2763
|
-
var q2 = quote;
|
|
2764
|
-
var n3 = makePath;
|
|
2765
|
-
async function createPFX(options) {
|
|
2766
|
-
const { certificateFile, privateKeyFile, outputFile, passphrase = "", caCertificateFiles } = options;
|
|
2767
|
-
assert9(fs10.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);
|
|
2768
|
-
assert9(fs10.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);
|
|
2769
|
-
let cmd = `pkcs12 -export`;
|
|
2770
|
-
cmd += ` -in ${q2(n3(certificateFile))}`;
|
|
2771
|
-
cmd += ` -inkey ${q2(n3(privateKeyFile))}`;
|
|
2772
|
-
if (caCertificateFiles) {
|
|
2773
|
-
for (const caFile of caCertificateFiles) {
|
|
2774
|
-
assert9(fs10.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);
|
|
2775
|
-
cmd += ` -certfile ${q2(n3(caFile))}`;
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
cmd += ` -out ${q2(n3(outputFile))}`;
|
|
2779
|
-
cmd += ` -passout pass:${passphrase}`;
|
|
2780
|
-
await execute_openssl(cmd, {});
|
|
2781
|
-
}
|
|
2782
|
-
async function extractCertificateFromPFX(options) {
|
|
2783
|
-
const { pfxFile, passphrase = "" } = options;
|
|
2784
|
-
assert9(fs10.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2785
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -clcerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
2786
|
-
return await execute_openssl(cmd, {});
|
|
2787
|
-
}
|
|
2788
|
-
async function extractPrivateKeyFromPFX(options) {
|
|
2789
|
-
const { pfxFile, passphrase = "" } = options;
|
|
2790
|
-
assert9(fs10.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2791
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -nocerts -nodes -passin pass:${passphrase}`;
|
|
2792
|
-
return await execute_openssl(cmd, {});
|
|
2793
|
-
}
|
|
2794
|
-
async function extractCACertificatesFromPFX(options) {
|
|
2795
|
-
const { pfxFile, passphrase = "" } = options;
|
|
2796
|
-
assert9(fs10.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2797
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -cacerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
2798
|
-
return await execute_openssl(cmd, {});
|
|
2799
|
-
}
|
|
2800
|
-
async function extractAllFromPFX(options) {
|
|
2801
|
-
const [certificate, privateKey, caCertificates] = await Promise.all([
|
|
2802
|
-
extractCertificateFromPFX(options),
|
|
2803
|
-
extractPrivateKeyFromPFX(options),
|
|
2804
|
-
extractCACertificatesFromPFX(options)
|
|
2805
|
-
]);
|
|
2806
|
-
return { certificate, privateKey, caCertificates };
|
|
2807
|
-
}
|
|
2808
|
-
async function convertPFXtoPEM(pfxFile, pemFile, passphrase = "") {
|
|
2809
|
-
assert9(fs10.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2810
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -out ${q2(n3(pemFile))} -nodes -passin pass:${passphrase}`;
|
|
2811
|
-
await execute_openssl(cmd, {});
|
|
2812
|
-
}
|
|
2813
|
-
async function dumpPFX(pfxFile, passphrase = "") {
|
|
2814
|
-
assert9(fs10.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2815
|
-
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -info -nodes -passin pass:${passphrase}`;
|
|
2816
|
-
return await execute_openssl(cmd, {});
|
|
2817
|
-
}
|
|
2818
3197
|
export {
|
|
2819
3198
|
CertificateAuthority,
|
|
2820
3199
|
CertificateManager,
|