node-opcua-pki 6.10.1 → 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 +512 -116
- 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 +643 -257
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +648 -259
- 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,39 +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
|
-
|
|
2474
|
-
|
|
2510
|
+
const caKeyExists = fs10.existsSync(path6.join(caRootDir, "private/cakey.pem"));
|
|
2511
|
+
const caCertExists = fs10.existsSync(path6.join(caRootDir, "public/cacert.pem"));
|
|
2512
|
+
if (caKeyExists && caCertExists && !config3.forceCA) {
|
|
2513
|
+
debugLog("CA private key and certificate already exist ... skipping");
|
|
2475
2514
|
return;
|
|
2476
2515
|
}
|
|
2516
|
+
if (caKeyExists && !caCertExists) {
|
|
2517
|
+
debugLog("CA private key exists but cacert.pem is missing \u2014 rebuilding CA");
|
|
2518
|
+
fs10.unlinkSync(path6.join(caRootDir, "private/cakey.pem"));
|
|
2519
|
+
const staleCsr = path6.join(caRootDir, "private/cakey.csr");
|
|
2520
|
+
if (fs10.existsSync(staleCsr)) {
|
|
2521
|
+
fs10.unlinkSync(staleCsr);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2477
2524
|
displayTitle("Create Certificate Authority (CA)");
|
|
2478
2525
|
const indexFileAttr = path6.join(caRootDir, "index.txt.attr");
|
|
2479
|
-
if (!
|
|
2480
|
-
await
|
|
2526
|
+
if (!fs10.existsSync(indexFileAttr)) {
|
|
2527
|
+
await fs10.promises.writeFile(indexFileAttr, "unique_subject = no");
|
|
2481
2528
|
}
|
|
2482
2529
|
const caConfigFile = certificateAuthority.configFile;
|
|
2483
2530
|
if (1) {
|
|
2484
2531
|
let data = configurationFileTemplate;
|
|
2485
2532
|
data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));
|
|
2486
|
-
await
|
|
2533
|
+
await fs10.promises.writeFile(caConfigFile, data);
|
|
2487
2534
|
}
|
|
2488
2535
|
const subjectOpt = ` -subj "${subject.toString()}" `;
|
|
2489
2536
|
processAltNames({});
|
|
2490
2537
|
const options = { cwd: caRootDir };
|
|
2491
2538
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
2492
|
-
const configOption = ` -config ${
|
|
2539
|
+
const configOption = ` -config ${q4(n5(configFile))}`;
|
|
2493
2540
|
const keySize = certificateAuthority.keySize;
|
|
2494
2541
|
const privateKeyFilename = path6.join(caRootDir, "private/cakey.pem");
|
|
2495
2542
|
const csrFilename = path6.join(caRootDir, "private/cakey.csr");
|
|
@@ -2497,14 +2544,26 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
2497
2544
|
await generatePrivateKeyFile2(privateKeyFilename, keySize);
|
|
2498
2545
|
displayTitle("Generate a certificate request for the CA key");
|
|
2499
2546
|
await execute_openssl(
|
|
2500
|
-
"req -new -sha256 -text -extensions v3_ca_req" + configOption + " -key " +
|
|
2501
|
-
options
|
|
2502
|
-
);
|
|
2503
|
-
displayTitle("Generate CA Certificate (self-signed)");
|
|
2504
|
-
await execute_openssl(
|
|
2505
|
-
" 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,
|
|
2506
2548
|
options
|
|
2507
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
|
+
}
|
|
2508
2567
|
displaySubtitle("generate initial CRL (Certificate Revocation List)");
|
|
2509
2568
|
await regenerateCrl(certificateAuthority.revocationList, configOption, options);
|
|
2510
2569
|
displayTitle("Create Certificate Authority (CA) ---> DONE");
|
|
@@ -2514,7 +2573,7 @@ async function regenerateCrl(revocationList, configOption, options) {
|
|
|
2514
2573
|
await execute_openssl(`ca -gencrl ${configOption} -out crl/revocation_list.crl`, options);
|
|
2515
2574
|
await execute_openssl("crl -in crl/revocation_list.crl -out crl/revocation_list.der -outform der", options);
|
|
2516
2575
|
displaySubtitle("Display (Certificate Revocation List)");
|
|
2517
|
-
await execute_openssl(`crl -in ${
|
|
2576
|
+
await execute_openssl(`crl -in ${q4(n5(revocationList))} -text -noout`, options);
|
|
2518
2577
|
}
|
|
2519
2578
|
function parseOpenSSLDate(dateStr) {
|
|
2520
2579
|
const raw = dateStr?.split(",")[0] ?? "";
|
|
@@ -2528,24 +2587,27 @@ function parseOpenSSLDate(dateStr) {
|
|
|
2528
2587
|
const sec = raw.substring(10, 12);
|
|
2529
2588
|
return `${year}-${month}-${day}T${hour}:${min}:${sec}Z`;
|
|
2530
2589
|
}
|
|
2531
|
-
var defaultSubject, configurationFileTemplate, config3,
|
|
2590
|
+
var defaultSubject, configurationFileTemplate, configurationFileSimpleTemplate2, config3, n5, q4, CertificateAuthority;
|
|
2532
2591
|
var init_certificate_authority = __esm({
|
|
2533
2592
|
"packages/node-opcua-pki/lib/ca/certificate_authority.ts"() {
|
|
2534
2593
|
"use strict";
|
|
2535
2594
|
init_esm_shims();
|
|
2595
|
+
init_toolbox_pfx();
|
|
2536
2596
|
init_toolbox();
|
|
2537
2597
|
init_with_openssl();
|
|
2598
|
+
init_simple_config_template_cnf();
|
|
2538
2599
|
init_ca_config_template_cnf();
|
|
2539
2600
|
defaultSubject = "/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA";
|
|
2540
2601
|
configurationFileTemplate = ca_config_template_cnf_default;
|
|
2602
|
+
configurationFileSimpleTemplate2 = simple_config_template_cnf_default;
|
|
2541
2603
|
config3 = {
|
|
2542
2604
|
certificateDir: "INVALID",
|
|
2543
2605
|
forceCA: false,
|
|
2544
2606
|
pkiDir: "INVALID"
|
|
2545
2607
|
};
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2608
|
+
n5 = makePath;
|
|
2609
|
+
q4 = quote;
|
|
2610
|
+
assert10(octetStringToIpAddress("c07b9179") === "192.123.145.121");
|
|
2549
2611
|
CertificateAuthority = class {
|
|
2550
2612
|
/** RSA key size used when generating the CA private key. */
|
|
2551
2613
|
keySize;
|
|
@@ -2553,12 +2615,15 @@ var init_certificate_authority = __esm({
|
|
|
2553
2615
|
location;
|
|
2554
2616
|
/** X.500 subject of the CA certificate. */
|
|
2555
2617
|
subject;
|
|
2618
|
+
/** @internal Parent CA (undefined for root CAs). */
|
|
2619
|
+
_issuerCA;
|
|
2556
2620
|
constructor(options) {
|
|
2557
|
-
|
|
2558
|
-
|
|
2621
|
+
assert10(Object.prototype.hasOwnProperty.call(options, "location"));
|
|
2622
|
+
assert10(Object.prototype.hasOwnProperty.call(options, "keySize"));
|
|
2559
2623
|
this.location = options.location;
|
|
2560
2624
|
this.keySize = options.keySize || 2048;
|
|
2561
2625
|
this.subject = new Subject4(options.subject || defaultSubject);
|
|
2626
|
+
this._issuerCA = options.issuerCA;
|
|
2562
2627
|
}
|
|
2563
2628
|
/** Absolute path to the CA root directory (alias for {@link location}). */
|
|
2564
2629
|
get rootDir() {
|
|
@@ -2572,6 +2637,18 @@ var init_certificate_authority = __esm({
|
|
|
2572
2637
|
get caCertificate() {
|
|
2573
2638
|
return makePath(this.rootDir, "./public/cacert.pem");
|
|
2574
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
|
+
}
|
|
2575
2652
|
/**
|
|
2576
2653
|
* Path to the current Certificate Revocation List in DER format.
|
|
2577
2654
|
* (`crl/revocation_list.der`)
|
|
@@ -2629,10 +2706,10 @@ var init_certificate_authority = __esm({
|
|
|
2629
2706
|
*/
|
|
2630
2707
|
getCRLDER() {
|
|
2631
2708
|
const crlPath = this.revocationListDER;
|
|
2632
|
-
if (!
|
|
2709
|
+
if (!fs10.existsSync(crlPath)) {
|
|
2633
2710
|
return Buffer.alloc(0);
|
|
2634
2711
|
}
|
|
2635
|
-
return
|
|
2712
|
+
return fs10.readFileSync(crlPath);
|
|
2636
2713
|
}
|
|
2637
2714
|
/**
|
|
2638
2715
|
* Return the current Certificate Revocation List as a
|
|
@@ -2642,10 +2719,10 @@ var init_certificate_authority = __esm({
|
|
|
2642
2719
|
*/
|
|
2643
2720
|
getCRLPEM() {
|
|
2644
2721
|
const crlPath = this.revocationList;
|
|
2645
|
-
if (!
|
|
2722
|
+
if (!fs10.existsSync(crlPath)) {
|
|
2646
2723
|
return "";
|
|
2647
2724
|
}
|
|
2648
|
-
const raw =
|
|
2725
|
+
const raw = fs10.readFileSync(crlPath, "utf-8");
|
|
2649
2726
|
const beginMarker = "-----BEGIN X509 CRL-----";
|
|
2650
2727
|
const idx = raw.indexOf(beginMarker);
|
|
2651
2728
|
if (idx > 0) {
|
|
@@ -2698,7 +2775,7 @@ var init_certificate_authority = __esm({
|
|
|
2698
2775
|
getCertificateBySerial(serial) {
|
|
2699
2776
|
const upper = serial.toUpperCase();
|
|
2700
2777
|
const certFile = path6.join(this.rootDir, "certs", `${upper}.pem`);
|
|
2701
|
-
if (!
|
|
2778
|
+
if (!fs10.existsSync(certFile)) {
|
|
2702
2779
|
return void 0;
|
|
2703
2780
|
}
|
|
2704
2781
|
const pem = readCertificatePEM(certFile);
|
|
@@ -2727,10 +2804,10 @@ var init_certificate_authority = __esm({
|
|
|
2727
2804
|
*/
|
|
2728
2805
|
_parseIndexTxt() {
|
|
2729
2806
|
const indexPath = this.indexFile;
|
|
2730
|
-
if (!
|
|
2807
|
+
if (!fs10.existsSync(indexPath)) {
|
|
2731
2808
|
return [];
|
|
2732
2809
|
}
|
|
2733
|
-
const content =
|
|
2810
|
+
const content = fs10.readFileSync(indexPath, "utf-8");
|
|
2734
2811
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
2735
2812
|
const records = [];
|
|
2736
2813
|
for (const line of lines) {
|
|
@@ -2784,25 +2861,145 @@ var init_certificate_authority = __esm({
|
|
|
2784
2861
|
* internally so that callers can work with in-memory
|
|
2785
2862
|
* buffers only.
|
|
2786
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
|
+
*
|
|
2787
2868
|
* @param csrDer - the CSR as a DER-encoded buffer
|
|
2788
|
-
* @param options - signing options
|
|
2789
|
-
* @param options.validity - certificate validity in days
|
|
2790
|
-
* (default: 365)
|
|
2869
|
+
* @param options - signing options and CA overrides
|
|
2791
2870
|
* @returns the signed certificate as a DER-encoded buffer
|
|
2792
2871
|
*/
|
|
2793
2872
|
async signCertificateRequestFromDER(csrDer, options) {
|
|
2794
2873
|
const validity = options?.validity ?? 365;
|
|
2795
|
-
const tmpDir = await
|
|
2874
|
+
const tmpDir = await fs10.promises.mkdtemp(path6.join(os4.tmpdir(), "pki-sign-"));
|
|
2796
2875
|
try {
|
|
2797
2876
|
const csrFile = path6.join(tmpDir, "request.csr");
|
|
2798
2877
|
const certFile = path6.join(tmpDir, "certificate.pem");
|
|
2799
2878
|
const csrPem = toPem2(csrDer, "CERTIFICATE REQUEST");
|
|
2800
|
-
await
|
|
2801
|
-
|
|
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);
|
|
2802
2887
|
const certPem = readCertificatePEM(certFile);
|
|
2803
2888
|
return convertPEMtoDER(certPem);
|
|
2804
2889
|
} finally {
|
|
2805
|
-
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, {
|
|
2806
3003
|
recursive: true,
|
|
2807
3004
|
force: true
|
|
2808
3005
|
});
|
|
@@ -2824,7 +3021,7 @@ var init_certificate_authority = __esm({
|
|
|
2824
3021
|
const info = exploreCertificate2(certDer);
|
|
2825
3022
|
const serial = info.tbsCertificate.serialNumber.replace(/:/g, "").toUpperCase();
|
|
2826
3023
|
const storedCertFile = path6.join(this.rootDir, "certs", `${serial}.pem`);
|
|
2827
|
-
if (!
|
|
3024
|
+
if (!fs10.existsSync(storedCertFile)) {
|
|
2828
3025
|
throw new Error(`Cannot revoke: no stored certificate found for serial ${serial} at ${storedCertFile}`);
|
|
2829
3026
|
}
|
|
2830
3027
|
await this.revokeCertificate(storedCertFile, {
|
|
@@ -2839,6 +3036,204 @@ var init_certificate_authority = __esm({
|
|
|
2839
3036
|
async initialize() {
|
|
2840
3037
|
await construct_CertificateAuthority(this);
|
|
2841
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
|
+
}
|
|
2842
3237
|
/**
|
|
2843
3238
|
* Rebuild the combined CA certificate + CRL file.
|
|
2844
3239
|
*
|
|
@@ -2848,13 +3243,13 @@ var init_certificate_authority = __esm({
|
|
|
2848
3243
|
*/
|
|
2849
3244
|
async constructCACertificateWithCRL() {
|
|
2850
3245
|
const cacertWithCRL = this.caCertificateWithCrl;
|
|
2851
|
-
if (
|
|
2852
|
-
await
|
|
3246
|
+
if (fs10.existsSync(this.revocationList)) {
|
|
3247
|
+
await fs10.promises.writeFile(
|
|
2853
3248
|
cacertWithCRL,
|
|
2854
|
-
|
|
3249
|
+
fs10.readFileSync(this.caCertificate, "utf8") + fs10.readFileSync(this.revocationList, "utf8")
|
|
2855
3250
|
);
|
|
2856
3251
|
} else {
|
|
2857
|
-
await
|
|
3252
|
+
await fs10.promises.writeFile(cacertWithCRL, fs10.readFileSync(this.caCertificate));
|
|
2858
3253
|
}
|
|
2859
3254
|
}
|
|
2860
3255
|
/**
|
|
@@ -2864,14 +3259,15 @@ var init_certificate_authority = __esm({
|
|
|
2864
3259
|
* @param certificate - path to the certificate file to extend
|
|
2865
3260
|
*/
|
|
2866
3261
|
async constructCertificateChain(certificate) {
|
|
2867
|
-
|
|
2868
|
-
|
|
3262
|
+
assert10(fs10.existsSync(certificate));
|
|
3263
|
+
assert10(fs10.existsSync(this.caCertificate));
|
|
2869
3264
|
debugLog(chalk6.yellow(" certificate file :"), chalk6.cyan(certificate));
|
|
2870
|
-
await
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
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);
|
|
2875
3271
|
}
|
|
2876
3272
|
/**
|
|
2877
3273
|
* Create a self-signed certificate using OpenSSL.
|
|
@@ -2881,8 +3277,8 @@ var init_certificate_authority = __esm({
|
|
|
2881
3277
|
* @param params - certificate parameters (subject, validity, SANs)
|
|
2882
3278
|
*/
|
|
2883
3279
|
async createSelfSignedCertificate(certificateFile, privateKey, params) {
|
|
2884
|
-
|
|
2885
|
-
|
|
3280
|
+
assert10(typeof privateKey === "string");
|
|
3281
|
+
assert10(fs10.existsSync(privateKey));
|
|
2886
3282
|
if (!certificateFileExist(certificateFile)) {
|
|
2887
3283
|
return;
|
|
2888
3284
|
}
|
|
@@ -2890,7 +3286,7 @@ var init_certificate_authority = __esm({
|
|
|
2890
3286
|
adjustApplicationUri(params);
|
|
2891
3287
|
processAltNames(params);
|
|
2892
3288
|
const csrFile = `${certificateFile}_csr`;
|
|
2893
|
-
|
|
3289
|
+
assert10(csrFile);
|
|
2894
3290
|
const configFile = generateStaticConfig(this.configFile, { cwd: this.rootDir });
|
|
2895
3291
|
const options = {
|
|
2896
3292
|
cwd: this.rootDir,
|
|
@@ -2901,19 +3297,19 @@ var init_certificate_authority = __esm({
|
|
|
2901
3297
|
const subjectOptions = subject && subject.length > 1 ? ` -subj ${subject} ` : "";
|
|
2902
3298
|
displaySubtitle("- the certificate signing request");
|
|
2903
3299
|
await execute_openssl(
|
|
2904
|
-
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " +
|
|
3300
|
+
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " + q4(n5(privateKey)) + " -out " + q4(n5(csrFile)),
|
|
2905
3301
|
options
|
|
2906
3302
|
);
|
|
2907
3303
|
displaySubtitle("- creating the self-signed certificate");
|
|
2908
3304
|
await execute_openssl(
|
|
2909
|
-
"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)),
|
|
2910
3306
|
options
|
|
2911
3307
|
);
|
|
2912
3308
|
displaySubtitle("- dump the certificate for a check");
|
|
2913
|
-
await execute_openssl(`x509 -in ${
|
|
3309
|
+
await execute_openssl(`x509 -in ${q4(n5(certificateFile))} -dates -fingerprint -purpose -noout`, {});
|
|
2914
3310
|
displaySubtitle("- verify self-signed certificate");
|
|
2915
|
-
await execute_openssl_no_failure(`verify -verbose -CAfile ${
|
|
2916
|
-
await
|
|
3311
|
+
await execute_openssl_no_failure(`verify -verbose -CAfile ${q4(n5(certificateFile))} ${q4(n5(certificateFile))}`, options);
|
|
3312
|
+
await fs10.promises.unlink(csrFile);
|
|
2917
3313
|
}
|
|
2918
3314
|
/**
|
|
2919
3315
|
* Revoke a certificate and regenerate the CRL.
|
|
@@ -2942,22 +3338,22 @@ var init_certificate_authority = __esm({
|
|
|
2942
3338
|
setEnv("ALTNAME", "");
|
|
2943
3339
|
const randomFile = path6.join(this.rootDir, "random.rnd");
|
|
2944
3340
|
setEnv("RANDFILE", randomFile);
|
|
2945
|
-
const configOption = ` -config ${
|
|
3341
|
+
const configOption = ` -config ${q4(n5(configFile))}`;
|
|
2946
3342
|
const reason = params.reason || "keyCompromise";
|
|
2947
|
-
|
|
3343
|
+
assert10(crlReasons.indexOf(reason) >= 0);
|
|
2948
3344
|
displayTitle(`Revoking certificate ${certificate}`);
|
|
2949
3345
|
displaySubtitle("Revoke certificate");
|
|
2950
|
-
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);
|
|
2951
3347
|
await regenerateCrl(this.revocationList, configOption, options);
|
|
2952
3348
|
displaySubtitle("Verify that certificate is revoked");
|
|
2953
3349
|
await execute_openssl_no_failure(
|
|
2954
|
-
"verify -verbose -CRLfile " +
|
|
3350
|
+
"verify -verbose -CRLfile " + q4(n5(this.revocationList)) + " -CAfile " + q4(n5(this.caCertificate)) + " -crl_check " + q4(n5(certificate)),
|
|
2955
3351
|
options
|
|
2956
3352
|
);
|
|
2957
3353
|
displaySubtitle("Produce CRL in DER form ");
|
|
2958
|
-
await execute_openssl(`crl -in ${
|
|
3354
|
+
await execute_openssl(`crl -in ${q4(n5(this.revocationList))} -out crl/revocation_list.der -outform der`, options);
|
|
2959
3355
|
displaySubtitle("Produce CRL in PEM form ");
|
|
2960
|
-
await execute_openssl(`crl -in ${
|
|
3356
|
+
await execute_openssl(`crl -in ${q4(n5(this.revocationList))} -out crl/revocation_list.pem -outform pem -text `, options);
|
|
2961
3357
|
}
|
|
2962
3358
|
/**
|
|
2963
3359
|
* Sign a Certificate Signing Request (CSR) with this CA.
|
|
@@ -2973,7 +3369,7 @@ var init_certificate_authority = __esm({
|
|
|
2973
3369
|
*/
|
|
2974
3370
|
async signCertificateRequest(certificate, certificateSigningRequestFilename, params1) {
|
|
2975
3371
|
await ensure_openssl_installed();
|
|
2976
|
-
|
|
3372
|
+
assert10(fs10.existsSync(certificateSigningRequestFilename));
|
|
2977
3373
|
if (!certificateFileExist(certificate)) {
|
|
2978
3374
|
return "";
|
|
2979
3375
|
}
|
|
@@ -3000,11 +3396,11 @@ var init_certificate_authority = __esm({
|
|
|
3000
3396
|
displaySubtitle("- then we ask the authority to sign the certificate signing request");
|
|
3001
3397
|
const configOption = ` -config ${configFile}`;
|
|
3002
3398
|
await execute_openssl(
|
|
3003
|
-
"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)),
|
|
3004
3400
|
options
|
|
3005
3401
|
);
|
|
3006
3402
|
displaySubtitle("- dump the certificate for a check");
|
|
3007
|
-
await execute_openssl(`x509 -in ${
|
|
3403
|
+
await execute_openssl(`x509 -in ${q4(n5(certificate))} -dates -fingerprint -purpose -noout`, options);
|
|
3008
3404
|
displaySubtitle("- construct CA certificate with CRL");
|
|
3009
3405
|
await this.constructCACertificateWithCRL();
|
|
3010
3406
|
displaySubtitle("- construct certificate chain");
|
|
@@ -3027,7 +3423,7 @@ var init_certificate_authority = __esm({
|
|
|
3027
3423
|
const _configOption = ` -config ${configFile}`;
|
|
3028
3424
|
_configOption;
|
|
3029
3425
|
await execute_openssl_no_failure(
|
|
3030
|
-
`verify -verbose -CAfile ${
|
|
3426
|
+
`verify -verbose -CAfile ${q4(n5(this.caCertificateWithCrl))} ${q4(n5(certificate))}`,
|
|
3031
3427
|
options
|
|
3032
3428
|
);
|
|
3033
3429
|
}
|
|
@@ -3037,13 +3433,13 @@ var init_certificate_authority = __esm({
|
|
|
3037
3433
|
});
|
|
3038
3434
|
|
|
3039
3435
|
// packages/node-opcua-pki/lib/ca/crypto_create_CA.ts
|
|
3040
|
-
import
|
|
3041
|
-
import
|
|
3436
|
+
import assert11 from "assert";
|
|
3437
|
+
import fs11 from "fs";
|
|
3042
3438
|
import { createRequire } from "module";
|
|
3043
3439
|
import os5 from "os";
|
|
3044
3440
|
import path7 from "path";
|
|
3045
3441
|
import chalk7 from "chalk";
|
|
3046
|
-
import { CertificatePurpose as
|
|
3442
|
+
import { CertificatePurpose as CertificatePurpose3, generatePrivateKeyFile as generatePrivateKeyFile3, Subject as Subject5 } from "node-opcua-crypto";
|
|
3047
3443
|
import commandLineArgs from "command-line-args";
|
|
3048
3444
|
import commandLineUsage from "command-line-usage";
|
|
3049
3445
|
function get_offset_date(date, nbDays) {
|
|
@@ -3052,8 +3448,8 @@ function get_offset_date(date, nbDays) {
|
|
|
3052
3448
|
return d;
|
|
3053
3449
|
}
|
|
3054
3450
|
async function construct_CertificateAuthority2(subject) {
|
|
3055
|
-
|
|
3056
|
-
|
|
3451
|
+
assert11(typeof gLocalConfig.CAFolder === "string", "expecting a CAFolder in config");
|
|
3452
|
+
assert11(typeof gLocalConfig.keySize === "number", "expecting a keySize in config");
|
|
3057
3453
|
if (!g_certificateAuthority) {
|
|
3058
3454
|
g_certificateAuthority = new CertificateAuthority({
|
|
3059
3455
|
keySize: gLocalConfig.keySize,
|
|
@@ -3064,7 +3460,7 @@ async function construct_CertificateAuthority2(subject) {
|
|
|
3064
3460
|
}
|
|
3065
3461
|
}
|
|
3066
3462
|
async function construct_CertificateManager() {
|
|
3067
|
-
|
|
3463
|
+
assert11(typeof gLocalConfig.PKIFolder === "string", "expecting a PKIFolder in config");
|
|
3068
3464
|
if (!certificateManager) {
|
|
3069
3465
|
certificateManager = new CertificateManager({
|
|
3070
3466
|
keySize: gLocalConfig.keySize,
|
|
@@ -3075,35 +3471,35 @@ async function construct_CertificateManager() {
|
|
|
3075
3471
|
}
|
|
3076
3472
|
function default_template_content() {
|
|
3077
3473
|
if (process.pkg?.entrypoint) {
|
|
3078
|
-
const a =
|
|
3474
|
+
const a = fs11.readFileSync(path7.join(__dirname, "../../bin/pki_config.example.js"), "utf8");
|
|
3079
3475
|
return a;
|
|
3080
3476
|
}
|
|
3081
3477
|
function find_default_config_template() {
|
|
3082
3478
|
const rootFolder = find_module_root_folder();
|
|
3083
3479
|
const configName = "pki_config.example.js";
|
|
3084
3480
|
let default_config_template2 = path7.join(rootFolder, "bin", configName);
|
|
3085
|
-
if (!
|
|
3481
|
+
if (!fs11.existsSync(default_config_template2)) {
|
|
3086
3482
|
default_config_template2 = path7.join(__dirname, "..", configName);
|
|
3087
|
-
if (!
|
|
3483
|
+
if (!fs11.existsSync(default_config_template2)) {
|
|
3088
3484
|
default_config_template2 = path7.join(__dirname, `../bin/${configName}`);
|
|
3089
3485
|
}
|
|
3090
3486
|
}
|
|
3091
3487
|
return default_config_template2;
|
|
3092
3488
|
}
|
|
3093
3489
|
const default_config_template = find_default_config_template();
|
|
3094
|
-
|
|
3095
|
-
const default_config_template_content =
|
|
3490
|
+
assert11(fs11.existsSync(default_config_template));
|
|
3491
|
+
const default_config_template_content = fs11.readFileSync(default_config_template, "utf8");
|
|
3096
3492
|
return default_config_template_content;
|
|
3097
3493
|
}
|
|
3098
3494
|
function find_module_root_folder() {
|
|
3099
3495
|
let rootFolder = path7.join(__dirname);
|
|
3100
3496
|
for (let i = 0; i < 4; i++) {
|
|
3101
|
-
if (
|
|
3497
|
+
if (fs11.existsSync(path7.join(rootFolder, "package.json"))) {
|
|
3102
3498
|
return rootFolder;
|
|
3103
3499
|
}
|
|
3104
3500
|
rootFolder = path7.join(rootFolder, "..");
|
|
3105
3501
|
}
|
|
3106
|
-
|
|
3502
|
+
assert11(fs11.existsSync(path7.join(rootFolder, "package.json")), "root folder must have a package.json file");
|
|
3107
3503
|
return rootFolder;
|
|
3108
3504
|
}
|
|
3109
3505
|
async function readConfiguration(argv) {
|
|
@@ -3132,19 +3528,19 @@ async function readConfiguration(argv) {
|
|
|
3132
3528
|
return makePath(tmp);
|
|
3133
3529
|
}
|
|
3134
3530
|
certificateDir = argv.root;
|
|
3135
|
-
|
|
3531
|
+
assert11(typeof certificateDir === "string");
|
|
3136
3532
|
certificateDir = prepare(certificateDir);
|
|
3137
3533
|
mkdirRecursiveSync(certificateDir);
|
|
3138
|
-
|
|
3534
|
+
assert11(fs11.existsSync(certificateDir));
|
|
3139
3535
|
const default_config = path7.join(certificateDir, "config.js");
|
|
3140
|
-
if (!
|
|
3536
|
+
if (!fs11.existsSync(default_config)) {
|
|
3141
3537
|
debugLog(chalk7.yellow(" Creating default g_config file "), chalk7.cyan(default_config));
|
|
3142
3538
|
const default_config_template_content = default_template_content();
|
|
3143
|
-
|
|
3539
|
+
fs11.writeFileSync(default_config, default_config_template_content);
|
|
3144
3540
|
} else {
|
|
3145
3541
|
debugLog(chalk7.yellow(" using g_config file "), chalk7.cyan(default_config));
|
|
3146
3542
|
}
|
|
3147
|
-
if (!
|
|
3543
|
+
if (!fs11.existsSync(default_config)) {
|
|
3148
3544
|
debugLog(chalk7.redBright(" cannot find config file ", default_config));
|
|
3149
3545
|
}
|
|
3150
3546
|
const defaultRandomFile = path7.join(path7.dirname(default_config), "random.rnd");
|
|
@@ -3200,7 +3596,7 @@ async function readConfiguration(argv) {
|
|
|
3200
3596
|
}
|
|
3201
3597
|
}
|
|
3202
3598
|
async function createDefaultCertificate(base_name, prefix, key_length, applicationUri, dev) {
|
|
3203
|
-
|
|
3599
|
+
assert11(key_length === 1024 || key_length === 2048 || key_length === 3072 || key_length === 4096);
|
|
3204
3600
|
const private_key_file = makePath(base_name, `${prefix}key_${key_length}.pem`);
|
|
3205
3601
|
const public_key_file = makePath(base_name, `${prefix}public_key_${key_length}.pub`);
|
|
3206
3602
|
const certificate_file = makePath(base_name, `${prefix}cert_${key_length}.pem`);
|
|
@@ -3220,7 +3616,7 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3220
3616
|
}
|
|
3221
3617
|
const ip = [];
|
|
3222
3618
|
async function createCertificateIfNotExist(certificate, private_key, applicationUri2, startDate, validity) {
|
|
3223
|
-
if (
|
|
3619
|
+
if (fs11.existsSync(certificate)) {
|
|
3224
3620
|
warningLog(chalk7.yellow(" certificate"), chalk7.cyan(certificate), chalk7.yellow(" already exists => skipping"));
|
|
3225
3621
|
return "";
|
|
3226
3622
|
} else {
|
|
@@ -3239,7 +3635,7 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3239
3635
|
configFile,
|
|
3240
3636
|
dns: dns3,
|
|
3241
3637
|
ip: ip2,
|
|
3242
|
-
purpose:
|
|
3638
|
+
purpose: CertificatePurpose3.ForApplication
|
|
3243
3639
|
};
|
|
3244
3640
|
await createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFile, params);
|
|
3245
3641
|
return await g_certificateAuthority.signCertificateRequest(certificate, certificateSigningRequestFile, {
|
|
@@ -3263,7 +3659,7 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3263
3659
|
await g_certificateAuthority.revokeCertificate(certificate, {});
|
|
3264
3660
|
}
|
|
3265
3661
|
async function createPrivateKeyIfNotExist(privateKey, keyLength) {
|
|
3266
|
-
if (
|
|
3662
|
+
if (fs11.existsSync(privateKey)) {
|
|
3267
3663
|
warningLog(chalk7.yellow(" privateKey"), chalk7.cyan(privateKey), chalk7.yellow(" already exists => skipping"));
|
|
3268
3664
|
return;
|
|
3269
3665
|
} else {
|
|
@@ -3277,14 +3673,14 @@ async function createDefaultCertificate(base_name, prefix, key_length, applicati
|
|
|
3277
3673
|
displaySubtitle(` create Certificate ${certificate_file}`);
|
|
3278
3674
|
await createCertificateIfNotExist(certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
3279
3675
|
displaySubtitle(` create self signed Certificate ${self_signed_certificate_file}`);
|
|
3280
|
-
if (
|
|
3676
|
+
if (fs11.existsSync(self_signed_certificate_file)) {
|
|
3281
3677
|
return;
|
|
3282
3678
|
}
|
|
3283
3679
|
await createSelfSignedCertificate2(self_signed_certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
3284
3680
|
if (dev) {
|
|
3285
3681
|
await createCertificateIfNotExist(certificate_file_outofdate, private_key_file, applicationUri, two_years_ago, 365);
|
|
3286
3682
|
await createCertificateIfNotExist(certificate_file_not_active_yet, private_key_file, applicationUri, next_year, 365);
|
|
3287
|
-
if (!
|
|
3683
|
+
if (!fs11.existsSync(certificate_revoked)) {
|
|
3288
3684
|
const certificate = await createCertificateIfNotExist(
|
|
3289
3685
|
certificate_revoked,
|
|
3290
3686
|
private_key_file,
|
|
@@ -3306,9 +3702,9 @@ async function wrap(func) {
|
|
|
3306
3702
|
}
|
|
3307
3703
|
}
|
|
3308
3704
|
async function create_default_certificates(dev) {
|
|
3309
|
-
|
|
3705
|
+
assert11(gLocalConfig);
|
|
3310
3706
|
const base_name = gLocalConfig.certificateDir || "";
|
|
3311
|
-
|
|
3707
|
+
assert11(fs11.existsSync(base_name));
|
|
3312
3708
|
let clientURN;
|
|
3313
3709
|
let serverURN;
|
|
3314
3710
|
let discoveryServerURN;
|
|
@@ -3405,7 +3801,7 @@ ${epilog}`
|
|
|
3405
3801
|
}
|
|
3406
3802
|
if (command === "version") {
|
|
3407
3803
|
const rootFolder = find_module_root_folder();
|
|
3408
|
-
const pkg = JSON.parse(
|
|
3804
|
+
const pkg = JSON.parse(fs11.readFileSync(path7.join(rootFolder, "package.json"), "utf-8"));
|
|
3409
3805
|
console.log(pkg.version);
|
|
3410
3806
|
return;
|
|
3411
3807
|
}
|
|
@@ -3430,12 +3826,12 @@ ${epilog}`
|
|
|
3430
3826
|
await readConfiguration(local_argv);
|
|
3431
3827
|
if (local_argv.clean) {
|
|
3432
3828
|
displayTitle("Cleaning old certificates");
|
|
3433
|
-
|
|
3829
|
+
assert11(gLocalConfig);
|
|
3434
3830
|
const certificateDir = gLocalConfig.certificateDir || "";
|
|
3435
|
-
const files = await
|
|
3831
|
+
const files = await fs11.promises.readdir(certificateDir);
|
|
3436
3832
|
for (const file of files) {
|
|
3437
3833
|
if (file.includes(".pem") || file.includes(".pub")) {
|
|
3438
|
-
await
|
|
3834
|
+
await fs11.promises.unlink(path7.join(certificateDir, file));
|
|
3439
3835
|
}
|
|
3440
3836
|
}
|
|
3441
3837
|
mkdirRecursiveSync(certificateDir);
|
|
@@ -3542,7 +3938,7 @@ ${epilog}`
|
|
|
3542
3938
|
await readConfiguration(local_argv2);
|
|
3543
3939
|
await construct_CertificateManager();
|
|
3544
3940
|
await construct_CertificateAuthority2("");
|
|
3545
|
-
|
|
3941
|
+
assert11(fs11.existsSync(gLocalConfig.CAFolder || ""), " CA folder must exist");
|
|
3546
3942
|
gLocalConfig.privateKey = void 0;
|
|
3547
3943
|
gLocalConfig.subject = local_argv2.subject && local_argv2.subject.length > 1 ? local_argv2.subject : gLocalConfig.subject;
|
|
3548
3944
|
const csr_file = await certificateManager.createCertificateRequest(
|
|
@@ -3553,7 +3949,7 @@ ${epilog}`
|
|
|
3553
3949
|
}
|
|
3554
3950
|
warningLog(" csr_file = ", csr_file);
|
|
3555
3951
|
const certificate = csr_file.replace(".csr", ".pem");
|
|
3556
|
-
if (
|
|
3952
|
+
if (fs11.existsSync(certificate)) {
|
|
3557
3953
|
throw new Error(` File ${certificate} already exist`);
|
|
3558
3954
|
}
|
|
3559
3955
|
await g_certificateAuthority.signCertificateRequest(
|
|
@@ -3561,8 +3957,8 @@ ${epilog}`
|
|
|
3561
3957
|
csr_file,
|
|
3562
3958
|
gLocalConfig
|
|
3563
3959
|
);
|
|
3564
|
-
|
|
3565
|
-
|
|
3960
|
+
assert11(typeof gLocalConfig.outputFile === "string");
|
|
3961
|
+
fs11.writeFileSync(gLocalConfig.outputFile || "", fs11.readFileSync(certificate, "ascii"));
|
|
3566
3962
|
}
|
|
3567
3963
|
await wrap(async () => await command_certificate(local_argv));
|
|
3568
3964
|
return;
|
|
@@ -3583,7 +3979,7 @@ ${epilog}`
|
|
|
3583
3979
|
await wrap(async () => {
|
|
3584
3980
|
const certificate = path7.resolve(local_argv.certificateFile);
|
|
3585
3981
|
warningLog(chalk7.yellow(" Certificate to revoke : "), chalk7.cyan(certificate));
|
|
3586
|
-
if (!
|
|
3982
|
+
if (!fs11.existsSync(certificate)) {
|
|
3587
3983
|
throw new Error(`cannot find certificate to revoke ${certificate}`);
|
|
3588
3984
|
}
|
|
3589
3985
|
await readConfiguration(local_argv);
|
|
@@ -3630,11 +4026,11 @@ ${epilog}`
|
|
|
3630
4026
|
if (local_argv.help) return showHelp("csr", "create a certificate signing request", optionsDef);
|
|
3631
4027
|
await wrap(async () => {
|
|
3632
4028
|
await readConfiguration(local_argv);
|
|
3633
|
-
if (!
|
|
4029
|
+
if (!fs11.existsSync(gLocalConfig.PKIFolder || "")) {
|
|
3634
4030
|
warningLog("PKI folder must exist");
|
|
3635
4031
|
}
|
|
3636
4032
|
await construct_CertificateManager();
|
|
3637
|
-
if (!gLocalConfig.outputFile ||
|
|
4033
|
+
if (!gLocalConfig.outputFile || fs11.existsSync(gLocalConfig.outputFile)) {
|
|
3638
4034
|
throw new Error(` File ${gLocalConfig.outputFile} already exist`);
|
|
3639
4035
|
}
|
|
3640
4036
|
gLocalConfig.privateKey = void 0;
|
|
@@ -3649,8 +4045,8 @@ ${epilog}`
|
|
|
3649
4045
|
warningLog("please specify a output file");
|
|
3650
4046
|
return;
|
|
3651
4047
|
}
|
|
3652
|
-
const csr = await
|
|
3653
|
-
|
|
4048
|
+
const csr = await fs11.promises.readFile(internal_csr_file, "utf-8");
|
|
4049
|
+
fs11.writeFileSync(gLocalConfig.outputFile || "", csr, "utf-8");
|
|
3654
4050
|
warningLog("Subject = ", gLocalConfig.subject);
|
|
3655
4051
|
warningLog("applicationUri = ", gLocalConfig.applicationUri);
|
|
3656
4052
|
warningLog("altNames = ", gLocalConfig.altNames);
|
|
@@ -3678,16 +4074,16 @@ ${epilog}`
|
|
|
3678
4074
|
return showHelp("sign", "validate a certificate signing request and generate a certificate", optionsDef);
|
|
3679
4075
|
await wrap(async () => {
|
|
3680
4076
|
await readConfiguration(local_argv);
|
|
3681
|
-
if (!
|
|
4077
|
+
if (!fs11.existsSync(gLocalConfig.CAFolder || "")) {
|
|
3682
4078
|
throw new Error(`CA folder must exist:${gLocalConfig.CAFolder}`);
|
|
3683
4079
|
}
|
|
3684
4080
|
await construct_CertificateAuthority2("");
|
|
3685
4081
|
const csr_file = path7.resolve(local_argv.csr || "");
|
|
3686
|
-
if (!
|
|
4082
|
+
if (!fs11.existsSync(csr_file)) {
|
|
3687
4083
|
throw new Error(`Certificate signing request doesn't exist: ${csr_file}`);
|
|
3688
4084
|
}
|
|
3689
4085
|
const certificate = path7.resolve(local_argv.output || csr_file.replace(".csr", ".pem"));
|
|
3690
|
-
if (
|
|
4086
|
+
if (fs11.existsSync(certificate)) {
|
|
3691
4087
|
throw new Error(` File ${certificate} already exist`);
|
|
3692
4088
|
}
|
|
3693
4089
|
await g_certificateAuthority.signCertificateRequest(
|
|
@@ -3695,8 +4091,8 @@ ${epilog}`
|
|
|
3695
4091
|
csr_file,
|
|
3696
4092
|
gLocalConfig
|
|
3697
4093
|
);
|
|
3698
|
-
|
|
3699
|
-
|
|
4094
|
+
assert11(typeof gLocalConfig.outputFile === "string");
|
|
4095
|
+
fs11.writeFileSync(gLocalConfig.outputFile || "", fs11.readFileSync(certificate, "ascii"));
|
|
3700
4096
|
});
|
|
3701
4097
|
return;
|
|
3702
4098
|
}
|