node-opcua-pki 6.2.0 → 6.4.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/index.d.mts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +202 -157
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +203 -157
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/readme.md +24 -7
package/dist/index.js
CHANGED
|
@@ -1132,7 +1132,7 @@ var CertificateAuthority = class {
|
|
|
1132
1132
|
};
|
|
1133
1133
|
|
|
1134
1134
|
// packages/node-opcua-pki/lib/ca/crypto_create_CA.ts
|
|
1135
|
-
var
|
|
1135
|
+
var import_node_assert10 = __toESM(require("assert"));
|
|
1136
1136
|
var import_node_fs10 = __toESM(require("fs"));
|
|
1137
1137
|
var import_node_module = require("module");
|
|
1138
1138
|
var import_node_os4 = __toESM(require("os"));
|
|
@@ -1213,7 +1213,6 @@ function getFullyQualifiedDomainName(optional_max_length) {
|
|
|
1213
1213
|
prepareFQDN();
|
|
1214
1214
|
|
|
1215
1215
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1216
|
-
var import_node_assert10 = __toESM(require("assert"));
|
|
1217
1216
|
var import_node_fs9 = __toESM(require("fs"));
|
|
1218
1217
|
var import_node_path6 = __toESM(require("path"));
|
|
1219
1218
|
var import_global_mutex = require("@ster5/global-mutex");
|
|
@@ -1298,6 +1297,12 @@ var simple_config_template_cnf_default = config3;
|
|
|
1298
1297
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1299
1298
|
var configurationFileSimpleTemplate = simple_config_template_cnf_default;
|
|
1300
1299
|
var fsWriteFile = import_node_fs9.default.promises.writeFile;
|
|
1300
|
+
function getOrComputeInfo(entry) {
|
|
1301
|
+
if (!entry.info) {
|
|
1302
|
+
entry.info = (0, import_node_opcua_crypto5.exploreCertificate)(entry.certificate);
|
|
1303
|
+
}
|
|
1304
|
+
return entry.info;
|
|
1305
|
+
}
|
|
1301
1306
|
var VerificationStatus = /* @__PURE__ */ ((VerificationStatus2) => {
|
|
1302
1307
|
VerificationStatus2["BadCertificateInvalid"] = "BadCertificateInvalid";
|
|
1303
1308
|
VerificationStatus2["BadSecurityChecksFailed"] = "BadSecurityChecksFailed";
|
|
@@ -1335,11 +1340,10 @@ function buildIdealCertificateName(certificate) {
|
|
|
1335
1340
|
}
|
|
1336
1341
|
}
|
|
1337
1342
|
function findMatchingIssuerKey(entries, wantedIssuerKey) {
|
|
1338
|
-
|
|
1339
|
-
const info = (
|
|
1343
|
+
return entries.filter((entry) => {
|
|
1344
|
+
const info = getOrComputeInfo(entry);
|
|
1340
1345
|
return info.tbsCertificate.extensions && info.tbsCertificate.extensions.subjectKeyIdentifier === wantedIssuerKey;
|
|
1341
1346
|
});
|
|
1342
|
-
return selected;
|
|
1343
1347
|
}
|
|
1344
1348
|
function isSelfSigned2(info) {
|
|
1345
1349
|
return info.tbsCertificate.extensions?.subjectKeyIdentifier === info.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;
|
|
@@ -1385,26 +1389,35 @@ var CertificateManagerState = /* @__PURE__ */ ((CertificateManagerState2) => {
|
|
|
1385
1389
|
var CertificateManager = class {
|
|
1386
1390
|
untrustUnknownCertificate = true;
|
|
1387
1391
|
state = 0 /* Uninitialized */;
|
|
1392
|
+
/** @deprecated Use {@link folderPollingInterval} instead (typo fix). */
|
|
1388
1393
|
folderPoolingInterval = 5e3;
|
|
1394
|
+
/** Interval in milliseconds for file-system polling (when enabled). */
|
|
1395
|
+
get folderPollingInterval() {
|
|
1396
|
+
return this.folderPoolingInterval;
|
|
1397
|
+
}
|
|
1398
|
+
set folderPollingInterval(value) {
|
|
1399
|
+
this.folderPoolingInterval = value;
|
|
1400
|
+
}
|
|
1389
1401
|
keySize;
|
|
1390
1402
|
location;
|
|
1391
1403
|
_watchers = [];
|
|
1392
1404
|
_readCertificatesCalled = false;
|
|
1393
|
-
_filenameToHash =
|
|
1405
|
+
_filenameToHash = /* @__PURE__ */ new Map();
|
|
1406
|
+
_initializingPromise;
|
|
1394
1407
|
_thumbs = {
|
|
1395
|
-
rejected:
|
|
1396
|
-
trusted:
|
|
1408
|
+
rejected: /* @__PURE__ */ new Map(),
|
|
1409
|
+
trusted: /* @__PURE__ */ new Map(),
|
|
1397
1410
|
issuers: {
|
|
1398
|
-
certs:
|
|
1411
|
+
certs: /* @__PURE__ */ new Map()
|
|
1399
1412
|
},
|
|
1400
|
-
crl:
|
|
1401
|
-
issuersCrl:
|
|
1413
|
+
crl: /* @__PURE__ */ new Map(),
|
|
1414
|
+
issuersCrl: /* @__PURE__ */ new Map()
|
|
1402
1415
|
};
|
|
1403
1416
|
constructor(options) {
|
|
1404
1417
|
options.keySize = options.keySize || 2048;
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1418
|
+
if (!options.location) {
|
|
1419
|
+
throw new Error("CertificateManager: missing 'location' option");
|
|
1420
|
+
}
|
|
1408
1421
|
this.location = makePath(options.location, "");
|
|
1409
1422
|
this.keySize = options.keySize;
|
|
1410
1423
|
mkdirRecursiveSync(options.location);
|
|
@@ -1432,15 +1445,11 @@ var CertificateManager = class {
|
|
|
1432
1445
|
await this.initialize();
|
|
1433
1446
|
let status = await this._checkRejectedOrTrusted(certificate);
|
|
1434
1447
|
if (status === "unknown") {
|
|
1435
|
-
(0, import_node_assert10.default)(certificate instanceof Buffer);
|
|
1436
1448
|
const pem = (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE");
|
|
1437
1449
|
const fingerprint2 = makeFingerprint(certificate);
|
|
1438
1450
|
const filename = import_node_path6.default.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
1439
1451
|
await import_node_fs9.default.promises.writeFile(filename, pem);
|
|
1440
|
-
this._thumbs.rejected
|
|
1441
|
-
certificate,
|
|
1442
|
-
filename
|
|
1443
|
-
};
|
|
1452
|
+
this._thumbs.rejected.set(fingerprint2, { certificate, filename });
|
|
1444
1453
|
status = "rejected";
|
|
1445
1454
|
}
|
|
1446
1455
|
return status;
|
|
@@ -1491,30 +1500,27 @@ var CertificateManager = class {
|
|
|
1491
1500
|
*/
|
|
1492
1501
|
async isCertificateTrusted(certificate) {
|
|
1493
1502
|
const fingerprint2 = makeFingerprint(certificate);
|
|
1494
|
-
|
|
1495
|
-
if (certificateInTrust) {
|
|
1503
|
+
if (this._thumbs.trusted.has(fingerprint2)) {
|
|
1496
1504
|
return "Good";
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
if (!
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
);
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
}
|
|
1507
|
-
try {
|
|
1508
|
-
const _certificateInfo = (0, import_node_opcua_crypto5.exploreCertificateInfo)(certificate);
|
|
1509
|
-
_certificateInfo;
|
|
1510
|
-
} catch (_err) {
|
|
1511
|
-
return "BadCertificateInvalid";
|
|
1512
|
-
}
|
|
1513
|
-
debugLog("certificate has never been seen before and is now rejected (untrusted) ", certificateFilenameInRejected);
|
|
1514
|
-
await fsWriteFile(certificateFilenameInRejected, (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE"));
|
|
1505
|
+
}
|
|
1506
|
+
if (!this._thumbs.rejected.has(fingerprint2)) {
|
|
1507
|
+
if (!this.untrustUnknownCertificate) {
|
|
1508
|
+
return "Good";
|
|
1509
|
+
}
|
|
1510
|
+
try {
|
|
1511
|
+
(0, import_node_opcua_crypto5.exploreCertificateInfo)(certificate);
|
|
1512
|
+
} catch (_err) {
|
|
1513
|
+
return "BadCertificateInvalid";
|
|
1515
1514
|
}
|
|
1516
|
-
|
|
1515
|
+
const filename = import_node_path6.default.join(
|
|
1516
|
+
this.rejectedFolder,
|
|
1517
|
+
`${buildIdealCertificateName(certificate)}.pem`
|
|
1518
|
+
);
|
|
1519
|
+
debugLog("certificate has never been seen before and is now rejected (untrusted) ", filename);
|
|
1520
|
+
await fsWriteFile(filename, (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE"));
|
|
1521
|
+
this._thumbs.rejected.set(fingerprint2, { certificate, filename });
|
|
1517
1522
|
}
|
|
1523
|
+
return "BadCertificateUntrusted";
|
|
1518
1524
|
}
|
|
1519
1525
|
async _innerVerifyCertificateAsync(certificate, _isIssuer, level, options) {
|
|
1520
1526
|
if (level >= 5) {
|
|
@@ -1609,7 +1615,7 @@ var CertificateManager = class {
|
|
|
1609
1615
|
return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
|
|
1610
1616
|
}
|
|
1611
1617
|
const _c2 = chain[1] ? (0, import_node_opcua_crypto5.exploreCertificateInfo)(chain[1]) : "non";
|
|
1612
|
-
_c2;
|
|
1618
|
+
debugLog("chain[1] info=", _c2);
|
|
1613
1619
|
const certificateInfo = (0, import_node_opcua_crypto5.exploreCertificateInfo)(certificate);
|
|
1614
1620
|
const now = /* @__PURE__ */ new Date();
|
|
1615
1621
|
let isTimeInvalid = false;
|
|
@@ -1632,7 +1638,6 @@ var CertificateManager = class {
|
|
|
1632
1638
|
if (status === "trusted") {
|
|
1633
1639
|
return isTimeInvalid ? "BadCertificateTimeInvalid" /* BadCertificateTimeInvalid */ : "Good" /* Good */;
|
|
1634
1640
|
}
|
|
1635
|
-
(0, import_node_assert10.default)(status === "unknown");
|
|
1636
1641
|
if (hasIssuerKey) {
|
|
1637
1642
|
if (!hasTrustedIssuer) {
|
|
1638
1643
|
return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
|
|
@@ -1675,7 +1680,9 @@ var CertificateManager = class {
|
|
|
1675
1680
|
return;
|
|
1676
1681
|
}
|
|
1677
1682
|
this.state = 1 /* Initializing */;
|
|
1678
|
-
|
|
1683
|
+
this._initializingPromise = this._initialize();
|
|
1684
|
+
await this._initializingPromise;
|
|
1685
|
+
this._initializingPromise = void 0;
|
|
1679
1686
|
this.state = 2 /* Initialized */;
|
|
1680
1687
|
}
|
|
1681
1688
|
async _initialize() {
|
|
@@ -1694,11 +1701,9 @@ var CertificateManager = class {
|
|
|
1694
1701
|
mkdirRecursiveSync(import_node_path6.default.join(pkiDir, "issuers/crl"));
|
|
1695
1702
|
if (!import_node_fs9.default.existsSync(this.configFile) || !import_node_fs9.default.existsSync(this.privateKey)) {
|
|
1696
1703
|
return await this.withLock2(async () => {
|
|
1697
|
-
|
|
1698
|
-
if (this.state === 4 /* Disposed */) {
|
|
1704
|
+
if (this.state === 3 /* Disposing */ || this.state === 4 /* Disposed */) {
|
|
1699
1705
|
return;
|
|
1700
1706
|
}
|
|
1701
|
-
(0, import_node_assert10.default)(this.state === 1 /* Initializing */);
|
|
1702
1707
|
if (!import_node_fs9.default.existsSync(this.configFile)) {
|
|
1703
1708
|
import_node_fs9.default.writeFileSync(this.configFile, configurationFileSimpleTemplate);
|
|
1704
1709
|
}
|
|
@@ -1728,8 +1733,9 @@ var CertificateManager = class {
|
|
|
1728
1733
|
return;
|
|
1729
1734
|
}
|
|
1730
1735
|
if (this.state === 1 /* Initializing */) {
|
|
1731
|
-
|
|
1732
|
-
|
|
1736
|
+
if (this._initializingPromise) {
|
|
1737
|
+
await this._initializingPromise;
|
|
1738
|
+
}
|
|
1733
1739
|
}
|
|
1734
1740
|
try {
|
|
1735
1741
|
this.state = 3 /* Disposing */;
|
|
@@ -1742,6 +1748,30 @@ var CertificateManager = class {
|
|
|
1742
1748
|
this.state = 4 /* Disposed */;
|
|
1743
1749
|
}
|
|
1744
1750
|
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Force a full re-scan of all PKI folders, rebuilding
|
|
1753
|
+
* the in-memory `_thumbs` index from scratch.
|
|
1754
|
+
*
|
|
1755
|
+
* Call this after external processes have modified the
|
|
1756
|
+
* PKI folders (e.g. via `writeTrustList` or CLI tools)
|
|
1757
|
+
* to ensure the CertificateManager sees the latest
|
|
1758
|
+
* state without waiting for file-system events.
|
|
1759
|
+
*/
|
|
1760
|
+
async reloadCertificates() {
|
|
1761
|
+
await Promise.all(this._watchers.map((w) => w.close()));
|
|
1762
|
+
for (const w of this._watchers) {
|
|
1763
|
+
w.removeAllListeners();
|
|
1764
|
+
}
|
|
1765
|
+
this._watchers.splice(0);
|
|
1766
|
+
this._thumbs.rejected.clear();
|
|
1767
|
+
this._thumbs.trusted.clear();
|
|
1768
|
+
this._thumbs.issuers.certs.clear();
|
|
1769
|
+
this._thumbs.crl.clear();
|
|
1770
|
+
this._thumbs.issuersCrl.clear();
|
|
1771
|
+
this._filenameToHash.clear();
|
|
1772
|
+
this._readCertificatesCalled = false;
|
|
1773
|
+
await this._readCertificates();
|
|
1774
|
+
}
|
|
1745
1775
|
async withLock2(action) {
|
|
1746
1776
|
const lockFileName = import_node_path6.default.join(this.rootDir, "mutex.lock");
|
|
1747
1777
|
return (0, import_global_mutex.withLock)({ fileToLock: lockFileName }, async () => {
|
|
@@ -1754,7 +1784,9 @@ var CertificateManager = class {
|
|
|
1754
1784
|
*
|
|
1755
1785
|
*/
|
|
1756
1786
|
async createSelfSignedCertificate(params) {
|
|
1757
|
-
|
|
1787
|
+
if (typeof params.applicationUri !== "string") {
|
|
1788
|
+
throw new Error("createSelfSignedCertificate: expecting applicationUri to be a string");
|
|
1789
|
+
}
|
|
1758
1790
|
if (!import_node_fs9.default.existsSync(this.privateKey)) {
|
|
1759
1791
|
throw new Error(`Cannot find private key ${this.privateKey}`);
|
|
1760
1792
|
}
|
|
@@ -1770,14 +1802,13 @@ var CertificateManager = class {
|
|
|
1770
1802
|
});
|
|
1771
1803
|
}
|
|
1772
1804
|
async createCertificateRequest(params) {
|
|
1773
|
-
|
|
1805
|
+
if (!params) {
|
|
1806
|
+
throw new Error("params is required");
|
|
1807
|
+
}
|
|
1774
1808
|
const _params = params;
|
|
1775
1809
|
if (Object.prototype.hasOwnProperty.call(_params, "rootDir")) {
|
|
1776
1810
|
throw new Error("rootDir should not be specified ");
|
|
1777
1811
|
}
|
|
1778
|
-
(0, import_node_assert10.default)(!_params.rootDir);
|
|
1779
|
-
(0, import_node_assert10.default)(!_params.configFile);
|
|
1780
|
-
(0, import_node_assert10.default)(!_params.privateKey);
|
|
1781
1812
|
_params.rootDir = import_node_path6.default.resolve(this.rootDir);
|
|
1782
1813
|
_params.configFile = import_node_path6.default.resolve(this.configFile);
|
|
1783
1814
|
_params.privateKey = import_node_path6.default.resolve(this.privateKey);
|
|
@@ -1806,12 +1837,12 @@ var CertificateManager = class {
|
|
|
1806
1837
|
}
|
|
1807
1838
|
const pemCertificate = (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE");
|
|
1808
1839
|
const fingerprint2 = makeFingerprint(certificate);
|
|
1809
|
-
if (this._thumbs.issuers.certs
|
|
1840
|
+
if (this._thumbs.issuers.certs.has(fingerprint2)) {
|
|
1810
1841
|
return "Good" /* Good */;
|
|
1811
1842
|
}
|
|
1812
1843
|
const filename = import_node_path6.default.join(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
|
|
1813
1844
|
await import_node_fs9.default.promises.writeFile(filename, pemCertificate, "ascii");
|
|
1814
|
-
this._thumbs.issuers.certs
|
|
1845
|
+
this._thumbs.issuers.certs.set(fingerprint2, { certificate, filename });
|
|
1815
1846
|
if (addInTrustList) {
|
|
1816
1847
|
await this.trustCertificate(certificate);
|
|
1817
1848
|
}
|
|
@@ -1829,8 +1860,8 @@ var CertificateManager = class {
|
|
|
1829
1860
|
const folder = target === "trusted" ? this.crlFolder : this.issuersCrlFolder;
|
|
1830
1861
|
const crlInfo = (0, import_node_opcua_crypto5.exploreCertificateRevocationList)(crl);
|
|
1831
1862
|
const key = crlInfo.tbsCertList.issuerFingerprint;
|
|
1832
|
-
if (!index
|
|
1833
|
-
index
|
|
1863
|
+
if (!index.has(key)) {
|
|
1864
|
+
index.set(key, { crls: [], serialNumbers: {} });
|
|
1834
1865
|
}
|
|
1835
1866
|
const pemCertificate = (0, import_node_opcua_crypto5.toPem)(crl, "X509 CRL");
|
|
1836
1867
|
const filename = import_node_path6.default.join(folder, `crl_${buildIdealCertificateName(crl)}.pem`);
|
|
@@ -1865,9 +1896,7 @@ var CertificateManager = class {
|
|
|
1865
1896
|
throw err;
|
|
1866
1897
|
}
|
|
1867
1898
|
}
|
|
1868
|
-
|
|
1869
|
-
delete index[key];
|
|
1870
|
-
}
|
|
1899
|
+
index.clear();
|
|
1871
1900
|
};
|
|
1872
1901
|
if (target === "issuers" || target === "all") {
|
|
1873
1902
|
await clearFolder(this.issuersCrlFolder, this._thumbs.issuersCrl);
|
|
@@ -1884,7 +1913,7 @@ var CertificateManager = class {
|
|
|
1884
1913
|
async hasIssuer(thumbprint) {
|
|
1885
1914
|
await this._readCertificates();
|
|
1886
1915
|
const normalized = thumbprint.toLowerCase();
|
|
1887
|
-
return
|
|
1916
|
+
return this._thumbs.issuers.certs.has(normalized);
|
|
1888
1917
|
}
|
|
1889
1918
|
/**
|
|
1890
1919
|
* Remove a trusted certificate identified by its SHA-1 thumbprint.
|
|
@@ -1896,7 +1925,7 @@ var CertificateManager = class {
|
|
|
1896
1925
|
async removeTrustedCertificate(thumbprint) {
|
|
1897
1926
|
await this._readCertificates();
|
|
1898
1927
|
const normalized = thumbprint.toLowerCase();
|
|
1899
|
-
const entry = this._thumbs.trusted
|
|
1928
|
+
const entry = this._thumbs.trusted.get(normalized);
|
|
1900
1929
|
if (!entry) {
|
|
1901
1930
|
return null;
|
|
1902
1931
|
}
|
|
@@ -1907,7 +1936,7 @@ var CertificateManager = class {
|
|
|
1907
1936
|
throw err;
|
|
1908
1937
|
}
|
|
1909
1938
|
}
|
|
1910
|
-
|
|
1939
|
+
this._thumbs.trusted.delete(normalized);
|
|
1911
1940
|
return entry.certificate;
|
|
1912
1941
|
}
|
|
1913
1942
|
/**
|
|
@@ -1920,7 +1949,7 @@ var CertificateManager = class {
|
|
|
1920
1949
|
async removeIssuer(thumbprint) {
|
|
1921
1950
|
await this._readCertificates();
|
|
1922
1951
|
const normalized = thumbprint.toLowerCase();
|
|
1923
|
-
const entry = this._thumbs.issuers.certs
|
|
1952
|
+
const entry = this._thumbs.issuers.certs.get(normalized);
|
|
1924
1953
|
if (!entry) {
|
|
1925
1954
|
return null;
|
|
1926
1955
|
}
|
|
@@ -1931,7 +1960,7 @@ var CertificateManager = class {
|
|
|
1931
1960
|
throw err;
|
|
1932
1961
|
}
|
|
1933
1962
|
}
|
|
1934
|
-
|
|
1963
|
+
this._thumbs.issuers.certs.delete(normalized);
|
|
1935
1964
|
return entry.certificate;
|
|
1936
1965
|
}
|
|
1937
1966
|
/**
|
|
@@ -1944,7 +1973,7 @@ var CertificateManager = class {
|
|
|
1944
1973
|
const issuerInfo = (0, import_node_opcua_crypto5.exploreCertificate)(issuerCertificate);
|
|
1945
1974
|
const issuerFingerprint = issuerInfo.tbsCertificate.subjectFingerPrint;
|
|
1946
1975
|
const processIndex = async (index) => {
|
|
1947
|
-
const crlData = index
|
|
1976
|
+
const crlData = index.get(issuerFingerprint);
|
|
1948
1977
|
if (!crlData) return;
|
|
1949
1978
|
for (const crlEntry of crlData.crls) {
|
|
1950
1979
|
try {
|
|
@@ -1955,7 +1984,7 @@ var CertificateManager = class {
|
|
|
1955
1984
|
}
|
|
1956
1985
|
}
|
|
1957
1986
|
}
|
|
1958
|
-
delete
|
|
1987
|
+
index.delete(issuerFingerprint);
|
|
1959
1988
|
};
|
|
1960
1989
|
if (target === "issuers" || target === "all") {
|
|
1961
1990
|
await processIndex(this._thumbs.issuersCrl);
|
|
@@ -1988,16 +2017,28 @@ var CertificateManager = class {
|
|
|
1988
2017
|
} catch (_err) {
|
|
1989
2018
|
return "BadCertificateInvalid" /* BadCertificateInvalid */;
|
|
1990
2019
|
}
|
|
1991
|
-
const
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
if (status !== "Good" /* Good */ && status !== "BadCertificateUntrusted" /* BadCertificateUntrusted */) {
|
|
1995
|
-
return status;
|
|
2020
|
+
const result = await (0, import_node_opcua_crypto5.verifyCertificateChain)([leafCertificate]);
|
|
2021
|
+
if (result.status !== "Good") {
|
|
2022
|
+
return "BadCertificateInvalid" /* BadCertificateInvalid */;
|
|
1996
2023
|
}
|
|
1997
2024
|
if (certificates.length > 1) {
|
|
2025
|
+
const issuerFolder = this.issuersCertFolder;
|
|
2026
|
+
const issuerThumbprints = /* @__PURE__ */ new Set();
|
|
2027
|
+
const files = await import_node_fs9.default.promises.readdir(issuerFolder);
|
|
2028
|
+
for (const file of files) {
|
|
2029
|
+
const ext = import_node_path6.default.extname(file).toLowerCase();
|
|
2030
|
+
if (ext === ".pem" || ext === ".der") {
|
|
2031
|
+
try {
|
|
2032
|
+
const issuerCert = (0, import_node_opcua_crypto5.readCertificate)(import_node_path6.default.join(issuerFolder, file));
|
|
2033
|
+
const fp = makeFingerprint(issuerCert);
|
|
2034
|
+
issuerThumbprints.add(fp);
|
|
2035
|
+
} catch (_err) {
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
1998
2039
|
for (const issuerCert of certificates.slice(1)) {
|
|
1999
2040
|
const thumbprint = makeFingerprint(issuerCert);
|
|
2000
|
-
if (!
|
|
2041
|
+
if (!issuerThumbprints.has(thumbprint)) {
|
|
2001
2042
|
return "BadCertificateChainIncomplete" /* BadCertificateChainIncomplete */;
|
|
2002
2043
|
}
|
|
2003
2044
|
}
|
|
@@ -2019,7 +2060,7 @@ var CertificateManager = class {
|
|
|
2019
2060
|
*/
|
|
2020
2061
|
async isIssuerInUseByTrustedCertificate(issuerCertificate) {
|
|
2021
2062
|
await this._readCertificates();
|
|
2022
|
-
for (const entry of
|
|
2063
|
+
for (const entry of this._thumbs.trusted.values()) {
|
|
2023
2064
|
if (!entry.certificate) continue;
|
|
2024
2065
|
try {
|
|
2025
2066
|
if ((0, import_node_opcua_crypto5.verifyCertificateSignature)(entry.certificate, issuerCertificate)) {
|
|
@@ -2054,7 +2095,7 @@ var CertificateManager = class {
|
|
|
2054
2095
|
debugLog("Certificate has no extension 3");
|
|
2055
2096
|
return null;
|
|
2056
2097
|
}
|
|
2057
|
-
const issuerCertificates =
|
|
2098
|
+
const issuerCertificates = [...this._thumbs.issuers.certs.values()];
|
|
2058
2099
|
const selectedIssuerCertificates = findMatchingIssuerKey(issuerCertificates, wantedIssuerKey);
|
|
2059
2100
|
if (selectedIssuerCertificates.length > 0) {
|
|
2060
2101
|
if (selectedIssuerCertificates.length > 1) {
|
|
@@ -2062,7 +2103,7 @@ var CertificateManager = class {
|
|
|
2062
2103
|
}
|
|
2063
2104
|
return selectedIssuerCertificates[0].certificate || null;
|
|
2064
2105
|
}
|
|
2065
|
-
const trustedCertificates =
|
|
2106
|
+
const trustedCertificates = [...this._thumbs.trusted.values()];
|
|
2066
2107
|
const selectedTrustedCertificates = findMatchingIssuerKey(trustedCertificates, wantedIssuerKey);
|
|
2067
2108
|
if (selectedTrustedCertificates.length > 1) {
|
|
2068
2109
|
warningLog(
|
|
@@ -2078,48 +2119,47 @@ var CertificateManager = class {
|
|
|
2078
2119
|
* @private
|
|
2079
2120
|
*/
|
|
2080
2121
|
async _checkRejectedOrTrusted(certificate) {
|
|
2081
|
-
(0, import_node_assert10.default)(certificate instanceof Buffer);
|
|
2082
2122
|
const fingerprint2 = makeFingerprint(certificate);
|
|
2083
2123
|
debugLog("_checkRejectedOrTrusted fingerprint ", short(fingerprint2));
|
|
2084
2124
|
await this._readCertificates();
|
|
2085
|
-
if (
|
|
2125
|
+
if (this._thumbs.rejected.has(fingerprint2)) {
|
|
2086
2126
|
return "rejected";
|
|
2087
2127
|
}
|
|
2088
|
-
if (
|
|
2128
|
+
if (this._thumbs.trusted.has(fingerprint2)) {
|
|
2089
2129
|
return "trusted";
|
|
2090
2130
|
}
|
|
2091
2131
|
return "unknown";
|
|
2092
2132
|
}
|
|
2093
2133
|
async _moveCertificate(certificate, newStatus) {
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
const indexSrc = status === "rejected" ? this._thumbs.rejected : this._thumbs.trusted;
|
|
2101
|
-
const certificateSrc = indexSrc[fingerprint2]?.filename;
|
|
2102
|
-
if (!certificateSrc) {
|
|
2103
|
-
debugLog(" cannot find certificate ", fingerprint2.substring(0, 10), " in", this._thumbs, [status]);
|
|
2104
|
-
throw new Error("internal");
|
|
2134
|
+
await this.withLock2(async () => {
|
|
2135
|
+
const fingerprint2 = makeFingerprint(certificate);
|
|
2136
|
+
const status = await this.getCertificateStatus(certificate);
|
|
2137
|
+
debugLog("_moveCertificate", fingerprint2.substring(0, 10), "from", status, "to", newStatus);
|
|
2138
|
+
if (status !== "rejected" && status !== "trusted") {
|
|
2139
|
+
throw new Error(`_moveCertificate: unexpected status '${status}' for certificate ${fingerprint2.substring(0, 10)}`);
|
|
2105
2140
|
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
filename
|
|
2116
|
-
|
|
2117
|
-
|
|
2141
|
+
if (status !== newStatus) {
|
|
2142
|
+
const indexSrc = status === "rejected" ? this._thumbs.rejected : this._thumbs.trusted;
|
|
2143
|
+
const srcEntry = indexSrc.get(fingerprint2);
|
|
2144
|
+
if (!srcEntry) {
|
|
2145
|
+
debugLog(" cannot find certificate ", fingerprint2.substring(0, 10), " in", status);
|
|
2146
|
+
throw new Error(`_moveCertificate: certificate ${fingerprint2.substring(0, 10)} not found in ${status} index`);
|
|
2147
|
+
}
|
|
2148
|
+
const destFolder = newStatus === "trusted" ? this.trustedFolder : this.rejectedFolder;
|
|
2149
|
+
const certificateDest = import_node_path6.default.join(destFolder, import_node_path6.default.basename(srcEntry.filename));
|
|
2150
|
+
debugLog("_moveCertificate", fingerprint2.substring(0, 10), "old name", srcEntry.filename);
|
|
2151
|
+
debugLog("_moveCertificate", fingerprint2.substring(0, 10), "new name", certificateDest);
|
|
2152
|
+
await import_node_fs9.default.promises.rename(srcEntry.filename, certificateDest);
|
|
2153
|
+
indexSrc.delete(fingerprint2);
|
|
2154
|
+
const indexDest = newStatus === "trusted" ? this._thumbs.trusted : this._thumbs.rejected;
|
|
2155
|
+
indexDest.set(fingerprint2, { certificate, filename: certificateDest });
|
|
2156
|
+
}
|
|
2157
|
+
});
|
|
2118
2158
|
}
|
|
2119
2159
|
_findAssociatedCRLs(issuerCertificate) {
|
|
2120
2160
|
const issuerCertificateInfo = (0, import_node_opcua_crypto5.exploreCertificate)(issuerCertificate);
|
|
2121
2161
|
const key = issuerCertificateInfo.tbsCertificate.subjectFingerPrint;
|
|
2122
|
-
return this._thumbs.issuersCrl
|
|
2162
|
+
return this._thumbs.issuersCrl.get(key) ?? this._thumbs.crl.get(key) ?? null;
|
|
2123
2163
|
}
|
|
2124
2164
|
async isCertificateRevoked(certificate, issuerCertificate) {
|
|
2125
2165
|
if (isSelfSigned3(certificate)) {
|
|
@@ -2138,7 +2178,7 @@ var CertificateManager = class {
|
|
|
2138
2178
|
const certInfo = (0, import_node_opcua_crypto5.exploreCertificate)(certificate);
|
|
2139
2179
|
const serialNumber = certInfo.tbsCertificate.serialNumber || certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.serial || "";
|
|
2140
2180
|
const key = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint || "<unknown>";
|
|
2141
|
-
const crl2 = this._thumbs.crl
|
|
2181
|
+
const crl2 = this._thumbs.crl.get(key) ?? null;
|
|
2142
2182
|
if (crls.serialNumbers[serialNumber] || crl2?.serialNumbers[serialNumber]) {
|
|
2143
2183
|
return "BadCertificateRevoked" /* BadCertificateRevoked */;
|
|
2144
2184
|
}
|
|
@@ -2163,19 +2203,18 @@ var CertificateManager = class {
|
|
|
2163
2203
|
const crlInfo = (0, import_node_opcua_crypto5.exploreCertificateRevocationList)(crl);
|
|
2164
2204
|
debugLog(import_chalk6.default.cyan("add CRL in folder "), filename);
|
|
2165
2205
|
const fingerprint2 = crlInfo.tbsCertList.issuerFingerprint;
|
|
2166
|
-
|
|
2167
|
-
crls: [],
|
|
2168
|
-
|
|
2169
|
-
};
|
|
2170
|
-
|
|
2171
|
-
const serialNumbers = index[fingerprint2].serialNumbers;
|
|
2206
|
+
if (!index.has(fingerprint2)) {
|
|
2207
|
+
index.set(fingerprint2, { crls: [], serialNumbers: {} });
|
|
2208
|
+
}
|
|
2209
|
+
const data = index.get(fingerprint2) || { crls: [], serialNumbers: {} };
|
|
2210
|
+
data.crls.push({ crlInfo, filename });
|
|
2172
2211
|
for (const revokedCertificate of crlInfo.tbsCertList.revokedCertificates) {
|
|
2173
2212
|
const serialNumber = revokedCertificate.userCertificate;
|
|
2174
|
-
if (!serialNumbers[serialNumber]) {
|
|
2175
|
-
serialNumbers[serialNumber] = revokedCertificate.revocationDate;
|
|
2213
|
+
if (!data.serialNumbers[serialNumber]) {
|
|
2214
|
+
data.serialNumbers[serialNumber] = revokedCertificate.revocationDate;
|
|
2176
2215
|
}
|
|
2177
2216
|
}
|
|
2178
|
-
debugLog(import_chalk6.default.cyan("CRL"), fingerprint2, "serial numbers = ", Object.keys(serialNumbers));
|
|
2217
|
+
debugLog(import_chalk6.default.cyan("CRL"), fingerprint2, "serial numbers = ", Object.keys(data.serialNumbers));
|
|
2179
2218
|
} catch (err) {
|
|
2180
2219
|
debugLog("CRL filename error =");
|
|
2181
2220
|
debugLog(err);
|
|
@@ -2195,28 +2234,28 @@ var CertificateManager = class {
|
|
|
2195
2234
|
return;
|
|
2196
2235
|
}
|
|
2197
2236
|
this._readCertificatesCalled = true;
|
|
2237
|
+
const usePolling = process.env.OPCUA_PKI_USE_POLLING === "true";
|
|
2198
2238
|
const options = {
|
|
2199
|
-
usePolling
|
|
2200
|
-
interval: Math.min(10 * 60 * 1e3, Math.max(100, this.folderPoolingInterval)),
|
|
2201
|
-
persistent: false
|
|
2202
|
-
awaitWriteFinish: {
|
|
2203
|
-
stabilityThreshold: 2e3,
|
|
2204
|
-
pollInterval: 600
|
|
2205
|
-
}
|
|
2239
|
+
usePolling,
|
|
2240
|
+
...usePolling ? { interval: Math.min(10 * 60 * 1e3, Math.max(100, this.folderPoolingInterval)) } : {},
|
|
2241
|
+
persistent: false
|
|
2206
2242
|
};
|
|
2207
2243
|
async function _walkCRLFiles(folder, index) {
|
|
2208
2244
|
await new Promise((resolve, _reject) => {
|
|
2209
2245
|
const w = import_chokidar.default.watch(folder, options);
|
|
2210
|
-
w.on("unlink", (filename
|
|
2211
|
-
|
|
2212
|
-
|
|
2246
|
+
w.on("unlink", (filename) => {
|
|
2247
|
+
for (const [key, data] of index.entries()) {
|
|
2248
|
+
data.crls = data.crls.filter((c) => c.filename !== filename);
|
|
2249
|
+
if (data.crls.length === 0) {
|
|
2250
|
+
index.delete(key);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2213
2253
|
});
|
|
2214
|
-
w.on("add", (filename
|
|
2215
|
-
stat;
|
|
2254
|
+
w.on("add", (filename) => {
|
|
2216
2255
|
this._on_crl_file_added(index, filename);
|
|
2217
2256
|
});
|
|
2218
|
-
w.on("change", (
|
|
2219
|
-
debugLog("change in folder ", folder,
|
|
2257
|
+
w.on("change", (changedPath) => {
|
|
2258
|
+
debugLog("change in folder ", folder, changedPath);
|
|
2220
2259
|
});
|
|
2221
2260
|
this._watchers.push(w);
|
|
2222
2261
|
w.on("ready", () => {
|
|
@@ -2226,26 +2265,21 @@ var CertificateManager = class {
|
|
|
2226
2265
|
}
|
|
2227
2266
|
async function _walkAllFiles(folder, index) {
|
|
2228
2267
|
const w = import_chokidar.default.watch(folder, options);
|
|
2229
|
-
w.on("unlink", (filename
|
|
2230
|
-
stat;
|
|
2268
|
+
w.on("unlink", (filename) => {
|
|
2231
2269
|
debugLog(import_chalk6.default.cyan(`unlink in folder ${folder}`), filename);
|
|
2232
|
-
const h = this._filenameToHash
|
|
2233
|
-
if (h && index
|
|
2234
|
-
delete
|
|
2270
|
+
const h = this._filenameToHash.get(filename);
|
|
2271
|
+
if (h && index.has(h)) {
|
|
2272
|
+
index.delete(h);
|
|
2235
2273
|
}
|
|
2236
2274
|
});
|
|
2237
|
-
w.on("add", (filename
|
|
2238
|
-
stat;
|
|
2275
|
+
w.on("add", (filename) => {
|
|
2239
2276
|
debugLog(import_chalk6.default.cyan(`add in folder ${folder}`), filename);
|
|
2240
2277
|
try {
|
|
2241
2278
|
const certificate = (0, import_node_opcua_crypto5.readCertificate)(filename);
|
|
2242
2279
|
const info = (0, import_node_opcua_crypto5.exploreCertificate)(certificate);
|
|
2243
2280
|
const fingerprint2 = makeFingerprint(certificate);
|
|
2244
|
-
index
|
|
2245
|
-
|
|
2246
|
-
filename
|
|
2247
|
-
};
|
|
2248
|
-
this._filenameToHash[filename] = fingerprint2;
|
|
2281
|
+
index.set(fingerprint2, { certificate, filename, info });
|
|
2282
|
+
this._filenameToHash.set(filename, fingerprint2);
|
|
2249
2283
|
debugLog(
|
|
2250
2284
|
import_chalk6.default.magenta("CERT"),
|
|
2251
2285
|
info.tbsCertificate.subjectFingerPrint,
|
|
@@ -2257,15 +2291,26 @@ var CertificateManager = class {
|
|
|
2257
2291
|
debugLog(err);
|
|
2258
2292
|
}
|
|
2259
2293
|
});
|
|
2260
|
-
w.on("change", (
|
|
2261
|
-
|
|
2262
|
-
|
|
2294
|
+
w.on("change", (changedPath) => {
|
|
2295
|
+
debugLog(import_chalk6.default.cyan(`change in folder ${folder}`), changedPath);
|
|
2296
|
+
try {
|
|
2297
|
+
const certificate = (0, import_node_opcua_crypto5.readCertificate)(changedPath);
|
|
2298
|
+
const newFingerprint = makeFingerprint(certificate);
|
|
2299
|
+
const oldHash = this._filenameToHash.get(changedPath);
|
|
2300
|
+
if (oldHash && oldHash !== newFingerprint) {
|
|
2301
|
+
index.delete(oldHash);
|
|
2302
|
+
}
|
|
2303
|
+
index.set(newFingerprint, { certificate, filename: changedPath, info: (0, import_node_opcua_crypto5.exploreCertificate)(certificate) });
|
|
2304
|
+
this._filenameToHash.set(changedPath, newFingerprint);
|
|
2305
|
+
} catch (err) {
|
|
2306
|
+
debugLog(`change event: failed to re-read ${changedPath}`, err);
|
|
2307
|
+
}
|
|
2263
2308
|
});
|
|
2264
2309
|
this._watchers.push(w);
|
|
2265
2310
|
await new Promise((resolve, _reject) => {
|
|
2266
2311
|
w.on("ready", () => {
|
|
2267
2312
|
debugLog("ready");
|
|
2268
|
-
debugLog(
|
|
2313
|
+
debugLog([...index.keys()].map((k) => k.substring(0, 10)));
|
|
2269
2314
|
resolve();
|
|
2270
2315
|
});
|
|
2271
2316
|
});
|
|
@@ -2311,8 +2356,8 @@ var next_year = get_offset_date(today, 365);
|
|
|
2311
2356
|
var gLocalConfig = {};
|
|
2312
2357
|
var g_certificateAuthority;
|
|
2313
2358
|
async function construct_CertificateAuthority2(subject) {
|
|
2314
|
-
(0,
|
|
2315
|
-
(0,
|
|
2359
|
+
(0, import_node_assert10.default)(typeof gLocalConfig.CAFolder === "string", "expecting a CAFolder in config");
|
|
2360
|
+
(0, import_node_assert10.default)(typeof gLocalConfig.keySize === "number", "expecting a keySize in config");
|
|
2316
2361
|
if (!g_certificateAuthority) {
|
|
2317
2362
|
g_certificateAuthority = new CertificateAuthority({
|
|
2318
2363
|
keySize: gLocalConfig.keySize,
|
|
@@ -2324,7 +2369,7 @@ async function construct_CertificateAuthority2(subject) {
|
|
|
2324
2369
|
}
|
|
2325
2370
|
var certificateManager;
|
|
2326
2371
|
async function construct_CertificateManager() {
|
|
2327
|
-
(0,
|
|
2372
|
+
(0, import_node_assert10.default)(typeof gLocalConfig.PKIFolder === "string", "expecting a PKIFolder in config");
|
|
2328
2373
|
if (!certificateManager) {
|
|
2329
2374
|
certificateManager = new CertificateManager({
|
|
2330
2375
|
keySize: gLocalConfig.keySize,
|
|
@@ -2351,7 +2396,7 @@ function default_template_content() {
|
|
|
2351
2396
|
return default_config_template2;
|
|
2352
2397
|
}
|
|
2353
2398
|
const default_config_template = find_default_config_template();
|
|
2354
|
-
(0,
|
|
2399
|
+
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(default_config_template));
|
|
2355
2400
|
const default_config_template_content = import_node_fs10.default.readFileSync(default_config_template, "utf8");
|
|
2356
2401
|
return default_config_template_content;
|
|
2357
2402
|
}
|
|
@@ -2363,7 +2408,7 @@ function find_module_root_folder() {
|
|
|
2363
2408
|
}
|
|
2364
2409
|
rootFolder = import_node_path7.default.join(rootFolder, "..");
|
|
2365
2410
|
}
|
|
2366
|
-
(0,
|
|
2411
|
+
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(import_node_path7.default.join(rootFolder, "package.json")), "root folder must have a package.json file");
|
|
2367
2412
|
return rootFolder;
|
|
2368
2413
|
}
|
|
2369
2414
|
async function readConfiguration(argv) {
|
|
@@ -2392,10 +2437,10 @@ async function readConfiguration(argv) {
|
|
|
2392
2437
|
return makePath(tmp);
|
|
2393
2438
|
}
|
|
2394
2439
|
certificateDir = argv.root;
|
|
2395
|
-
(0,
|
|
2440
|
+
(0, import_node_assert10.default)(typeof certificateDir === "string");
|
|
2396
2441
|
certificateDir = prepare(certificateDir);
|
|
2397
2442
|
mkdirRecursiveSync(certificateDir);
|
|
2398
|
-
(0,
|
|
2443
|
+
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(certificateDir));
|
|
2399
2444
|
const default_config = import_node_path7.default.join(certificateDir, "config.js");
|
|
2400
2445
|
if (!import_node_fs10.default.existsSync(default_config)) {
|
|
2401
2446
|
debugLog(import_chalk7.default.yellow(" Creating default g_config file "), import_chalk7.default.cyan(default_config));
|
|
@@ -2460,7 +2505,7 @@ async function readConfiguration(argv) {
|
|
|
2460
2505
|
}
|
|
2461
2506
|
}
|
|
2462
2507
|
async function createDefaultCertificate(base_name, prefix, key_length, applicationUri, dev) {
|
|
2463
|
-
(0,
|
|
2508
|
+
(0, import_node_assert10.default)(key_length === 1024 || key_length === 2048 || key_length === 3072 || key_length === 4096);
|
|
2464
2509
|
const private_key_file = makePath(base_name, `${prefix}key_${key_length}.pem`);
|
|
2465
2510
|
const public_key_file = makePath(base_name, `${prefix}public_key_${key_length}.pub`);
|
|
2466
2511
|
const certificate_file = makePath(base_name, `${prefix}cert_${key_length}.pem`);
|
|
@@ -2566,9 +2611,9 @@ async function wrap(func) {
|
|
|
2566
2611
|
}
|
|
2567
2612
|
}
|
|
2568
2613
|
async function create_default_certificates(dev) {
|
|
2569
|
-
(0,
|
|
2614
|
+
(0, import_node_assert10.default)(gLocalConfig);
|
|
2570
2615
|
const base_name = gLocalConfig.certificateDir || "";
|
|
2571
|
-
(0,
|
|
2616
|
+
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(base_name));
|
|
2572
2617
|
let clientURN;
|
|
2573
2618
|
let serverURN;
|
|
2574
2619
|
let discoveryServerURN;
|
|
@@ -2723,7 +2768,7 @@ ${epilog}`
|
|
|
2723
2768
|
await readConfiguration(local_argv);
|
|
2724
2769
|
if (local_argv.clean) {
|
|
2725
2770
|
displayTitle("Cleaning old certificates");
|
|
2726
|
-
(0,
|
|
2771
|
+
(0, import_node_assert10.default)(gLocalConfig);
|
|
2727
2772
|
const certificateDir = gLocalConfig.certificateDir || "";
|
|
2728
2773
|
const files = await import_node_fs10.default.promises.readdir(certificateDir);
|
|
2729
2774
|
for (const file of files) {
|
|
@@ -2835,7 +2880,7 @@ ${epilog}`
|
|
|
2835
2880
|
await readConfiguration(local_argv2);
|
|
2836
2881
|
await construct_CertificateManager();
|
|
2837
2882
|
await construct_CertificateAuthority2("");
|
|
2838
|
-
(0,
|
|
2883
|
+
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(gLocalConfig.CAFolder || ""), " CA folder must exist");
|
|
2839
2884
|
gLocalConfig.privateKey = void 0;
|
|
2840
2885
|
gLocalConfig.subject = local_argv2.subject && local_argv2.subject.length > 1 ? local_argv2.subject : gLocalConfig.subject;
|
|
2841
2886
|
const csr_file = await certificateManager.createCertificateRequest(
|
|
@@ -2854,7 +2899,7 @@ ${epilog}`
|
|
|
2854
2899
|
csr_file,
|
|
2855
2900
|
gLocalConfig
|
|
2856
2901
|
);
|
|
2857
|
-
(0,
|
|
2902
|
+
(0, import_node_assert10.default)(typeof gLocalConfig.outputFile === "string");
|
|
2858
2903
|
import_node_fs10.default.writeFileSync(gLocalConfig.outputFile || "", import_node_fs10.default.readFileSync(certificate, "ascii"));
|
|
2859
2904
|
}
|
|
2860
2905
|
await wrap(async () => await command_certificate(local_argv));
|
|
@@ -2988,7 +3033,7 @@ ${epilog}`
|
|
|
2988
3033
|
csr_file,
|
|
2989
3034
|
gLocalConfig
|
|
2990
3035
|
);
|
|
2991
|
-
(0,
|
|
3036
|
+
(0, import_node_assert10.default)(typeof gLocalConfig.outputFile === "string");
|
|
2992
3037
|
import_node_fs10.default.writeFileSync(gLocalConfig.outputFile || "", import_node_fs10.default.readFileSync(certificate, "ascii"));
|
|
2993
3038
|
});
|
|
2994
3039
|
return;
|