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/bin/pki.mjs
CHANGED
|
@@ -1070,7 +1070,8 @@ var init_certificate_manager = __esm({
|
|
|
1070
1070
|
index.set(key, { crls: [], serialNumbers: {} });
|
|
1071
1071
|
}
|
|
1072
1072
|
const pemCertificate = toPem(crl, "X509 CRL");
|
|
1073
|
-
const
|
|
1073
|
+
const sanitizedKey = key.replace(/:/g, "");
|
|
1074
|
+
const filename = path2.join(folder, `crl_[${sanitizedKey}].pem`);
|
|
1074
1075
|
await fs4.promises.writeFile(filename, pemCertificate, "ascii");
|
|
1075
1076
|
await this.#onCrlFileAdded(index, filename);
|
|
1076
1077
|
await this.#waitAndCheckCRLProcessingStatus();
|
|
@@ -2285,6 +2286,39 @@ var init_with_openssl = __esm({
|
|
|
2285
2286
|
}
|
|
2286
2287
|
});
|
|
2287
2288
|
|
|
2289
|
+
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
2290
|
+
import assert9 from "assert";
|
|
2291
|
+
import fs9 from "fs";
|
|
2292
|
+
async function createPFX(options) {
|
|
2293
|
+
const { certificateFile, privateKeyFile, outputFile, passphrase = "", caCertificateFiles } = options;
|
|
2294
|
+
assert9(fs9.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);
|
|
2295
|
+
assert9(fs9.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);
|
|
2296
|
+
let cmd = `pkcs12 -export`;
|
|
2297
|
+
cmd += ` -in ${q3(n4(certificateFile))}`;
|
|
2298
|
+
cmd += ` -inkey ${q3(n4(privateKeyFile))}`;
|
|
2299
|
+
if (caCertificateFiles) {
|
|
2300
|
+
for (const caFile of caCertificateFiles) {
|
|
2301
|
+
assert9(fs9.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);
|
|
2302
|
+
cmd += ` -certfile ${q3(n4(caFile))}`;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
cmd += ` -out ${q3(n4(outputFile))}`;
|
|
2306
|
+
cmd += ` -passout pass:${passphrase}`;
|
|
2307
|
+
await execute_openssl(cmd, {});
|
|
2308
|
+
}
|
|
2309
|
+
var q3, n4;
|
|
2310
|
+
var init_toolbox_pfx = __esm({
|
|
2311
|
+
"packages/node-opcua-pki/lib/pki/toolbox_pfx.ts"() {
|
|
2312
|
+
"use strict";
|
|
2313
|
+
init_esm_shims();
|
|
2314
|
+
init_common();
|
|
2315
|
+
init_common2();
|
|
2316
|
+
init_execute_openssl();
|
|
2317
|
+
q3 = quote;
|
|
2318
|
+
n4 = makePath;
|
|
2319
|
+
}
|
|
2320
|
+
});
|
|
2321
|
+
|
|
2288
2322
|
// packages/node-opcua-pki/lib/ca/templates/ca_config_template.cnf.ts
|
|
2289
2323
|
var config2, ca_config_template_cnf_default;
|
|
2290
2324
|
var init_ca_config_template_cnf = __esm({
|
|
@@ -2425,18 +2459,21 @@ authorityKeyIdentifier = keyid:always,issuer:always
|
|
|
2425
2459
|
});
|
|
2426
2460
|
|
|
2427
2461
|
// packages/node-opcua-pki/lib/ca/certificate_authority.ts
|
|
2428
|
-
import
|
|
2429
|
-
import
|
|
2462
|
+
import assert10 from "assert";
|
|
2463
|
+
import fs10 from "fs";
|
|
2430
2464
|
import os4 from "os";
|
|
2431
2465
|
import path6 from "path";
|
|
2432
2466
|
import chalk6 from "chalk";
|
|
2433
2467
|
import {
|
|
2468
|
+
CertificatePurpose as CertificatePurpose2,
|
|
2469
|
+
certificateMatchesPrivateKey,
|
|
2434
2470
|
convertPEMtoDER,
|
|
2435
2471
|
exploreCertificate as exploreCertificate2,
|
|
2436
2472
|
exploreCertificateSigningRequest,
|
|
2437
2473
|
generatePrivateKeyFile as generatePrivateKeyFile2,
|
|
2438
2474
|
readCertificatePEM,
|
|
2439
2475
|
readCertificateSigningRequest,
|
|
2476
|
+
readPrivateKey,
|
|
2440
2477
|
Subject as Subject4,
|
|
2441
2478
|
toPem as toPem2
|
|
2442
2479
|
} from "node-opcua-crypto";
|
|
@@ -2457,49 +2494,49 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
2457
2494
|
await make_folders();
|
|
2458
2495
|
async function construct_default_files() {
|
|
2459
2496
|
const serial = path6.join(caRootDir, "serial");
|
|
2460
|
-
if (!
|
|
2461
|
-
await
|
|
2497
|
+
if (!fs10.existsSync(serial)) {
|
|
2498
|
+
await fs10.promises.writeFile(serial, "1000");
|
|
2462
2499
|
}
|
|
2463
2500
|
const crlNumber = path6.join(caRootDir, "crlnumber");
|
|
2464
|
-
if (!
|
|
2465
|
-
await
|
|
2501
|
+
if (!fs10.existsSync(crlNumber)) {
|
|
2502
|
+
await fs10.promises.writeFile(crlNumber, "1000");
|
|
2466
2503
|
}
|
|
2467
2504
|
const indexFile = path6.join(caRootDir, "index.txt");
|
|
2468
|
-
if (!
|
|
2469
|
-
await
|
|
2505
|
+
if (!fs10.existsSync(indexFile)) {
|
|
2506
|
+
await fs10.promises.writeFile(indexFile, "");
|
|
2470
2507
|
}
|
|
2471
2508
|
}
|
|
2472
2509
|
await construct_default_files();
|
|
2473
|
-
const caKeyExists =
|
|
2474
|
-
const caCertExists =
|
|
2510
|
+
const caKeyExists = fs10.existsSync(path6.join(caRootDir, "private/cakey.pem"));
|
|
2511
|
+
const caCertExists = fs10.existsSync(path6.join(caRootDir, "public/cacert.pem"));
|
|
2475
2512
|
if (caKeyExists && caCertExists && !config3.forceCA) {
|
|
2476
2513
|
debugLog("CA private key and certificate already exist ... skipping");
|
|
2477
2514
|
return;
|
|
2478
2515
|
}
|
|
2479
2516
|
if (caKeyExists && !caCertExists) {
|
|
2480
2517
|
debugLog("CA private key exists but cacert.pem is missing \u2014 rebuilding CA");
|
|
2481
|
-
|
|
2518
|
+
fs10.unlinkSync(path6.join(caRootDir, "private/cakey.pem"));
|
|
2482
2519
|
const staleCsr = path6.join(caRootDir, "private/cakey.csr");
|
|
2483
|
-
if (
|
|
2484
|
-
|
|
2520
|
+
if (fs10.existsSync(staleCsr)) {
|
|
2521
|
+
fs10.unlinkSync(staleCsr);
|
|
2485
2522
|
}
|
|
2486
2523
|
}
|
|
2487
2524
|
displayTitle("Create Certificate Authority (CA)");
|
|
2488
2525
|
const indexFileAttr = path6.join(caRootDir, "index.txt.attr");
|
|
2489
|
-
if (!
|
|
2490
|
-
await
|
|
2526
|
+
if (!fs10.existsSync(indexFileAttr)) {
|
|
2527
|
+
await fs10.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
2491
2528
|
}
|
|
2492
2529
|
const caConfigFile = certificateAuthority.configFile;
|
|
2493
2530
|
if (1) {
|
|
2494
2531
|
let data = configurationFileTemplate;
|
|
2495
2532
|
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
2496
|
-
await
|
|
2533
|
+
await fs10.promises.writeFile(caConfigFile, data);
|
|
2497
2534
|
}
|
|
2498
2535
|
const subjectOpt = ` -subj "${subject.toString()}" `;
|
|
2499
2536
|
processAltNames({});
|
|
2500
2537
|
const options = { cwd: caRootDir };
|
|
2501
2538
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
2502
|
-
const configOption = ` -config ${
|
|
2539
|
+
const configOption = ` -config ${q4(n5(configFile))}`;
|
|
2503
2540
|
const keySize = certificateAuthority.keySize;
|
|
2504
2541
|
const privateKeyFilename = path6.join(caRootDir, "private/cakey.pem");
|
|
2505
2542
|
const csrFilename = path6.join(caRootDir, "private/cakey.csr");
|
|
@@ -2507,14 +2544,26 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
2507
2544
|
await generatePrivateKeyFile2(privateKeyFilename, keySize);
|
|
2508
2545
|
displayTitle("Generate a certificate request for the CA key");
|
|
2509
2546
|
await execute_openssl(
|
|
2510
|
-
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " +
|
|
2511
|
-
options
|
|
2512
|
-
);
|
|
2513
|
-
displayTitle("Generate CA Certificate (self-signed)");
|
|
2514
|
-
await execute_openssl(
|
|
2515
|
-
" 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",
|
|
2547
|
+
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " + q4(n5(privateKeyFilename)) + " -out " + q4(n5(csrFilename)) + " " + subjectOpt,
|
|
2516
2548
|
options
|
|
2517
2549
|
);
|
|
2550
|
+
const issuerCA = certificateAuthority._issuerCA;
|
|
2551
|
+
if (issuerCA) {
|
|
2552
|
+
displayTitle("Generate CA Certificate (signed by issuer CA)");
|
|
2553
|
+
const issuerCert = path6.resolve(issuerCA.caCertificate);
|
|
2554
|
+
const issuerKey = path6.resolve(issuerCA.rootDir, "private/cakey.pem");
|
|
2555
|
+
const issuerSerial = path6.resolve(issuerCA.rootDir, "serial");
|
|
2556
|
+
await execute_openssl(
|
|
2557
|
+
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " + q4(n5(configFile)) + " -in private/cakey.csr -CA " + q4(n5(issuerCert)) + " -CAkey " + q4(n5(issuerKey)) + " -CAserial " + q4(n5(issuerSerial)) + " -out public/cacert.pem",
|
|
2558
|
+
options
|
|
2559
|
+
);
|
|
2560
|
+
} else {
|
|
2561
|
+
displayTitle("Generate CA Certificate (self-signed)");
|
|
2562
|
+
await execute_openssl(
|
|
2563
|
+
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " + q4(n5(configFile)) + " -in private/cakey.csr -signkey " + q4(n5(privateKeyFilename)) + " -out public/cacert.pem",
|
|
2564
|
+
options
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2518
2567
|
displaySubtitle("generate initial CRL (Certificate Revocation List)");
|
|
2519
2568
|
await regenerateCrl(certificateAuthority.revocationList, configOption, options);
|
|
2520
2569
|
displayTitle("Create Certificate Authority (CA) ---> DONE");
|
|
@@ -2524,7 +2573,7 @@ async function regenerateCrl(revocationList, configOption, options) {
|
|
|
2524
2573
|
await execute_openssl(`ca -gencrl ${configOption} -out crl/revocation_list.crl`, options);
|
|
2525
2574
|
await execute_openssl("crl -in crl/revocation_list.crl -out crl/revocation_list.der -outform der", options);
|
|
2526
2575
|
displaySubtitle("Display (Certificate Revocation List)");
|
|
2527
|
-
await execute_openssl(`crl -in ${
|
|
2576
|
+
await execute_openssl(`crl -in ${q4(n5(revocationList))} -text -noout`, options);
|
|
2528
2577
|
}
|
|
2529
2578
|
function parseOpenSSLDate(dateStr) {
|
|
2530
2579
|
const raw = dateStr?.split(",")[0] ?? "";
|
|
@@ -2538,24 +2587,27 @@ function parseOpenSSLDate(dateStr) {
|
|
|
2538
2587
|
const sec = raw.substring(10, 12);
|
|
2539
2588
|
return `${year}-${month}-${day}T${hour}:${min}:${sec}Z`;
|
|
2540
2589
|
}
|
|
2541
|
-
var defaultSubject, configurationFileTemplate, config3,
|
|
2590
|
+
var defaultSubject, configurationFileTemplate, configurationFileSimpleTemplate2, config3, n5, q4, CertificateAuthority;
|
|
2542
2591
|
var init_certificate_authority = __esm({
|
|
2543
2592
|
"packages/node-opcua-pki/lib/ca/certificate_authority.ts"() {
|
|
2544
2593
|
"use strict";
|
|
2545
2594
|
init_esm_shims();
|
|
2595
|
+
init_toolbox_pfx();
|
|
2546
2596
|
init_toolbox();
|
|
2547
2597
|
init_with_openssl();
|
|
2598
|
+
init_simple_config_template_cnf();
|
|
2548
2599
|
init_ca_config_template_cnf();
|
|
2549
2600
|
defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
|
|
2550
2601
|
configurationFileTemplate = ca_config_template_cnf_default;
|
|
2602
|
+
configurationFileSimpleTemplate2 = simple_config_template_cnf_default;
|
|
2551
2603
|
config3 = {
|
|
2552
2604
|
certificateDir: "INVALID",
|
|
2553
2605
|
forceCA: false,
|
|
2554
2606
|
pkiDir: "INVALID"
|
|
2555
2607
|
};
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2608
|
+
n5 = makePath;
|
|
2609
|
+
q4 = quote;
|
|
2610
|
+
assert10(octetStringToIpAddress("c07b9179") === "192.123.145.121");
|
|
2559
2611
|
CertificateAuthority = class {
|
|
2560
2612
|
/** RSA key size used when generating the CA private key. */
|
|
2561
2613
|
keySize;
|
|
@@ -2563,12 +2615,15 @@ var init_certificate_authority = __esm({
|
|
|
2563
2615
|
location;
|
|
2564
2616
|
/** X.500 subject of the CA certificate. */
|
|
2565
2617
|
subject;
|
|
2618
|
+
/** @internal Parent CA (undefined for root CAs). */
|
|
2619
|
+
_issuerCA;
|
|
2566
2620
|
constructor(options) {
|
|
2567
|
-
|
|
2568
|
-
|
|
2621
|
+
assert10(Object.prototype.hasOwnProperty.call(options, "location"));
|
|
2622
|
+
assert10(Object.prototype.hasOwnProperty.call(options, "keySize"));
|
|
2569
2623
|
this.location = options.location;
|
|
2570
2624
|
this.keySize = options.keySize || 2048;
|
|
2571
2625
|
this.subject = new Subject4(options.subject || defaultSubject);
|
|
2626
|
+
this._issuerCA = options.issuerCA;
|
|
2572
2627
|
}
|
|
2573
2628
|
/** Absolute path to the CA root directory (alias for {@link location}). */
|
|
2574
2629
|
get rootDir() {
|
|
@@ -2582,6 +2637,18 @@ var init_certificate_authority = __esm({
|
|
|
2582
2637
|
get caCertificate() {
|
|
2583
2638
|
return makePath(this.rootDir, "./public/cacert.pem");
|
|
2584
2639
|
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Path to the issuer certificate chain (`public/issuer_chain.pem`).
|
|
2642
|
+
*
|
|
2643
|
+
* This file is created by {@link installCACertificate} when the
|
|
2644
|
+
* provided cert file contains additional issuer certificates
|
|
2645
|
+
* (e.g. intermediate + root). It is appended to signed certs
|
|
2646
|
+
* by {@link constructCertificateChain} to produce a full chain
|
|
2647
|
+
* per OPC UA Part 6 §6.2.6.
|
|
2648
|
+
*/
|
|
2649
|
+
get issuerCertificateChain() {
|
|
2650
|
+
return makePath(this.rootDir, "./public/issuer_chain.pem");
|
|
2651
|
+
}
|
|
2585
2652
|
/**
|
|
2586
2653
|
* Path to the current Certificate Revocation List in DER format.
|
|
2587
2654
|
* (`crl/revocation_list.der`)
|
|
@@ -2639,10 +2706,10 @@ var init_certificate_authority = __esm({
|
|
|
2639
2706
|
*/
|
|
2640
2707
|
getCRLDER() {
|
|
2641
2708
|
const crlPath = this.revocationListDER;
|
|
2642
|
-
if (!
|
|
2709
|
+
if (!fs10.existsSync(crlPath)) {
|
|
2643
2710
|
return Buffer.alloc(0);
|
|
2644
2711
|
}
|
|
2645
|
-
return
|
|
2712
|
+
return fs10.readFileSync(crlPath);
|
|
2646
2713
|
}
|
|
2647
2714
|
/**
|
|
2648
2715
|
* Return the current Certificate Revocation List as a
|
|
@@ -2652,10 +2719,10 @@ var init_certificate_authority = __esm({
|
|
|
2652
2719
|
*/
|
|
2653
2720
|
getCRLPEM() {
|
|
2654
2721
|
const crlPath = this.revocationList;
|
|
2655
|
-
if (!
|
|
2722
|
+
if (!fs10.existsSync(crlPath)) {
|
|
2656
2723
|
return "";
|
|
2657
2724
|
}
|
|
2658
|
-
const raw =
|
|
2725
|
+
const raw = fs10.readFileSync(crlPath, "utf-8");
|
|
2659
2726
|
const beginMarker = "-----BEGIN X509 CRL-----";
|
|
2660
2727
|
const idx = raw.indexOf(beginMarker);
|
|
2661
2728
|
if (idx > 0) {
|
|
@@ -2708,7 +2775,7 @@ var init_certificate_authority = __esm({
|
|
|
2708
2775
|
getCertificateBySerial(serial) {
|
|
2709
2776
|
const upper = serial.toUpperCase();
|
|
2710
2777
|
const certFile = path6.join(this.rootDir, "certs", `${upper}.pem`);
|
|
2711
|
-
if (!
|
|
2778
|
+
if (!fs10.existsSync(certFile)) {
|
|
2712
2779
|
return void 0;
|
|
2713
2780
|
}
|
|
2714
2781
|
const pem = readCertificatePEM(certFile);
|
|
@@ -2737,10 +2804,10 @@ var init_certificate_authority = __esm({
|
|
|
2737
2804
|
*/
|
|
2738
2805
|
_parseIndexTxt() {
|
|
2739
2806
|
const indexPath = this.indexFile;
|
|
2740
|
-
if (!
|
|
2807
|
+
if (!fs10.existsSync(indexPath)) {
|
|
2741
2808
|
return [];
|
|
2742
2809
|
}
|
|
2743
|
-
const content =
|
|
2810
|
+
const content = fs10.readFileSync(indexPath, "utf-8");
|
|
2744
2811
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
2745
2812
|
const records = [];
|
|
2746
2813
|
for (const line of lines) {
|
|
@@ -2794,25 +2861,145 @@ var init_certificate_authority = __esm({
|
|
|
2794
2861
|
* internally so that callers can work with in-memory
|
|
2795
2862
|
* buffers only.
|
|
2796
2863
|
*
|
|
2864
|
+
* The CA can override fields from the CSR by passing
|
|
2865
|
+
* `options.dns`, `options.ip`, `options.applicationUri`,
|
|
2866
|
+
* `options.startDate`, or `options.subject`.
|
|
2867
|
+
*
|
|
2797
2868
|
* @param csrDer - the CSR as a DER-encoded buffer
|
|
2798
|
-
* @param options - signing options
|
|
2799
|
-
* @param options.validity - certificate validity in days
|
|
2800
|
-
* (default: 365)
|
|
2869
|
+
* @param options - signing options and CA overrides
|
|
2801
2870
|
* @returns the signed certificate as a DER-encoded buffer
|
|
2802
2871
|
*/
|
|
2803
2872
|
async signCertificateRequestFromDER(csrDer, options) {
|
|
2804
2873
|
const validity = options?.validity ?? 365;
|
|
2805
|
-
const tmpDir = await
|
|
2874
|
+
const tmpDir = await fs10.promises.mkdtemp(path6.join(os4.tmpdir(), "pki-sign-"));
|
|
2806
2875
|
try {
|
|
2807
2876
|
const csrFile = path6.join(tmpDir, "request.csr");
|
|
2808
2877
|
const certFile = path6.join(tmpDir, "certificate.pem");
|
|
2809
2878
|
const csrPem = toPem2(csrDer, "CERTIFICATE REQUEST");
|
|
2810
|
-
await
|
|
2811
|
-
|
|
2879
|
+
await fs10.promises.writeFile(csrFile, csrPem, "utf-8");
|
|
2880
|
+
const signingParams = { validity };
|
|
2881
|
+
if (options?.startDate) signingParams.startDate = options.startDate;
|
|
2882
|
+
if (options?.dns) signingParams.dns = options.dns;
|
|
2883
|
+
if (options?.ip) signingParams.ip = options.ip;
|
|
2884
|
+
if (options?.applicationUri) signingParams.applicationUri = options.applicationUri;
|
|
2885
|
+
if (options?.subject) signingParams.subject = options.subject;
|
|
2886
|
+
await this.signCertificateRequest(certFile, csrFile, signingParams);
|
|
2812
2887
|
const certPem = readCertificatePEM(certFile);
|
|
2813
2888
|
return convertPEMtoDER(certPem);
|
|
2814
2889
|
} finally {
|
|
2815
|
-
await
|
|
2890
|
+
await fs10.promises.rm(tmpDir, {
|
|
2891
|
+
recursive: true,
|
|
2892
|
+
force: true
|
|
2893
|
+
});
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Generate a new RSA key pair, create an internal CSR, sign it
|
|
2898
|
+
* with this CA, and return both the certificate and private key
|
|
2899
|
+
* as DER-encoded buffers.
|
|
2900
|
+
*
|
|
2901
|
+
* The private key is **never stored** by the CA — it exists only
|
|
2902
|
+
* in a temporary directory that is cleaned up after the operation.
|
|
2903
|
+
*
|
|
2904
|
+
* This is used by `StartNewKeyPairRequest` (OPC UA Part 12) for
|
|
2905
|
+
* constrained devices that cannot generate their own keys.
|
|
2906
|
+
*
|
|
2907
|
+
* @param options - key generation and certificate parameters
|
|
2908
|
+
* @returns `{ certificateDer, privateKey }` — certificate as DER,
|
|
2909
|
+
* private key as a branded `PrivateKey` buffer
|
|
2910
|
+
*/
|
|
2911
|
+
async generateKeyPairAndSignDER(options) {
|
|
2912
|
+
const keySize = options.keySize ?? 2048;
|
|
2913
|
+
const validity = options.validity ?? 365;
|
|
2914
|
+
const startDate = options.startDate ?? /* @__PURE__ */ new Date();
|
|
2915
|
+
const tmpDir = await fs10.promises.mkdtemp(path6.join(os4.tmpdir(), "pki-keygen-"));
|
|
2916
|
+
try {
|
|
2917
|
+
const privateKeyFile = path6.join(tmpDir, "private_key.pem");
|
|
2918
|
+
await generatePrivateKeyFile2(privateKeyFile, keySize);
|
|
2919
|
+
const configFile = path6.join(tmpDir, "openssl.cnf");
|
|
2920
|
+
await fs10.promises.writeFile(configFile, configurationFileSimpleTemplate2, "utf-8");
|
|
2921
|
+
const csrFile = path6.join(tmpDir, "request.csr");
|
|
2922
|
+
await createCertificateSigningRequestWithOpenSSL(csrFile, {
|
|
2923
|
+
rootDir: tmpDir,
|
|
2924
|
+
configFile,
|
|
2925
|
+
privateKey: privateKeyFile,
|
|
2926
|
+
applicationUri: options.applicationUri,
|
|
2927
|
+
subject: options.subject,
|
|
2928
|
+
dns: options.dns ?? [],
|
|
2929
|
+
ip: options.ip ?? [],
|
|
2930
|
+
purpose: CertificatePurpose2.ForApplication
|
|
2931
|
+
});
|
|
2932
|
+
const certFile = path6.join(tmpDir, "certificate.pem");
|
|
2933
|
+
await this.signCertificateRequest(certFile, csrFile, {
|
|
2934
|
+
applicationUri: options.applicationUri,
|
|
2935
|
+
dns: options.dns,
|
|
2936
|
+
ip: options.ip,
|
|
2937
|
+
startDate,
|
|
2938
|
+
validity
|
|
2939
|
+
});
|
|
2940
|
+
const certPem = readCertificatePEM(certFile);
|
|
2941
|
+
const certificateDer = convertPEMtoDER(certPem);
|
|
2942
|
+
const privateKey = readPrivateKey(privateKeyFile);
|
|
2943
|
+
return { certificateDer, privateKey };
|
|
2944
|
+
} finally {
|
|
2945
|
+
await fs10.promises.rm(tmpDir, {
|
|
2946
|
+
recursive: true,
|
|
2947
|
+
force: true
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
/**
|
|
2952
|
+
* Generate a new RSA key pair, create an internal CSR, sign it
|
|
2953
|
+
* with this CA, and return the result as a PKCS#12 (PFX)
|
|
2954
|
+
* buffer bundling the certificate, private key, and CA chain.
|
|
2955
|
+
*
|
|
2956
|
+
* The private key is **never stored** by the CA — it exists only
|
|
2957
|
+
* in a temporary directory that is cleaned up after the operation.
|
|
2958
|
+
*
|
|
2959
|
+
* @param options - key generation, certificate, and PFX options
|
|
2960
|
+
* @returns the PFX as a `Buffer`
|
|
2961
|
+
*/
|
|
2962
|
+
async generateKeyPairAndSignPFX(options) {
|
|
2963
|
+
const keySize = options.keySize ?? 2048;
|
|
2964
|
+
const validity = options.validity ?? 365;
|
|
2965
|
+
const startDate = options.startDate ?? /* @__PURE__ */ new Date();
|
|
2966
|
+
const passphrase = options.passphrase ?? "";
|
|
2967
|
+
const tmpDir = await fs10.promises.mkdtemp(path6.join(os4.tmpdir(), "pki-keygen-pfx-"));
|
|
2968
|
+
try {
|
|
2969
|
+
const privateKeyFile = path6.join(tmpDir, "private_key.pem");
|
|
2970
|
+
await generatePrivateKeyFile2(privateKeyFile, keySize);
|
|
2971
|
+
const configFile = path6.join(tmpDir, "openssl.cnf");
|
|
2972
|
+
await fs10.promises.writeFile(configFile, configurationFileSimpleTemplate2, "utf-8");
|
|
2973
|
+
const csrFile = path6.join(tmpDir, "request.csr");
|
|
2974
|
+
await createCertificateSigningRequestWithOpenSSL(csrFile, {
|
|
2975
|
+
rootDir: tmpDir,
|
|
2976
|
+
configFile,
|
|
2977
|
+
privateKey: privateKeyFile,
|
|
2978
|
+
applicationUri: options.applicationUri,
|
|
2979
|
+
subject: options.subject,
|
|
2980
|
+
dns: options.dns ?? [],
|
|
2981
|
+
ip: options.ip ?? [],
|
|
2982
|
+
purpose: CertificatePurpose2.ForApplication
|
|
2983
|
+
});
|
|
2984
|
+
const certFile = path6.join(tmpDir, "certificate.pem");
|
|
2985
|
+
await this.signCertificateRequest(certFile, csrFile, {
|
|
2986
|
+
applicationUri: options.applicationUri,
|
|
2987
|
+
dns: options.dns,
|
|
2988
|
+
ip: options.ip,
|
|
2989
|
+
startDate,
|
|
2990
|
+
validity
|
|
2991
|
+
});
|
|
2992
|
+
const pfxFile = path6.join(tmpDir, "bundle.pfx");
|
|
2993
|
+
await createPFX({
|
|
2994
|
+
certificateFile: certFile,
|
|
2995
|
+
privateKeyFile,
|
|
2996
|
+
outputFile: pfxFile,
|
|
2997
|
+
passphrase,
|
|
2998
|
+
caCertificateFiles: [this.caCertificate]
|
|
2999
|
+
});
|
|
3000
|
+
return await fs10.promises.readFile(pfxFile);
|
|
3001
|
+
} finally {
|
|
3002
|
+
await fs10.promises.rm(tmpDir, {
|
|
2816
3003
|
recursive: true,
|
|
2817
3004
|
force: true
|
|
2818
3005
|
});
|
|
@@ -2834,7 +3021,7 @@ var init_certificate_authority = __esm({
|
|
|
2834
3021
|
const info = exploreCertificate2(certDer);
|
|
2835
3022
|
const serial = info.tbsCertificate.serialNumber.replace(/:/g, "").toUpperCase();
|
|
2836
3023
|
const storedCertFile = path6.join(this.rootDir, "certs", `${serial}.pem`);
|
|
2837
|
-
if (!
|
|
3024
|
+
if (!fs10.existsSync(storedCertFile)) {
|
|
2838
3025
|
throw new Error(`Cannot revoke: no stored certificate found for serial ${serial} at ${storedCertFile}`);
|
|
2839
3026
|
}
|
|
2840
3027
|
await this.revokeCertificate(storedCertFile, {
|
|
@@ -2849,6 +3036,204 @@ var init_certificate_authority = __esm({
|
|
|
2849
3036
|
async initialize() {
|
|
2850
3037
|
await construct_CertificateAuthority(this);
|
|
2851
3038
|
}
|
|
3039
|
+
/**
|
|
3040
|
+
* Initialize the CA directory structure and generate the
|
|
3041
|
+
* private key + CSR **without signing**.
|
|
3042
|
+
*
|
|
3043
|
+
* Use this when the CA certificate will be signed by an
|
|
3044
|
+
* external (third-party) root CA. After receiving the signed
|
|
3045
|
+
* certificate, call {@link installCACertificate} to complete
|
|
3046
|
+
* the setup.
|
|
3047
|
+
*
|
|
3048
|
+
* **Idempotent / restart-safe:**
|
|
3049
|
+
* - If the CA certificate exists and is valid → `{ status: "ready" }`
|
|
3050
|
+
* - If the CA certificate has expired → `{ status: "expired", csrPath, expiryDate }`
|
|
3051
|
+
* (a new CSR is generated, preserving the existing private key)
|
|
3052
|
+
* - If key + CSR exist but no cert (restart before install) →
|
|
3053
|
+
* `{ status: "pending", csrPath }` without regenerating
|
|
3054
|
+
* - Otherwise → generates key + CSR → `{ status: "created", csrPath }`
|
|
3055
|
+
*
|
|
3056
|
+
* @returns an {@link InitializeCSRResult} describing the CA state
|
|
3057
|
+
*/
|
|
3058
|
+
async initializeCSR() {
|
|
3059
|
+
const caRootDir = path6.resolve(this.rootDir);
|
|
3060
|
+
mkdirRecursiveSync(caRootDir);
|
|
3061
|
+
for (const dir of ["private", "public", "certs", "crl", "conf"]) {
|
|
3062
|
+
mkdirRecursiveSync(path6.join(caRootDir, dir));
|
|
3063
|
+
}
|
|
3064
|
+
const caCertFile = this.caCertificate;
|
|
3065
|
+
const privateKeyFile = path6.join(caRootDir, "private/cakey.pem");
|
|
3066
|
+
const csrFile = path6.join(caRootDir, "private/cakey.csr");
|
|
3067
|
+
if (fs10.existsSync(caCertFile)) {
|
|
3068
|
+
const certDer = convertPEMtoDER(readCertificatePEM(caCertFile));
|
|
3069
|
+
const certInfo = exploreCertificate2(certDer);
|
|
3070
|
+
const notAfter = certInfo.tbsCertificate.validity.notAfter;
|
|
3071
|
+
if (notAfter.getTime() < Date.now()) {
|
|
3072
|
+
debugLog("CA certificate has expired \u2014 generating renewal CSR");
|
|
3073
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
3074
|
+
return { status: "expired", csrPath: csrFile, expiryDate: notAfter };
|
|
3075
|
+
}
|
|
3076
|
+
debugLog("CA certificate already exists and is valid \u2014 ready");
|
|
3077
|
+
return { status: "ready" };
|
|
3078
|
+
}
|
|
3079
|
+
if (fs10.existsSync(privateKeyFile) && fs10.existsSync(csrFile)) {
|
|
3080
|
+
debugLog("CA key + CSR already exist \u2014 pending external signing");
|
|
3081
|
+
return { status: "pending", csrPath: csrFile };
|
|
3082
|
+
}
|
|
3083
|
+
const serial = path6.join(caRootDir, "serial");
|
|
3084
|
+
if (!fs10.existsSync(serial)) {
|
|
3085
|
+
await fs10.promises.writeFile(serial, "1000");
|
|
3086
|
+
}
|
|
3087
|
+
const crlNumber = path6.join(caRootDir, "crlnumber");
|
|
3088
|
+
if (!fs10.existsSync(crlNumber)) {
|
|
3089
|
+
await fs10.promises.writeFile(crlNumber, "1000");
|
|
3090
|
+
}
|
|
3091
|
+
const indexFile = path6.join(caRootDir, "index.txt");
|
|
3092
|
+
if (!fs10.existsSync(indexFile)) {
|
|
3093
|
+
await fs10.promises.writeFile(indexFile, "");
|
|
3094
|
+
}
|
|
3095
|
+
const indexFileAttr = path6.join(caRootDir, "index.txt.attr");
|
|
3096
|
+
if (!fs10.existsSync(indexFileAttr)) {
|
|
3097
|
+
await fs10.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
3098
|
+
}
|
|
3099
|
+
const caConfigFile = this.configFile;
|
|
3100
|
+
let data = configurationFileTemplate;
|
|
3101
|
+
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
3102
|
+
await fs10.promises.writeFile(caConfigFile, data);
|
|
3103
|
+
if (!fs10.existsSync(privateKeyFile)) {
|
|
3104
|
+
await generatePrivateKeyFile2(privateKeyFile, this.keySize);
|
|
3105
|
+
}
|
|
3106
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
3107
|
+
return { status: "created", csrPath: csrFile };
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Check whether the CA certificate needs renewal and, if so,
|
|
3111
|
+
* generate a new CSR for re-signing by the external root CA.
|
|
3112
|
+
*
|
|
3113
|
+
* Use this while the CA is running to detect upcoming expiry
|
|
3114
|
+
* **before** it actually expires. The existing private key is
|
|
3115
|
+
* preserved so previously issued certs remain valid.
|
|
3116
|
+
*
|
|
3117
|
+
* @param thresholdDays - number of days before expiry at which
|
|
3118
|
+
* to trigger renewal (default: 30)
|
|
3119
|
+
* @returns an {@link InitializeCSRResult} — `"expired"` if
|
|
3120
|
+
* renewal is needed, `"ready"` if the cert is still valid
|
|
3121
|
+
*/
|
|
3122
|
+
async renewCSR(thresholdDays = 30) {
|
|
3123
|
+
const caRootDir = path6.resolve(this.rootDir);
|
|
3124
|
+
const caCertFile = this.caCertificate;
|
|
3125
|
+
const privateKeyFile = path6.join(caRootDir, "private/cakey.pem");
|
|
3126
|
+
const csrFile = path6.join(caRootDir, "private/cakey.csr");
|
|
3127
|
+
if (!fs10.existsSync(caCertFile)) {
|
|
3128
|
+
return this.initializeCSR();
|
|
3129
|
+
}
|
|
3130
|
+
const certDer = convertPEMtoDER(readCertificatePEM(caCertFile));
|
|
3131
|
+
const certInfo = exploreCertificate2(certDer);
|
|
3132
|
+
const notAfter = certInfo.tbsCertificate.validity.notAfter;
|
|
3133
|
+
const thresholdMs = thresholdDays * 24 * 60 * 60 * 1e3;
|
|
3134
|
+
if (notAfter.getTime() - Date.now() < thresholdMs) {
|
|
3135
|
+
debugLog(`CA certificate expires within ${thresholdDays} days \u2014 generating renewal CSR`);
|
|
3136
|
+
await this._generateCSR(caRootDir, privateKeyFile, csrFile);
|
|
3137
|
+
return { status: "expired", csrPath: csrFile, expiryDate: notAfter };
|
|
3138
|
+
}
|
|
3139
|
+
return { status: "ready" };
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* Generate a CSR using the existing private key.
|
|
3143
|
+
* @internal
|
|
3144
|
+
*/
|
|
3145
|
+
async _generateCSR(caRootDir, privateKeyFile, csrFile) {
|
|
3146
|
+
const subjectOpt = ` -subj "${this.subject.toString()}" `;
|
|
3147
|
+
processAltNames({});
|
|
3148
|
+
const options = { cwd: caRootDir };
|
|
3149
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
3150
|
+
const configOption = ` -config ${q4(n5(configFile))}`;
|
|
3151
|
+
await execute_openssl(
|
|
3152
|
+
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " + q4(n5(privateKeyFile)) + " -out " + q4(n5(csrFile)) + " " + subjectOpt,
|
|
3153
|
+
options
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Install an externally-signed CA certificate and generate
|
|
3158
|
+
* the initial CRL.
|
|
3159
|
+
*
|
|
3160
|
+
* Call this after {@link initializeCSR} once the external
|
|
3161
|
+
* root CA has signed the CSR.
|
|
3162
|
+
*
|
|
3163
|
+
* **Safety checks:**
|
|
3164
|
+
* - Verifies that the certificate's public key matches the
|
|
3165
|
+
* CA private key before installing.
|
|
3166
|
+
*
|
|
3167
|
+
* @param signedCertFile - path to the PEM-encoded signed
|
|
3168
|
+
* CA certificate (issued by the external root CA)
|
|
3169
|
+
* @returns an {@link InstallCACertificateResult} with
|
|
3170
|
+
* `status: "success"` or `status: "error"` and a `reason`
|
|
3171
|
+
*/
|
|
3172
|
+
async installCACertificate(signedCertFile) {
|
|
3173
|
+
const caRootDir = path6.resolve(this.rootDir);
|
|
3174
|
+
const caCertFile = this.caCertificate;
|
|
3175
|
+
const privateKeyFile = path6.join(caRootDir, "private/cakey.pem");
|
|
3176
|
+
const fullPem = await fs10.promises.readFile(signedCertFile, "utf8");
|
|
3177
|
+
const pemBlocks = fullPem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
3178
|
+
if (!pemBlocks || pemBlocks.length === 0) {
|
|
3179
|
+
return {
|
|
3180
|
+
status: "error",
|
|
3181
|
+
reason: "no_certificate_found",
|
|
3182
|
+
message: "The provided file does not contain any PEM-encoded certificate."
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
const certDer = convertPEMtoDER(pemBlocks[0]);
|
|
3186
|
+
const privateKey = readPrivateKey(privateKeyFile);
|
|
3187
|
+
if (!certificateMatchesPrivateKey(certDer, privateKey)) {
|
|
3188
|
+
return {
|
|
3189
|
+
status: "error",
|
|
3190
|
+
reason: "certificate_key_mismatch",
|
|
3191
|
+
message: "The provided certificate does not match the CA private key. Ensure the certificate was signed from the CSR generated by initializeCSR()."
|
|
3192
|
+
};
|
|
3193
|
+
}
|
|
3194
|
+
await fs10.promises.writeFile(caCertFile, `${pemBlocks[0]}
|
|
3195
|
+
`);
|
|
3196
|
+
const issuerChainFile = this.issuerCertificateChain;
|
|
3197
|
+
if (pemBlocks.length > 1) {
|
|
3198
|
+
const issuerPem = `${pemBlocks.slice(1).join("\n")}
|
|
3199
|
+
`;
|
|
3200
|
+
await fs10.promises.writeFile(issuerChainFile, issuerPem);
|
|
3201
|
+
debugLog(`Stored ${pemBlocks.length - 1} issuer certificate(s) in issuer_chain.pem`);
|
|
3202
|
+
} else {
|
|
3203
|
+
if (fs10.existsSync(issuerChainFile)) {
|
|
3204
|
+
await fs10.promises.unlink(issuerChainFile);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
const options = { cwd: caRootDir };
|
|
3208
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
3209
|
+
const configOption = ` -config ${q4(n5(configFile))}`;
|
|
3210
|
+
await regenerateCrl(this.revocationList, configOption, options);
|
|
3211
|
+
return { status: "success" };
|
|
3212
|
+
}
|
|
3213
|
+
/**
|
|
3214
|
+
* Sign a CSR with CA extensions (`v3_ca`), producing a
|
|
3215
|
+
* subordinate CA certificate.
|
|
3216
|
+
*
|
|
3217
|
+
* Unlike {@link signCertificateRequest} which signs with
|
|
3218
|
+
* end-entity extensions (SANs, etc.), this method signs
|
|
3219
|
+
* with `basicConstraints = CA:TRUE` and `keyUsage =
|
|
3220
|
+
* keyCertSign, cRLSign`.
|
|
3221
|
+
*
|
|
3222
|
+
* @param certFile - output path for the signed CA cert (PEM)
|
|
3223
|
+
* @param csrFile - path to the subordinate CA's CSR
|
|
3224
|
+
* @param params - signing parameters
|
|
3225
|
+
*/
|
|
3226
|
+
async signCACertificateRequest(certFile, csrFile, params) {
|
|
3227
|
+
const caRootDir = path6.resolve(this.rootDir);
|
|
3228
|
+
const options = { cwd: caRootDir };
|
|
3229
|
+
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
3230
|
+
const validity = params.validity ?? 3650;
|
|
3231
|
+
await execute_openssl(
|
|
3232
|
+
` x509 -sha256 -req -days ${validity} -text -extensions v3_ca -extfile ` + q4(n5(configFile)) + " -in " + q4(n5(csrFile)) + " -CA " + q4(n5(this.caCertificate)) + " -CAkey " + q4(n5(path6.join(caRootDir, "private/cakey.pem"))) + " -CAserial " + q4(n5(path6.join(caRootDir, "serial"))) + " -out " + q4(n5(certFile)),
|
|
3233
|
+
options
|
|
3234
|
+
);
|
|
3235
|
+
await this.constructCertificateChain(certFile);
|
|
3236
|
+
}
|
|
2852
3237
|
/**
|
|
2853
3238
|
* Rebuild the combined CA certificate + CRL file.
|
|
2854
3239
|
*
|
|
@@ -2858,13 +3243,13 @@ var init_certificate_authority = __esm({
|
|
|
2858
3243
|
*/
|
|
2859
3244
|
async constructCACertificateWithCRL() {
|
|
2860
3245
|
const cacertWithCRL = this.caCertificateWithCrl;
|
|
2861
|
-
if (
|
|
2862
|
-
await
|
|
3246
|
+
if (fs10.existsSync(this.revocationList)) {
|
|
3247
|
+
await fs10.promises.writeFile(
|
|
2863
3248
|
cacertWithCRL,
|
|
2864
|
-
|
|
3249
|
+
fs10.readFileSync(this.caCertificate, "utf8") + fs10.readFileSync(this.revocationList, "utf8")
|
|
2865
3250
|
);
|
|
2866
3251
|
} else {
|
|
2867
|
-
await
|
|
3252
|
+
await fs10.promises.writeFile(cacertWithCRL, fs10.readFileSync(this.caCertificate));
|
|
2868
3253
|
}
|
|
2869
3254
|
}
|
|
2870
3255
|
/**
|
|
@@ -2874,14 +3259,15 @@ var init_certificate_authority = __esm({
|
|
|
2874
3259
|
* @param certificate - path to the certificate file to extend
|
|
2875
3260
|
*/
|
|
2876
3261
|
async constructCertificateChain(certificate) {
|
|
2877
|
-
|
|
2878
|
-
|
|
3262
|
+
assert10(fs10.existsSync(certificate));
|
|
3263
|
+
assert10(fs10.existsSync(this.caCertificate));
|
|
2879
3264
|
debugLog(chalk6.yellow(" certificate file :"), chalk6.cyan(certificate));
|
|
2880
|
-
await
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
3265
|
+
let chain = await fs10.promises.readFile(certificate, "utf8");
|
|
3266
|
+
chain += await fs10.promises.readFile(this.caCertificate, "utf8");
|
|
3267
|
+
if (fs10.existsSync(this.issuerCertificateChain)) {
|
|
3268
|
+
chain += await fs10.promises.readFile(this.issuerCertificateChain, "utf8");
|
|
3269
|
+
}
|
|
3270
|
+
await fs10.promises.writeFile(certificate, chain);
|
|
2885
3271
|
}
|
|
2886
3272
|
/**
|
|
2887
3273
|
* Create a self-signed certificate using OpenSSL.
|
|
@@ -2891,8 +3277,8 @@ var init_certificate_authority = __esm({
|
|
|
2891
3277
|
* @param params - certificate parameters (subject, validity, SANs)
|
|
2892
3278
|
*/
|
|
2893
3279
|
async createSelfSignedCertificate(certificateFile, privateKey, params) {
|
|
2894
|
-
|
|
2895
|
-
|
|
3280
|
+
assert10(typeof privateKey === "string");
|
|
3281
|
+
assert10(fs10.existsSync(privateKey));
|
|
2896
3282
|
if (!certificateFileExist(certificateFile)) {
|
|
2897
3283
|
return;
|
|
2898
3284
|
}
|
|
@@ -2900,7 +3286,7 @@ var init_certificate_authority = __esm({
|
|
|
2900
3286
|
adjustApplicationUri(params);
|
|
2901
3287
|
processAltNames(params);
|
|
2902
3288
|
const csrFile = `${certificateFile}_csr`;
|
|
2903
|
-
|
|
3289
|
+
assert10(csrFile);
|
|
2904
3290
|
const configFile = generateStaticConfig(this.configFile, { cwd: this.rootDir });
|
|
2905
3291
|
const options = {
|
|
2906
3292
|
cwd: this.rootDir,
|
|
@@ -2911,19 +3297,19 @@ var init_certificate_authority = __esm({
|
|
|
2911
3297
|
const subjectOptions = subject && subject.length > 1 ? ` -subj ${subject} ` : "";
|
|
2912
3298
|
displaySubtitle("- the certificate signing request");
|
|
2913
3299
|
await execute_openssl(
|
|
2914
|
-
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " +
|
|
3300
|
+
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " + q4(n5(privateKey)) + " -out " + q4(n5(csrFile)),
|
|
2915
3301
|
options
|
|
2916
3302
|
);
|
|
2917
3303
|
displaySubtitle("- creating the self-signed certificate");
|
|
2918
3304
|
await execute_openssl(
|
|
2919
|
-
"ca -selfsign -keyfile " +
|
|
3305
|
+
"ca -selfsign -keyfile " + q4(n5(privateKey)) + " -startdate " + x509Date(params.startDate) + " -enddate " + x509Date(params.endDate) + " -batch -out " + q4(n5(certificateFile)) + " -in " + q4(n5(csrFile)),
|
|
2920
3306
|
options
|
|
2921
3307
|
);
|
|
2922
3308
|
displaySubtitle("- dump the certificate for a check");
|
|
2923
|
-
await execute_openssl(`x509 -in ${
|
|
3309
|
+
await execute_openssl(`x509 -in ${q4(n5(certificateFile))} -dates -fingerprint -purpose -noout`, {});
|
|
2924
3310
|
displaySubtitle("- verify self-signed certificate");
|
|
2925
|
-
await execute_openssl_no_failure(`verify -verbose -CAfile ${
|
|
2926
|
-
await
|
|
3311
|
+
await execute_openssl_no_failure(`verify -verbose -CAfile ${q4(n5(certificateFile))} ${q4(n5(certificateFile))}`, options);
|
|
3312
|
+
await fs10.promises.unlink(csrFile);
|
|
2927
3313
|
}
|
|
2928
3314
|
/**
|
|
2929
3315
|
* Revoke a certificate and regenerate the CRL.
|
|
@@ -2952,22 +3338,22 @@ var init_certificate_authority = __esm({
|
|
|
2952
3338
|
setEnv("ALTNAME", "");
|
|
2953
3339
|
const randomFile = path6.join(this.rootDir, "random.rnd");
|
|
2954
3340
|
setEnv("RANDFILE", randomFile);
|
|
2955
|
-
const configOption = ` -config ${
|
|
3341
|
+
const configOption = ` -config ${q4(n5(configFile))}`;
|
|
2956
3342
|
const reason = params.reason || "keyCompromise";
|
|
2957
|
-
|
|
3343
|
+
assert10(crlReasons.indexOf(reason) >= 0);
|
|
2958
3344
|
displayTitle(`Revoking certificate ${certificate}`);
|
|
2959
3345
|
displaySubtitle("Revoke certificate");
|
|
2960
|
-
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${
|
|
3346
|
+
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${q4(certificate)} -crl_reason ${reason}`, options);
|
|
2961
3347
|
await regenerateCrl(this.revocationList, configOption, options);
|
|
2962
3348
|
displaySubtitle("Verify that certificate is revoked");
|
|
2963
3349
|
await execute_openssl_no_failure(
|
|
2964
|
-
"verify -verbose -CRLfile " +
|
|
3350
|
+
"verify -verbose -CRLfile " + q4(n5(this.revocationList)) + " -CAfile " + q4(n5(this.caCertificate)) + " -crl_check " + q4(n5(certificate)),
|
|
2965
3351
|
options
|
|
2966
3352
|
);
|
|
2967
3353
|
displaySubtitle("Produce CRL in DER form ");
|
|
2968
|
-
await execute_openssl(`crl -in ${
|
|
3354
|
+
await execute_openssl(`crl -in ${q4(n5(this.revocationList))} -out crl/revocation_list.der -outform der`, options);
|
|
2969
3355
|
displaySubtitle("Produce CRL in PEM form ");
|
|
2970
|
-
await execute_openssl(`crl -in ${
|
|
3356
|
+
await execute_openssl(`crl -in ${q4(n5(this.revocationList))} -out crl/revocation_list.pem -outform pem -text `, options);
|
|
2971
3357
|
}
|
|
2972
3358
|
/**
|
|
2973
3359
|
* Sign a Certificate Signing Request (CSR) with this CA.
|
|
@@ -2983,7 +3369,7 @@ var init_certificate_authority = __esm({
|
|
|
2983
3369
|
*/
|
|
2984
3370
|
async signCertificateRequest(certificate, certificateSigningRequestFilename, params1) {
|
|
2985
3371
|
await ensure_openssl_installed();
|
|
2986
|
-
|
|
3372
|
+
assert10(fs10.existsSync(certificateSigningRequestFilename));
|
|
2987
3373
|
if (!certificateFileExist(certificate)) {
|
|
2988
3374
|
return "";
|
|
2989
3375
|
}
|
|
@@ -3010,11 +3396,11 @@ var init_certificate_authority = __esm({
|
|
|
3010
3396
|
displaySubtitle("- then we ask the authority to sign the certificate signing request");
|
|
3011
3397
|
const configOption = ` -config ${configFile}`;
|
|
3012
3398
|
await execute_openssl(
|
|
3013
|
-
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " +
|
|
3399
|
+
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " + q4(n5(certificate)) + " -in " + q4(n5(certificateSigningRequestFilename)),
|
|
3014
3400
|
options
|
|
3015
3401
|
);
|
|
3016
3402
|
displaySubtitle("- dump the certificate for a check");
|
|
3017
|
-
await execute_openssl(`x509 -in ${
|
|
3403
|
+
await execute_openssl(`x509 -in ${q4(n5(certificate))} -dates -fingerprint -purpose -noout`, options);
|
|
3018
3404
|
displaySubtitle("- construct CA certificate with CRL");
|
|
3019
3405
|
await this.constructCACertificateWithCRL();
|
|
3020
3406
|
displaySubtitle("- construct certificate chain");
|
|
@@ -3037,7 +3423,7 @@ var init_certificate_authority = __esm({
|
|
|
3037
3423
|
const _configOption = ` -config ${configFile}`;
|
|
3038
3424
|
_configOption;
|
|
3039
3425
|
await execute_openssl_no_failure(
|
|
3040
|
-
`verify -verbose -CAfile ${
|
|
3426
|
+
`verify -verbose -CAfile ${q4(n5(this.caCertificateWithCrl))} ${q4(n5(certificate))}`,
|
|
3041
3427
|
options
|
|
3042
3428
|
);
|
|
3043
3429
|
}
|
|
@@ -3047,13 +3433,13 @@ var init_certificate_authority = __esm({
|
|
|
3047
3433
|
});
|
|
3048
3434
|
|
|
3049
3435
|
// packages/node-opcua-pki/lib/ca/crypto_create_CA.ts
|
|
3050
|
-
import
|
|
3051
|
-
import
|
|
3436
|
+
import assert11 from "assert";
|
|
3437
|
+
import fs11 from "fs";
|
|
3052
3438
|
import { createRequire } from "module";
|
|
3053
3439
|
import os5 from "os";
|
|
3054
3440
|
import path7 from "path";
|
|
3055
3441
|
import chalk7 from "chalk";
|
|
3056
|
-
import { CertificatePurpose as
|
|
3442
|
+
import { CertificatePurpose as CertificatePurpose3, generatePrivateKeyFile as generatePrivateKeyFile3, Subject as Subject5 } from "node-opcua-crypto";
|
|
3057
3443
|
import commandLineArgs from "command-line-args";
|
|
3058
3444
|
import commandLineUsage from "command-line-usage";
|
|
3059
3445
|
function get_offset_date(date, nbDays) {
|
|
@@ -3062,8 +3448,8 @@ function get_offset_date(date, nbDays) {
|
|
|
3062
3448
|
return d;
|
|
3063
3449
|
}
|
|
3064
3450
|
async function construct_CertificateAuthority2(subject) {
|
|
3065
|
-
|
|
3066
|
-
|
|
3451
|
+
assert11(typeof gLocalConfig.CAFolder === "string", "expecting a CAFolder in config");
|
|
3452
|
+
assert11(typeof gLocalConfig.keySize === "number", "expecting a keySize in config");
|
|
3067
3453
|
if (!g_certificateAuthority) {
|
|
3068
3454
|
g_certificateAuthority = new CertificateAuthority({
|
|
3069
3455
|
keySize: gLocalConfig.keySize,
|
|
@@ -3074,7 +3460,7 @@ async function construct_CertificateAuthority2(subject) {
|
|
|
3074
3460
|
}
|
|
3075
3461
|
}
|
|
3076
3462
|
async function construct_CertificateManager() {
|
|
3077
|
-
|
|
3463
|
+
assert11(typeof gLocalConfig.PKIFolder === "string", "expecting a PKIFolder in config");
|
|
3078
3464
|
if (!certificateManager) {
|
|
3079
3465
|
certificateManager = new CertificateManager({
|
|
3080
3466
|
keySize: gLocalConfig.keySize,
|
|
@@ -3085,35 +3471,35 @@ async function construct_CertificateManager() {
|
|
|
3085
3471
|
}
|
|
3086
3472
|
function default_template_content() {
|
|
3087
3473
|
if (process.pkg?.entrypoint) {
|
|
3088
|
-
const a =
|
|
3474
|
+
const a = fs11.readFileSync(path7.join(__dirname, "../../bin/pki_config.example.js"), "utf8");
|
|
3089
3475
|
return a;
|
|
3090
3476
|
}
|
|
3091
3477
|
function find_default_config_template() {
|
|
3092
3478
|
const rootFolder = find_module_root_folder();
|
|
3093
3479
|
const configName = "pki_config.example.js";
|
|
3094
3480
|
let default_config_template2 = path7.join(rootFolder, "bin", configName);
|
|
3095
|
-
if (!
|
|
3481
|
+
if (!fs11.existsSync(default_config_template2)) {
|
|
3096
3482
|
default_config_template2 = path7.join(__dirname, "..", configName);
|
|
3097
|
-
if (!
|
|
3483
|
+
if (!fs11.existsSync(default_config_template2)) {
|
|
3098
3484
|
default_config_template2 = path7.join(__dirname, `../bin/${configName}`);
|
|
3099
3485
|
}
|
|
3100
3486
|
}
|
|
3101
3487
|
return default_config_template2;
|
|
3102
3488
|
}
|
|
3103
3489
|
const default_config_template = find_default_config_template();
|
|
3104
|
-
|
|
3105
|
-
const default_config_template_content =
|
|
3490
|
+
assert11(fs11.existsSync(default_config_template));
|
|
3491
|
+
const default_config_template_content = fs11.readFileSync(default_config_template, "utf8");
|
|
3106
3492
|
return default_config_template_content;
|
|
3107
3493
|
}
|
|
3108
3494
|
function find_module_root_folder() {
|
|
3109
3495
|
let rootFolder = path7.join(__dirname);
|
|
3110
3496
|
for (let i = 0; i < 4; i++) {
|
|
3111
|
-
if (
|
|
3497
|
+
if (fs11.existsSync(path7.join(rootFolder, "package.json"))) {
|
|
3112
3498
|
return rootFolder;
|
|
3113
3499
|
}
|
|
3114
3500
|
rootFolder = path7.join(rootFolder, "..");
|
|
3115
3501
|
}
|
|
3116
|
-
|
|
3502
|
+
assert11(fs11.existsSync(path7.join(rootFolder, "package.json")), "root folder must have a package.json file");
|
|
3117
3503
|
return rootFolder;
|
|
3118
3504
|
}
|
|
3119
3505
|
async function readConfiguration(argv) {
|
|
@@ -3142,19 +3528,19 @@ async function readConfiguration(argv) {
|
|
|
3142
3528
|
return makePath(tmp);
|
|
3143
3529
|
}
|
|
3144
3530
|
certificateDir = argv.root;
|
|
3145
|
-
|
|
3531
|
+
assert11(typeof certificateDir === "string");
|
|
3146
3532
|
certificateDir = prepare(certificateDir);
|
|
3147
3533
|
mkdirRecursiveSync(certificateDir);
|
|
3148
|
-
|
|
3534
|
+
assert11(fs11.existsSync(certificateDir));
|
|
3149
3535
|
const default_config = path7.join(certificateDir, "config.js");
|
|
3150
|
-
if (!
|
|
3536
|
+
if (!fs11.existsSync(default_config)) {
|
|
3151
3537
|
debugLog(chalk7.yellow(" Creating default g_config file "), chalk7.cyan(default_config));
|
|
3152
3538
|
const default_config_template_content = default_template_content();
|
|
3153
|
-
|
|
3539
|
+
fs11.writeFileSync(default_config, default_config_template_content);
|
|
3154
3540
|
} else {
|
|
3155
3541
|
debugLog(chalk7.yellow(" using g_config file "), chalk7.cyan(default_config));
|
|
3156
3542
|
}
|
|
3157
|
-
if (!
|
|
3543
|
+
if (!fs11.existsSync(default_config)) {
|
|
3158
3544
|
debugLog(chalk7.redBright(" cannot find config file ", default_config));
|
|
3159
3545
|
}
|
|
3160
3546
|
const defaultRandomFile = path7.join(path7.dirname(default_config), "random.rnd");
|
|
@@ -3210,7 +3596,7 @@ async function readConfiguration(argv) {
|
|
|
3210
3596
|
}
|
|
3211
3597
|
}
|
|
3212
3598
|
async function createDefaultCertificate(base_name, prefix, key_length, applicationUri, dev) {
|
|
3213
|
-
|
|
3599
|
+
assert11(key_length === 1024 || key_length === 2048 || key_length === 3072 || key_length === 4096);
|
|
3214
3600
|
const private_key_file = makePath(base_name, `${prefix}key_${key_length}.pem`);
|
|
3215
3601
|
const public_key_file = makePath(base_name, `${prefix}public_key_${key_length}.pub`);
|
|
3216
3602
|
const certificate_file = makePath(base_name, `${prefix}cert_${key_length}.pem`);
|
|
@@ -3230,7 +3616,7 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3230
3616
|
}
|
|
3231
3617
|
const ip = [];
|
|
3232
3618
|
async function createCertificateIfNotExist(certificate, private_key, applicationUri2, startDate, validity) {
|
|
3233
|
-
if (
|
|
3619
|
+
if (fs11.existsSync(certificate)) {
|
|
3234
3620
|
warningLog(chalk7.yellow(" certificate"), chalk7.cyan(certificate), chalk7.yellow(" already exists => skipping"));
|
|
3235
3621
|
return "";
|
|
3236
3622
|
} else {
|
|
@@ -3249,7 +3635,7 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3249
3635
|
configFile,
|
|
3250
3636
|
dns: dns3,
|
|
3251
3637
|
ip: ip2,
|
|
3252
|
-
purpose:
|
|
3638
|
+
purpose: CertificatePurpose3.ForApplication
|
|
3253
3639
|
};
|
|
3254
3640
|
await createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFile, params);
|
|
3255
3641
|
return await g_certificateAuthority.signCertificateRequest(certificate, certificateSigningRequestFile, {
|
|
@@ -3273,7 +3659,7 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3273
3659
|
await g_certificateAuthority.revokeCertificate(certificate, {});
|
|
3274
3660
|
}
|
|
3275
3661
|
async function createPrivateKeyIfNotExist(privateKey, keyLength) {
|
|
3276
|
-
if (
|
|
3662
|
+
if (fs11.existsSync(privateKey)) {
|
|
3277
3663
|
warningLog(chalk7.yellow(" privateKey"), chalk7.cyan(privateKey), chalk7.yellow(" already exists => skipping"));
|
|
3278
3664
|
return;
|
|
3279
3665
|
} else {
|
|
@@ -3287,14 +3673,14 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3287
3673
|
displaySubtitle(` create Certificate ${certificate_file}`);
|
|
3288
3674
|
await createCertificateIfNotExist(certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
3289
3675
|
displaySubtitle(` create self signed Certificate ${self_signed_certificate_file}`);
|
|
3290
|
-
if (
|
|
3676
|
+
if (fs11.existsSync(self_signed_certificate_file)) {
|
|
3291
3677
|
return;
|
|
3292
3678
|
}
|
|
3293
3679
|
await createSelfSignedCertificate2(self_signed_certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
3294
3680
|
if (dev) {
|
|
3295
3681
|
await createCertificateIfNotExist(certificate_file_outofdate, private_key_file, applicationUri, two_years_ago, 365);
|
|
3296
3682
|
await createCertificateIfNotExist(certificate_file_not_active_yet, private_key_file, applicationUri, next_year, 365);
|
|
3297
|
-
if (!
|
|
3683
|
+
if (!fs11.existsSync(certificate_revoked)) {
|
|
3298
3684
|
const certificate = await createCertificateIfNotExist(
|
|
3299
3685
|
certificate_revoked,
|
|
3300
3686
|
private_key_file,
|
|
@@ -3316,9 +3702,9 @@ async function wrap(func) {
|
|
|
3316
3702
|
}
|
|
3317
3703
|
}
|
|
3318
3704
|
async function create_default_certificates(dev) {
|
|
3319
|
-
|
|
3705
|
+
assert11(gLocalConfig);
|
|
3320
3706
|
const base_name = gLocalConfig.certificateDir || "";
|
|
3321
|
-
|
|
3707
|
+
assert11(fs11.existsSync(base_name));
|
|
3322
3708
|
let clientURN;
|
|
3323
3709
|
let serverURN;
|
|
3324
3710
|
let discoveryServerURN;
|
|
@@ -3415,7 +3801,7 @@ ${epilog}`
|
|
|
3415
3801
|
}
|
|
3416
3802
|
if (command === "version") {
|
|
3417
3803
|
const rootFolder = find_module_root_folder();
|
|
3418
|
-
const pkg = JSON.parse(
|
|
3804
|
+
const pkg = JSON.parse(fs11.readFileSync(path7.join(rootFolder, "package.json"), "utf-8"));
|
|
3419
3805
|
console.log(pkg.version);
|
|
3420
3806
|
return;
|
|
3421
3807
|
}
|
|
@@ -3440,12 +3826,12 @@ ${epilog}`
|
|
|
3440
3826
|
await readConfiguration(local_argv);
|
|
3441
3827
|
if (local_argv.clean) {
|
|
3442
3828
|
displayTitle("Cleaning old certificates");
|
|
3443
|
-
|
|
3829
|
+
assert11(gLocalConfig);
|
|
3444
3830
|
const certificateDir = gLocalConfig.certificateDir || "";
|
|
3445
|
-
const files = await
|
|
3831
|
+
const files = await fs11.promises.readdir(certificateDir);
|
|
3446
3832
|
for (const file of files) {
|
|
3447
3833
|
if (file.includes(".pem") || file.includes(".pub")) {
|
|
3448
|
-
await
|
|
3834
|
+
await fs11.promises.unlink(path7.join(certificateDir, file));
|
|
3449
3835
|
}
|
|
3450
3836
|
}
|
|
3451
3837
|
mkdirRecursiveSync(certificateDir);
|
|
@@ -3552,7 +3938,7 @@ ${epilog}`
|
|
|
3552
3938
|
await readConfiguration(local_argv2);
|
|
3553
3939
|
await construct_CertificateManager();
|
|
3554
3940
|
await construct_CertificateAuthority2("");
|
|
3555
|
-
|
|
3941
|
+
assert11(fs11.existsSync(gLocalConfig.CAFolder || ""), " CA folder must exist");
|
|
3556
3942
|
gLocalConfig.privateKey = void 0;
|
|
3557
3943
|
gLocalConfig.subject = local_argv2.subject && local_argv2.subject.length > 1 ? local_argv2.subject : gLocalConfig.subject;
|
|
3558
3944
|
const csr_file = await certificateManager.createCertificateRequest(
|
|
@@ -3563,7 +3949,7 @@ ${epilog}`
|
|
|
3563
3949
|
}
|
|
3564
3950
|
warningLog(" csr_file = ", csr_file);
|
|
3565
3951
|
const certificate = csr_file.replace(".csr", ".pem");
|
|
3566
|
-
if (
|
|
3952
|
+
if (fs11.existsSync(certificate)) {
|
|
3567
3953
|
throw new Error(` File ${certificate} already exist`);
|
|
3568
3954
|
}
|
|
3569
3955
|
await g_certificateAuthority.signCertificateRequest(
|
|
@@ -3571,8 +3957,8 @@ ${epilog}`
|
|
|
3571
3957
|
csr_file,
|
|
3572
3958
|
gLocalConfig
|
|
3573
3959
|
);
|
|
3574
|
-
|
|
3575
|
-
|
|
3960
|
+
assert11(typeof gLocalConfig.outputFile === "string");
|
|
3961
|
+
fs11.writeFileSync(gLocalConfig.outputFile || "", fs11.readFileSync(certificate, "ascii"));
|
|
3576
3962
|
}
|
|
3577
3963
|
await wrap(async () => await command_certificate(local_argv));
|
|
3578
3964
|
return;
|
|
@@ -3593,7 +3979,7 @@ ${epilog}`
|
|
|
3593
3979
|
await wrap(async () => {
|
|
3594
3980
|
const certificate = path7.resolve(local_argv.certificateFile);
|
|
3595
3981
|
warningLog(chalk7.yellow(" Certificate to revoke : "), chalk7.cyan(certificate));
|
|
3596
|
-
if (!
|
|
3982
|
+
if (!fs11.existsSync(certificate)) {
|
|
3597
3983
|
throw new Error(`cannot find certificate to revoke ${certificate}`);
|
|
3598
3984
|
}
|
|
3599
3985
|
await readConfiguration(local_argv);
|
|
@@ -3640,11 +4026,11 @@ ${epilog}`
|
|
|
3640
4026
|
if (local_argv.help) return showHelp("csr", "create a certificate signing request", optionsDef);
|
|
3641
4027
|
await wrap(async () => {
|
|
3642
4028
|
await readConfiguration(local_argv);
|
|
3643
|
-
if (!
|
|
4029
|
+
if (!fs11.existsSync(gLocalConfig.PKIFolder || "")) {
|
|
3644
4030
|
warningLog("PKI folder must exist");
|
|
3645
4031
|
}
|
|
3646
4032
|
await construct_CertificateManager();
|
|
3647
|
-
if (!gLocalConfig.outputFile ||
|
|
4033
|
+
if (!gLocalConfig.outputFile || fs11.existsSync(gLocalConfig.outputFile)) {
|
|
3648
4034
|
throw new Error(` File ${gLocalConfig.outputFile} already exist`);
|
|
3649
4035
|
}
|
|
3650
4036
|
gLocalConfig.privateKey = void 0;
|
|
@@ -3659,8 +4045,8 @@ ${epilog}`
|
|
|
3659
4045
|
warningLog("please specify a output file");
|
|
3660
4046
|
return;
|
|
3661
4047
|
}
|
|
3662
|
-
const csr = await
|
|
3663
|
-
|
|
4048
|
+
const csr = await fs11.promises.readFile(internal_csr_file, "utf-8");
|
|
4049
|
+
fs11.writeFileSync(gLocalConfig.outputFile || "", csr, "utf-8");
|
|
3664
4050
|
warningLog("Subject = ", gLocalConfig.subject);
|
|
3665
4051
|
warningLog("applicationUri = ", gLocalConfig.applicationUri);
|
|
3666
4052
|
warningLog("altNames = ", gLocalConfig.altNames);
|
|
@@ -3688,16 +4074,16 @@ ${epilog}`
|
|
|
3688
4074
|
return showHelp("sign", "validate a certificate signing request and generate a certificate", optionsDef);
|
|
3689
4075
|
await wrap(async () => {
|
|
3690
4076
|
await readConfiguration(local_argv);
|
|
3691
|
-
if (!
|
|
4077
|
+
if (!fs11.existsSync(gLocalConfig.CAFolder || "")) {
|
|
3692
4078
|
throw new Error(`CA folder must exist:${gLocalConfig.CAFolder}`);
|
|
3693
4079
|
}
|
|
3694
4080
|
await construct_CertificateAuthority2("");
|
|
3695
4081
|
const csr_file = path7.resolve(local_argv.csr || "");
|
|
3696
|
-
if (!
|
|
4082
|
+
if (!fs11.existsSync(csr_file)) {
|
|
3697
4083
|
throw new Error(`Certificate signing request doesn't exist: ${csr_file}`);
|
|
3698
4084
|
}
|
|
3699
4085
|
const certificate = path7.resolve(local_argv.output || csr_file.replace(".csr", ".pem"));
|
|
3700
|
-
if (
|
|
4086
|
+
if (fs11.existsSync(certificate)) {
|
|
3701
4087
|
throw new Error(` File ${certificate} already exist`);
|
|
3702
4088
|
}
|
|
3703
4089
|
await g_certificateAuthority.signCertificateRequest(
|
|
@@ -3705,8 +4091,8 @@ ${epilog}`
|
|
|
3705
4091
|
csr_file,
|
|
3706
4092
|
gLocalConfig
|
|
3707
4093
|
);
|
|
3708
|
-
|
|
3709
|
-
|
|
4094
|
+
assert11(typeof gLocalConfig.outputFile === "string");
|
|
4095
|
+
fs11.writeFileSync(gLocalConfig.outputFile || "", fs11.readFileSync(certificate, "ascii"));
|
|
3710
4096
|
});
|
|
3711
4097
|
return;
|
|
3712
4098
|
}
|