node-opcua-pki 6.4.0 → 6.5.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/bin/pki.ts +1 -1
- package/dist/bin/install_prerequisite.mjs +1 -1
- package/dist/bin/pki.mjs +3425 -3
- package/dist/bin/pki.mjs.map +1 -1
- package/dist/{chunk-VXGTT7QM.mjs → chunk-GCHH54PS.mjs} +9 -1
- package/dist/{chunk-VXGTT7QM.mjs.map → chunk-GCHH54PS.mjs.map} +1 -1
- package/dist/index.d.mts +493 -69
- package/dist/index.d.ts +493 -69
- package/dist/index.js +564 -1193
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +618 -1248
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/readme.md +1 -2
package/dist/index.js
CHANGED
|
@@ -37,23 +37,16 @@ __export(lib_exports, {
|
|
|
37
37
|
VerificationStatus: () => VerificationStatus,
|
|
38
38
|
adjustApplicationUri: () => adjustApplicationUri,
|
|
39
39
|
adjustDate: () => adjustDate,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
displayTitle: () => displayTitle,
|
|
48
|
-
doDebug: () => doDebug,
|
|
40
|
+
convertPFXtoPEM: () => convertPFXtoPEM,
|
|
41
|
+
createPFX: () => createPFX,
|
|
42
|
+
dumpPFX: () => dumpPFX,
|
|
43
|
+
extractAllFromPFX: () => extractAllFromPFX,
|
|
44
|
+
extractCACertificatesFromPFX: () => extractCACertificatesFromPFX,
|
|
45
|
+
extractCertificateFromPFX: () => extractCertificateFromPFX,
|
|
46
|
+
extractPrivateKeyFromPFX: () => extractPrivateKeyFromPFX,
|
|
49
47
|
findIssuerCertificateInChain: () => findIssuerCertificateInChain,
|
|
50
|
-
g_config: () => g_config,
|
|
51
48
|
install_prerequisite: () => install_prerequisite,
|
|
52
|
-
|
|
53
|
-
mkdirRecursiveSync: () => mkdirRecursiveSync,
|
|
54
|
-
pki_main: () => main,
|
|
55
|
-
quote: () => quote,
|
|
56
|
-
warningLog: () => warningLog
|
|
49
|
+
quote: () => quote
|
|
57
50
|
});
|
|
58
51
|
module.exports = __toCommonJS(lib_exports);
|
|
59
52
|
|
|
@@ -142,13 +135,6 @@ function makePath(folderName, filename) {
|
|
|
142
135
|
|
|
143
136
|
// packages/node-opcua-pki/lib/toolbox/display.ts
|
|
144
137
|
var import_chalk2 = __toESM(require("chalk"));
|
|
145
|
-
function displayChapter(str) {
|
|
146
|
-
const l = " ";
|
|
147
|
-
warningLog(`${import_chalk2.default.bgWhite(l)} `);
|
|
148
|
-
str = ` ${str}${l}`.substring(0, l.length);
|
|
149
|
-
warningLog(import_chalk2.default.bgWhite.cyan(str));
|
|
150
|
-
warningLog(`${import_chalk2.default.bgWhite(l)} `);
|
|
151
|
-
}
|
|
152
138
|
function displayTitle(str) {
|
|
153
139
|
if (!g_config.silent) {
|
|
154
140
|
warningLog("");
|
|
@@ -647,12 +633,6 @@ function generateStaticConfig(configPath, options) {
|
|
|
647
633
|
return temporaryConfigPath;
|
|
648
634
|
}
|
|
649
635
|
}
|
|
650
|
-
var q = quote;
|
|
651
|
-
var n2 = makePath;
|
|
652
|
-
async function getPublicKeyFromPrivateKey(privateKeyFilename, publicKeyFilename) {
|
|
653
|
-
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(privateKeyFilename));
|
|
654
|
-
await execute_openssl(`rsa -pubout -in ${q(n2(privateKeyFilename))} -out ${q(n2(publicKeyFilename))}`, {});
|
|
655
|
-
}
|
|
656
636
|
function x509Date(date) {
|
|
657
637
|
date = date || /* @__PURE__ */ new Date();
|
|
658
638
|
const Y = date.getUTCFullYear();
|
|
@@ -670,45 +650,6 @@ function x509Date(date) {
|
|
|
670
650
|
return `${w(Y, 4) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2)}Z`;
|
|
671
651
|
}
|
|
672
652
|
}
|
|
673
|
-
async function dumpCertificate(certificate) {
|
|
674
|
-
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(certificate));
|
|
675
|
-
return await execute_openssl(`x509 -in ${q(n2(certificate))} -text -noout`, {});
|
|
676
|
-
}
|
|
677
|
-
async function toDer(certificatePem) {
|
|
678
|
-
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(certificatePem));
|
|
679
|
-
const certificateDer = certificatePem.replace(".pem", ".der");
|
|
680
|
-
return await execute_openssl(`x509 -outform der -in ${certificatePem} -out ${certificateDer}`, {});
|
|
681
|
-
}
|
|
682
|
-
async function fingerprint(certificatePem) {
|
|
683
|
-
(0, import_node_assert4.default)(import_node_fs4.default.existsSync(certificatePem));
|
|
684
|
-
return await execute_openssl(`x509 -fingerprint -noout -in ${certificatePem}`, {});
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// packages/node-opcua-pki/lib/toolbox/with_openssl/create_certificate_signing_request.ts
|
|
688
|
-
var q2 = quote;
|
|
689
|
-
var n3 = makePath;
|
|
690
|
-
async function createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFilename, params) {
|
|
691
|
-
(0, import_node_assert5.default)(params);
|
|
692
|
-
(0, import_node_assert5.default)(params.rootDir);
|
|
693
|
-
(0, import_node_assert5.default)(params.configFile);
|
|
694
|
-
(0, import_node_assert5.default)(params.privateKey);
|
|
695
|
-
(0, import_node_assert5.default)(typeof params.privateKey === "string");
|
|
696
|
-
(0, import_node_assert5.default)(import_node_fs5.default.existsSync(params.configFile), `config file must exist ${params.configFile}`);
|
|
697
|
-
(0, import_node_assert5.default)(import_node_fs5.default.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
|
|
698
|
-
(0, import_node_assert5.default)(import_node_fs5.default.existsSync(params.rootDir), "RootDir key must exist");
|
|
699
|
-
(0, import_node_assert5.default)(typeof certificateSigningRequestFilename === "string");
|
|
700
|
-
processAltNames(params);
|
|
701
|
-
const configFile = generateStaticConfig(params.configFile, { cwd: params.rootDir });
|
|
702
|
-
const options = { cwd: params.rootDir, openssl_conf: import_node_path4.default.relative(params.rootDir, configFile) };
|
|
703
|
-
const configOption = ` -config ${q2(n3(configFile))}`;
|
|
704
|
-
const subject = params.subject ? new import_node_opcua_crypto.Subject(params.subject).toString() : void 0;
|
|
705
|
-
const subjectOptions = subject ? ` -subj "${subject}"` : "";
|
|
706
|
-
displaySubtitle("- Creating a Certificate Signing Request with openssl");
|
|
707
|
-
await execute_openssl(
|
|
708
|
-
"req -new -sha256 -batch -text " + configOption + " -key " + q2(n3(params.privateKey)) + subjectOptions + " -out " + q2(n3(certificateSigningRequestFilename)),
|
|
709
|
-
options
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
653
|
|
|
713
654
|
// packages/node-opcua-pki/lib/ca/templates/ca_config_template.cnf.ts
|
|
714
655
|
var config = `#.........DO NOT MODIFY BY HAND .........................
|
|
@@ -846,8 +787,8 @@ var config2 = {
|
|
|
846
787
|
forceCA: false,
|
|
847
788
|
pkiDir: "INVALID"
|
|
848
789
|
};
|
|
849
|
-
var
|
|
850
|
-
var
|
|
790
|
+
var n2 = makePath;
|
|
791
|
+
var q = quote;
|
|
851
792
|
function octetStringToIpAddress(a) {
|
|
852
793
|
return parseInt(a.substring(0, 2), 16).toString() + "." + parseInt(a.substring(2, 4), 16).toString() + "." + parseInt(a.substring(4, 6), 16).toString() + "." + parseInt(a.substring(6, 8), 16).toString();
|
|
853
794
|
}
|
|
@@ -898,7 +839,7 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
898
839
|
processAltNames({});
|
|
899
840
|
const options = { cwd: caRootDir };
|
|
900
841
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
901
|
-
const configOption = ` -config ${
|
|
842
|
+
const configOption = ` -config ${q(n2(configFile))}`;
|
|
902
843
|
const keySize = certificateAuthority.keySize;
|
|
903
844
|
const privateKeyFilename = import_node_path5.default.join(caRootDir, "private/cakey.pem");
|
|
904
845
|
const csrFilename = import_node_path5.default.join(caRootDir, "private/cakey.csr");
|
|
@@ -906,12 +847,12 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
906
847
|
await (0, import_node_opcua_crypto2.generatePrivateKeyFile)(privateKeyFilename, keySize);
|
|
907
848
|
displayTitle("Generate a certificate request for the CA key");
|
|
908
849
|
await execute_openssl(
|
|
909
|
-
"req -new -sha256 -text -extensions v3_ca" + configOption + " -key " +
|
|
850
|
+
"req -new -sha256 -text -extensions v3_ca" + configOption + " -key " + q(n2(privateKeyFilename)) + " -out " + q(n2(csrFilename)) + " " + subjectOpt,
|
|
910
851
|
options
|
|
911
852
|
);
|
|
912
853
|
displayTitle("Generate CA Certificate (self-signed)");
|
|
913
854
|
await execute_openssl(
|
|
914
|
-
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " +
|
|
855
|
+
" x509 -sha256 -req -days 3650 -text -extensions v3_ca -extfile " + q(n2(configFile)) + " -in private/cakey.csr -signkey " + q(n2(privateKeyFilename)) + " -out public/cacert.pem",
|
|
915
856
|
options
|
|
916
857
|
);
|
|
917
858
|
displaySubtitle("generate initial CRL (Certificate Revocation List)");
|
|
@@ -923,11 +864,14 @@ async function regenerateCrl(revocationList, configOption, options) {
|
|
|
923
864
|
await execute_openssl(`ca -gencrl ${configOption} -out crl/revocation_list.crl`, options);
|
|
924
865
|
await execute_openssl("crl -in crl/revocation_list.crl -out crl/revocation_list.der -outform der", options);
|
|
925
866
|
displaySubtitle("Display (Certificate Revocation List)");
|
|
926
|
-
await execute_openssl(`crl -in ${
|
|
867
|
+
await execute_openssl(`crl -in ${q(n2(revocationList))} -text -noout`, options);
|
|
927
868
|
}
|
|
928
869
|
var CertificateAuthority = class {
|
|
870
|
+
/** RSA key size used when generating the CA private key. */
|
|
929
871
|
keySize;
|
|
872
|
+
/** Root filesystem path of the CA directory structure. */
|
|
930
873
|
location;
|
|
874
|
+
/** X.500 subject of the CA certificate. */
|
|
931
875
|
subject;
|
|
932
876
|
constructor(options) {
|
|
933
877
|
(0, import_node_assert6.default)(Object.prototype.hasOwnProperty.call(options, "location"));
|
|
@@ -936,33 +880,54 @@ var CertificateAuthority = class {
|
|
|
936
880
|
this.keySize = options.keySize || 2048;
|
|
937
881
|
this.subject = new import_node_opcua_crypto2.Subject(options.subject || defaultSubject);
|
|
938
882
|
}
|
|
883
|
+
/** Absolute path to the CA root directory (alias for {@link location}). */
|
|
939
884
|
get rootDir() {
|
|
940
885
|
return this.location;
|
|
941
886
|
}
|
|
887
|
+
/** Path to the OpenSSL configuration file (`conf/caconfig.cnf`). */
|
|
942
888
|
get configFile() {
|
|
943
889
|
return import_node_path5.default.normalize(import_node_path5.default.join(this.rootDir, "./conf/caconfig.cnf"));
|
|
944
890
|
}
|
|
891
|
+
/** Path to the CA certificate in PEM format (`public/cacert.pem`). */
|
|
945
892
|
get caCertificate() {
|
|
946
893
|
return makePath(this.rootDir, "./public/cacert.pem");
|
|
947
894
|
}
|
|
948
895
|
/**
|
|
949
|
-
*
|
|
896
|
+
* Path to the current Certificate Revocation List in DER format.
|
|
897
|
+
* (`crl/revocation_list.der`)
|
|
950
898
|
*/
|
|
951
899
|
get revocationListDER() {
|
|
952
900
|
return makePath(this.rootDir, "./crl/revocation_list.der");
|
|
953
901
|
}
|
|
954
902
|
/**
|
|
955
|
-
*
|
|
903
|
+
* Path to the current Certificate Revocation List in PEM format.
|
|
904
|
+
* (`crl/revocation_list.crl`)
|
|
956
905
|
*/
|
|
957
906
|
get revocationList() {
|
|
958
907
|
return makePath(this.rootDir, "./crl/revocation_list.crl");
|
|
959
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* Path to the concatenated CA certificate + CRL file.
|
|
911
|
+
* Used by OpenSSL for CRL-based verification.
|
|
912
|
+
*/
|
|
960
913
|
get caCertificateWithCrl() {
|
|
961
914
|
return makePath(this.rootDir, "./public/cacertificate_with_crl.pem");
|
|
962
915
|
}
|
|
916
|
+
/**
|
|
917
|
+
* Initialize the CA directory structure, generate the CA
|
|
918
|
+
* private key and self-signed certificate if they do not
|
|
919
|
+
* already exist.
|
|
920
|
+
*/
|
|
963
921
|
async initialize() {
|
|
964
922
|
await construct_CertificateAuthority(this);
|
|
965
923
|
}
|
|
924
|
+
/**
|
|
925
|
+
* Rebuild the combined CA certificate + CRL file.
|
|
926
|
+
*
|
|
927
|
+
* This concatenates the CA certificate with the current
|
|
928
|
+
* revocation list so that OpenSSL can verify certificates
|
|
929
|
+
* with CRL checking enabled.
|
|
930
|
+
*/
|
|
966
931
|
async constructCACertificateWithCRL() {
|
|
967
932
|
const cacertWithCRL = this.caCertificateWithCrl;
|
|
968
933
|
if (import_node_fs6.default.existsSync(this.revocationList)) {
|
|
@@ -974,6 +939,12 @@ var CertificateAuthority = class {
|
|
|
974
939
|
await import_node_fs6.default.promises.writeFile(cacertWithCRL, import_node_fs6.default.readFileSync(this.caCertificate));
|
|
975
940
|
}
|
|
976
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* Append the CA certificate to a signed certificate file,
|
|
944
|
+
* creating a PEM certificate chain.
|
|
945
|
+
*
|
|
946
|
+
* @param certificate - path to the certificate file to extend
|
|
947
|
+
*/
|
|
977
948
|
async constructCertificateChain(certificate) {
|
|
978
949
|
(0, import_node_assert6.default)(import_node_fs6.default.existsSync(certificate));
|
|
979
950
|
(0, import_node_assert6.default)(import_node_fs6.default.existsSync(this.caCertificate));
|
|
@@ -984,6 +955,13 @@ var CertificateAuthority = class {
|
|
|
984
955
|
// + fs.readFileSync(this.revocationList)
|
|
985
956
|
);
|
|
986
957
|
}
|
|
958
|
+
/**
|
|
959
|
+
* Create a self-signed certificate using OpenSSL.
|
|
960
|
+
*
|
|
961
|
+
* @param certificateFile - output path for the signed certificate
|
|
962
|
+
* @param privateKey - path to the private key file
|
|
963
|
+
* @param params - certificate parameters (subject, validity, SANs)
|
|
964
|
+
*/
|
|
987
965
|
async createSelfSignedCertificate(certificateFile, privateKey, params) {
|
|
988
966
|
(0, import_node_assert6.default)(typeof privateKey === "string");
|
|
989
967
|
(0, import_node_assert6.default)(import_node_fs6.default.existsSync(privateKey));
|
|
@@ -1005,28 +983,27 @@ var CertificateAuthority = class {
|
|
|
1005
983
|
const subjectOptions = subject && subject.length > 1 ? ` -subj ${subject} ` : "";
|
|
1006
984
|
displaySubtitle("- the certificate signing request");
|
|
1007
985
|
await execute_openssl(
|
|
1008
|
-
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " +
|
|
986
|
+
"req -new -sha256 -text " + configOption + subjectOptions + " -batch -key " + q(n2(privateKey)) + " -out " + q(n2(csrFile)),
|
|
1009
987
|
options
|
|
1010
988
|
);
|
|
1011
989
|
displaySubtitle("- creating the self-signed certificate");
|
|
1012
990
|
await execute_openssl(
|
|
1013
|
-
"ca -selfsign -keyfile " +
|
|
991
|
+
"ca -selfsign -keyfile " + q(n2(privateKey)) + " -startdate " + x509Date(params.startDate) + " -enddate " + x509Date(params.endDate) + " -batch -out " + q(n2(certificateFile)) + " -in " + q(n2(csrFile)),
|
|
1014
992
|
options
|
|
1015
993
|
);
|
|
1016
994
|
displaySubtitle("- dump the certificate for a check");
|
|
1017
|
-
await execute_openssl(`x509 -in ${
|
|
995
|
+
await execute_openssl(`x509 -in ${q(n2(certificateFile))} -dates -fingerprint -purpose -noout`, {});
|
|
1018
996
|
displaySubtitle("- verify self-signed certificate");
|
|
1019
|
-
await execute_openssl_no_failure(`verify -verbose -CAfile ${
|
|
997
|
+
await execute_openssl_no_failure(`verify -verbose -CAfile ${q(n2(certificateFile))} ${q(n2(certificateFile))}`, options);
|
|
1020
998
|
await import_node_fs6.default.promises.unlink(csrFile);
|
|
1021
999
|
}
|
|
1022
1000
|
/**
|
|
1023
|
-
*
|
|
1001
|
+
* Revoke a certificate and regenerate the CRL.
|
|
1024
1002
|
*
|
|
1025
|
-
* @
|
|
1026
|
-
* @param
|
|
1027
|
-
* @param params
|
|
1028
|
-
*
|
|
1029
|
-
* @async
|
|
1003
|
+
* @param certificate - path to the certificate file to revoke
|
|
1004
|
+
* @param params - revocation parameters
|
|
1005
|
+
* @param params.reason - CRL reason code
|
|
1006
|
+
* (default `"keyCompromise"`)
|
|
1030
1007
|
*/
|
|
1031
1008
|
async revokeCertificate(certificate, params) {
|
|
1032
1009
|
const crlReasons = [
|
|
@@ -1047,31 +1024,34 @@ var CertificateAuthority = class {
|
|
|
1047
1024
|
setEnv("ALTNAME", "");
|
|
1048
1025
|
const randomFile = import_node_path5.default.join(this.rootDir, "random.rnd");
|
|
1049
1026
|
setEnv("RANDFILE", randomFile);
|
|
1050
|
-
const configOption = ` -config ${
|
|
1027
|
+
const configOption = ` -config ${q(n2(configFile))}`;
|
|
1051
1028
|
const reason = params.reason || "keyCompromise";
|
|
1052
1029
|
(0, import_node_assert6.default)(crlReasons.indexOf(reason) >= 0);
|
|
1053
1030
|
displayTitle(`Revoking certificate ${certificate}`);
|
|
1054
1031
|
displaySubtitle("Revoke certificate");
|
|
1055
|
-
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${
|
|
1032
|
+
await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${q(certificate)} -crl_reason ${reason}`, options);
|
|
1056
1033
|
await regenerateCrl(this.revocationList, configOption, options);
|
|
1057
1034
|
displaySubtitle("Verify that certificate is revoked");
|
|
1058
1035
|
await execute_openssl_no_failure(
|
|
1059
|
-
"verify -verbose -CRLfile " +
|
|
1036
|
+
"verify -verbose -CRLfile " + q(n2(this.revocationList)) + " -CAfile " + q(n2(this.caCertificate)) + " -crl_check " + q(n2(certificate)),
|
|
1060
1037
|
options
|
|
1061
1038
|
);
|
|
1062
1039
|
displaySubtitle("Produce CRL in DER form ");
|
|
1063
|
-
await execute_openssl(`crl -in ${
|
|
1040
|
+
await execute_openssl(`crl -in ${q(n2(this.revocationList))} -out crl/revocation_list.der -outform der`, options);
|
|
1064
1041
|
displaySubtitle("Produce CRL in PEM form ");
|
|
1065
|
-
await execute_openssl(`crl -in ${
|
|
1042
|
+
await execute_openssl(`crl -in ${q(n2(this.revocationList))} -out crl/revocation_list.pem -outform pem -text `, options);
|
|
1066
1043
|
}
|
|
1067
1044
|
/**
|
|
1045
|
+
* Sign a Certificate Signing Request (CSR) with this CA.
|
|
1046
|
+
*
|
|
1047
|
+
* The signed certificate is written to `certificate`, and the
|
|
1048
|
+
* CA certificate chain plus CRL are appended to form a
|
|
1049
|
+
* complete certificate chain.
|
|
1068
1050
|
*
|
|
1069
|
-
* @param certificate
|
|
1070
|
-
* @param certificateSigningRequestFilename
|
|
1071
|
-
* @param
|
|
1072
|
-
* @
|
|
1073
|
-
* @param params.startDate - startDate of the certificate
|
|
1074
|
-
* @param params.validity - number of day of validity of the certificate
|
|
1051
|
+
* @param certificate - output path for the signed certificate
|
|
1052
|
+
* @param certificateSigningRequestFilename - path to the CSR
|
|
1053
|
+
* @param params1 - signing parameters (validity, dates, SANs)
|
|
1054
|
+
* @returns the path to the signed certificate
|
|
1075
1055
|
*/
|
|
1076
1056
|
async signCertificateRequest(certificate, certificateSigningRequestFilename, params1) {
|
|
1077
1057
|
await ensure_openssl_installed();
|
|
@@ -1089,12 +1069,12 @@ var CertificateAuthority = class {
|
|
|
1089
1069
|
if (typeof applicationUri !== "string") {
|
|
1090
1070
|
throw new Error("Cannot find applicationUri in CSR");
|
|
1091
1071
|
}
|
|
1092
|
-
const
|
|
1072
|
+
const dns = csrInfo.extensionRequest.subjectAltName.dNSName || [];
|
|
1093
1073
|
let ip = csrInfo.extensionRequest.subjectAltName.iPAddress || [];
|
|
1094
1074
|
ip = ip.map(octetStringToIpAddress);
|
|
1095
1075
|
const params = {
|
|
1096
1076
|
applicationUri,
|
|
1097
|
-
dns
|
|
1077
|
+
dns,
|
|
1098
1078
|
ip
|
|
1099
1079
|
};
|
|
1100
1080
|
processAltNames(params);
|
|
@@ -1102,11 +1082,11 @@ var CertificateAuthority = class {
|
|
|
1102
1082
|
displaySubtitle("- then we ask the authority to sign the certificate signing request");
|
|
1103
1083
|
const configOption = ` -config ${configFile}`;
|
|
1104
1084
|
await execute_openssl(
|
|
1105
|
-
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " +
|
|
1085
|
+
"ca " + configOption + " -startdate " + x509Date(params1.startDate) + " -enddate " + x509Date(params1.endDate) + " -batch -out " + q(n2(certificate)) + " -in " + q(n2(certificateSigningRequestFilename)),
|
|
1106
1086
|
options
|
|
1107
1087
|
);
|
|
1108
1088
|
displaySubtitle("- dump the certificate for a check");
|
|
1109
|
-
await execute_openssl(`x509 -in ${
|
|
1089
|
+
await execute_openssl(`x509 -in ${q(n2(certificate))} -dates -fingerprint -purpose -noout`, options);
|
|
1110
1090
|
displaySubtitle("- construct CA certificate with CRL");
|
|
1111
1091
|
await this.constructCACertificateWithCRL();
|
|
1112
1092
|
displaySubtitle("- construct certificate chain");
|
|
@@ -1115,6 +1095,11 @@ var CertificateAuthority = class {
|
|
|
1115
1095
|
await this.verifyCertificate(certificate);
|
|
1116
1096
|
return certificate;
|
|
1117
1097
|
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Verify a certificate against this CA.
|
|
1100
|
+
*
|
|
1101
|
+
* @param certificate - path to the certificate file to verify
|
|
1102
|
+
*/
|
|
1118
1103
|
async verifyCertificate(certificate) {
|
|
1119
1104
|
const isImplemented = false;
|
|
1120
1105
|
if (isImplemented) {
|
|
@@ -1124,94 +1109,13 @@ var CertificateAuthority = class {
|
|
|
1124
1109
|
const _configOption = ` -config ${configFile}`;
|
|
1125
1110
|
_configOption;
|
|
1126
1111
|
await execute_openssl_no_failure(
|
|
1127
|
-
`verify -verbose -CAfile ${
|
|
1112
|
+
`verify -verbose -CAfile ${q(n2(this.caCertificateWithCrl))} ${q(n2(certificate))}`,
|
|
1128
1113
|
options
|
|
1129
1114
|
);
|
|
1130
1115
|
}
|
|
1131
1116
|
}
|
|
1132
1117
|
};
|
|
1133
1118
|
|
|
1134
|
-
// packages/node-opcua-pki/lib/ca/crypto_create_CA.ts
|
|
1135
|
-
var import_node_assert10 = __toESM(require("assert"));
|
|
1136
|
-
var import_node_fs10 = __toESM(require("fs"));
|
|
1137
|
-
var import_node_module = require("module");
|
|
1138
|
-
var import_node_os4 = __toESM(require("os"));
|
|
1139
|
-
var import_node_path7 = __toESM(require("path"));
|
|
1140
|
-
var import_chalk7 = __toESM(require("chalk"));
|
|
1141
|
-
var import_node_opcua_crypto6 = require("node-opcua-crypto");
|
|
1142
|
-
|
|
1143
|
-
// packages/node-opcua-pki/lib/misc/applicationurn.ts
|
|
1144
|
-
var import_node_assert7 = __toESM(require("assert"));
|
|
1145
|
-
var import_node_crypto = require("crypto");
|
|
1146
|
-
function makeApplicationUrn(hostname, suffix) {
|
|
1147
|
-
let hostnameHash = hostname;
|
|
1148
|
-
if (hostnameHash.length + 7 + suffix.length >= 64) {
|
|
1149
|
-
hostnameHash = (0, import_node_crypto.createHash)("md5").update(hostname).digest("hex").substring(0, 16);
|
|
1150
|
-
}
|
|
1151
|
-
const applicationUrn = `urn:${hostnameHash}:${suffix}`;
|
|
1152
|
-
(0, import_node_assert7.default)(applicationUrn.length <= 64);
|
|
1153
|
-
return applicationUrn;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
// packages/node-opcua-pki/lib/misc/hostname.ts
|
|
1157
|
-
var import_node_dns = __toESM(require("dns"));
|
|
1158
|
-
var import_node_os3 = __toESM(require("os"));
|
|
1159
|
-
var import_node_util = require("util");
|
|
1160
|
-
function trim(str, length) {
|
|
1161
|
-
if (!length) {
|
|
1162
|
-
return str;
|
|
1163
|
-
}
|
|
1164
|
-
return str.substring(0, Math.min(str.length, length));
|
|
1165
|
-
}
|
|
1166
|
-
function fqdn(callback) {
|
|
1167
|
-
const uqdn = import_node_os3.default.hostname();
|
|
1168
|
-
import_node_dns.default.lookup(uqdn, { hints: import_node_dns.default.ADDRCONFIG }, (err1, ip) => {
|
|
1169
|
-
if (err1) {
|
|
1170
|
-
return callback(err1);
|
|
1171
|
-
}
|
|
1172
|
-
import_node_dns.default.lookupService(ip, 0, (err2, _fqdn) => {
|
|
1173
|
-
if (err2) {
|
|
1174
|
-
return callback(err2);
|
|
1175
|
-
}
|
|
1176
|
-
_fqdn = _fqdn.replace(".localdomain", "");
|
|
1177
|
-
callback(null, _fqdn);
|
|
1178
|
-
});
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
var _fullyQualifiedDomainNameInCache;
|
|
1182
|
-
async function extractFullyQualifiedDomainName() {
|
|
1183
|
-
if (_fullyQualifiedDomainNameInCache) {
|
|
1184
|
-
return _fullyQualifiedDomainNameInCache;
|
|
1185
|
-
}
|
|
1186
|
-
if (process.platform === "win32") {
|
|
1187
|
-
const env = process.env;
|
|
1188
|
-
_fullyQualifiedDomainNameInCache = env.COMPUTERNAME + (env.USERDNSDOMAIN && env.USERDNSDOMAIN?.length > 0 ? `.${env.USERDNSDOMAIN}` : "");
|
|
1189
|
-
} else {
|
|
1190
|
-
try {
|
|
1191
|
-
_fullyQualifiedDomainNameInCache = await (0, import_node_util.promisify)(fqdn)();
|
|
1192
|
-
if (_fullyQualifiedDomainNameInCache === "localhost") {
|
|
1193
|
-
throw new Error("localhost not expected");
|
|
1194
|
-
}
|
|
1195
|
-
if (/sethostname/.test(_fullyQualifiedDomainNameInCache)) {
|
|
1196
|
-
throw new Error("Detecting fqdn on windows !!!");
|
|
1197
|
-
}
|
|
1198
|
-
} catch (_err) {
|
|
1199
|
-
_fullyQualifiedDomainNameInCache = import_node_os3.default.hostname();
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
return _fullyQualifiedDomainNameInCache;
|
|
1203
|
-
}
|
|
1204
|
-
async function prepareFQDN() {
|
|
1205
|
-
_fullyQualifiedDomainNameInCache = await extractFullyQualifiedDomainName();
|
|
1206
|
-
}
|
|
1207
|
-
function getFullyQualifiedDomainName(optional_max_length) {
|
|
1208
|
-
if (!_fullyQualifiedDomainNameInCache) {
|
|
1209
|
-
throw new Error("FullyQualifiedDomainName computation is not completed yet");
|
|
1210
|
-
}
|
|
1211
|
-
return _fullyQualifiedDomainNameInCache ? trim(_fullyQualifiedDomainNameInCache, optional_max_length) : "%FQDN%";
|
|
1212
|
-
}
|
|
1213
|
-
prepareFQDN();
|
|
1214
|
-
|
|
1215
1119
|
// packages/node-opcua-pki/lib/pki/certificate_manager.ts
|
|
1216
1120
|
var import_node_fs9 = __toESM(require("fs"));
|
|
1217
1121
|
var import_node_path6 = __toESM(require("path"));
|
|
@@ -1221,18 +1125,18 @@ var import_chokidar = __toESM(require("chokidar"));
|
|
|
1221
1125
|
var import_node_opcua_crypto5 = require("node-opcua-crypto");
|
|
1222
1126
|
|
|
1223
1127
|
// packages/node-opcua-pki/lib/toolbox/without_openssl/create_certificate_signing_request.ts
|
|
1224
|
-
var
|
|
1128
|
+
var import_node_assert7 = __toESM(require("assert"));
|
|
1225
1129
|
var import_node_fs7 = __toESM(require("fs"));
|
|
1226
1130
|
var import_node_opcua_crypto3 = require("node-opcua-crypto");
|
|
1227
1131
|
async function createCertificateSigningRequestAsync(certificateSigningRequestFilename, params) {
|
|
1228
|
-
(0,
|
|
1229
|
-
(0,
|
|
1230
|
-
(0,
|
|
1231
|
-
(0,
|
|
1232
|
-
(0,
|
|
1233
|
-
(0,
|
|
1234
|
-
(0,
|
|
1235
|
-
(0,
|
|
1132
|
+
(0, import_node_assert7.default)(params);
|
|
1133
|
+
(0, import_node_assert7.default)(params.rootDir);
|
|
1134
|
+
(0, import_node_assert7.default)(params.configFile);
|
|
1135
|
+
(0, import_node_assert7.default)(params.privateKey);
|
|
1136
|
+
(0, import_node_assert7.default)(typeof params.privateKey === "string");
|
|
1137
|
+
(0, import_node_assert7.default)(import_node_fs7.default.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
|
|
1138
|
+
(0, import_node_assert7.default)(import_node_fs7.default.existsSync(params.rootDir), "RootDir key must exist");
|
|
1139
|
+
(0, import_node_assert7.default)(typeof certificateSigningRequestFilename === "string");
|
|
1236
1140
|
const subject = params.subject ? new import_node_opcua_crypto3.Subject(params.subject).toString() : void 0;
|
|
1237
1141
|
displaySubtitle("- Creating a Certificate Signing Request with subtile");
|
|
1238
1142
|
const privateKeyPem = await import_node_fs7.default.promises.readFile(params.privateKey, "utf-8");
|
|
@@ -1251,22 +1155,22 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
1251
1155
|
}
|
|
1252
1156
|
|
|
1253
1157
|
// packages/node-opcua-pki/lib/toolbox/without_openssl/create_self_signed_certificate.ts
|
|
1254
|
-
var
|
|
1158
|
+
var import_node_assert8 = __toESM(require("assert"));
|
|
1255
1159
|
var import_node_fs8 = __toESM(require("fs"));
|
|
1256
1160
|
var import_node_opcua_crypto4 = require("node-opcua-crypto");
|
|
1257
1161
|
async function createSelfSignedCertificateAsync(certificate, params) {
|
|
1258
1162
|
params.purpose = params.purpose || import_node_opcua_crypto4.CertificatePurpose.ForApplication;
|
|
1259
|
-
(0,
|
|
1260
|
-
(0,
|
|
1261
|
-
(0,
|
|
1262
|
-
(0,
|
|
1163
|
+
(0, import_node_assert8.default)(params.purpose, "Please provide a Certificate Purpose");
|
|
1164
|
+
(0, import_node_assert8.default)(import_node_fs8.default.existsSync(params.configFile));
|
|
1165
|
+
(0, import_node_assert8.default)(import_node_fs8.default.existsSync(params.rootDir));
|
|
1166
|
+
(0, import_node_assert8.default)(import_node_fs8.default.existsSync(params.privateKey));
|
|
1263
1167
|
if (!params.subject) {
|
|
1264
1168
|
throw Error("Missing subject");
|
|
1265
1169
|
}
|
|
1266
|
-
(0,
|
|
1267
|
-
(0,
|
|
1170
|
+
(0, import_node_assert8.default)(typeof params.applicationUri === "string");
|
|
1171
|
+
(0, import_node_assert8.default)(Array.isArray(params.dns));
|
|
1268
1172
|
adjustDate(params);
|
|
1269
|
-
(0,
|
|
1173
|
+
(0, import_node_assert8.default)(Object.prototype.hasOwnProperty.call(params, "validity"));
|
|
1270
1174
|
let subject = new import_node_opcua_crypto4.Subject(params.subject);
|
|
1271
1175
|
subject = subject.toString();
|
|
1272
1176
|
const purpose = params.purpose;
|
|
@@ -1323,20 +1227,21 @@ var VerificationStatus = /* @__PURE__ */ ((VerificationStatus2) => {
|
|
|
1323
1227
|
return VerificationStatus2;
|
|
1324
1228
|
})(VerificationStatus || {});
|
|
1325
1229
|
function makeFingerprint(certificate) {
|
|
1326
|
-
|
|
1230
|
+
const chain = (0, import_node_opcua_crypto5.split_der)(certificate);
|
|
1231
|
+
return (0, import_node_opcua_crypto5.makeSHA1Thumbprint)(chain[0]).toString("hex");
|
|
1327
1232
|
}
|
|
1328
1233
|
function short(stringToShorten) {
|
|
1329
1234
|
return stringToShorten.substring(0, 10);
|
|
1330
1235
|
}
|
|
1331
1236
|
var forbiddenChars = /[\x00-\x1F<>:"/\\|?*]/g;
|
|
1332
1237
|
function buildIdealCertificateName(certificate) {
|
|
1333
|
-
const
|
|
1238
|
+
const fingerprint = makeFingerprint(certificate);
|
|
1334
1239
|
try {
|
|
1335
1240
|
const commonName = (0, import_node_opcua_crypto5.exploreCertificate)(certificate).tbsCertificate.subject.commonName || "";
|
|
1336
1241
|
const sanitizedCommonName = commonName.replace(forbiddenChars, "_");
|
|
1337
|
-
return `${sanitizedCommonName}[${
|
|
1242
|
+
return `${sanitizedCommonName}[${fingerprint}]`;
|
|
1338
1243
|
} catch (_err) {
|
|
1339
|
-
return `invalid_certificate_[${
|
|
1244
|
+
return `invalid_certificate_[${fingerprint}]`;
|
|
1340
1245
|
}
|
|
1341
1246
|
}
|
|
1342
1247
|
function findMatchingIssuerKey(entries, wantedIssuerKey) {
|
|
@@ -1386,8 +1291,79 @@ var CertificateManagerState = /* @__PURE__ */ ((CertificateManagerState2) => {
|
|
|
1386
1291
|
CertificateManagerState2[CertificateManagerState2["Disposed"] = 4] = "Disposed";
|
|
1387
1292
|
return CertificateManagerState2;
|
|
1388
1293
|
})(CertificateManagerState || {});
|
|
1389
|
-
var CertificateManager = class {
|
|
1294
|
+
var CertificateManager = class _CertificateManager {
|
|
1295
|
+
// ── Global instance registry ─────────────────────────────────
|
|
1296
|
+
// Tracks all initialized CertificateManager instances so their
|
|
1297
|
+
// file watchers can be closed automatically on process exit,
|
|
1298
|
+
// even if the consumer forgets to call dispose().
|
|
1299
|
+
static #activeInstances = /* @__PURE__ */ new Set();
|
|
1300
|
+
static #cleanupInstalled = false;
|
|
1301
|
+
static #installProcessCleanup() {
|
|
1302
|
+
if (_CertificateManager.#cleanupInstalled) return;
|
|
1303
|
+
_CertificateManager.#cleanupInstalled = true;
|
|
1304
|
+
const closeDanglingWatchers = () => {
|
|
1305
|
+
for (const cm of _CertificateManager.#activeInstances) {
|
|
1306
|
+
for (const w of cm.#watchers) {
|
|
1307
|
+
try {
|
|
1308
|
+
w.close();
|
|
1309
|
+
} catch {
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
cm.#watchers.splice(0);
|
|
1313
|
+
cm.state = 4 /* Disposed */;
|
|
1314
|
+
}
|
|
1315
|
+
_CertificateManager.#activeInstances.clear();
|
|
1316
|
+
};
|
|
1317
|
+
process.on("beforeExit", closeDanglingWatchers);
|
|
1318
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
1319
|
+
process.once(signal, () => {
|
|
1320
|
+
closeDanglingWatchers();
|
|
1321
|
+
process.exit();
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Dispose **all** active CertificateManager instances,
|
|
1327
|
+
* closing their file watchers and freeing resources.
|
|
1328
|
+
*
|
|
1329
|
+
* This is mainly useful in test tear-down to ensure the
|
|
1330
|
+
* Node.js process can exit cleanly.
|
|
1331
|
+
*/
|
|
1332
|
+
static async disposeAll() {
|
|
1333
|
+
const instances = [..._CertificateManager.#activeInstances];
|
|
1334
|
+
await Promise.all(instances.map((cm) => cm.dispose()));
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Assert that all CertificateManager instances have been
|
|
1338
|
+
* properly disposed. Throws an Error listing the locations
|
|
1339
|
+
* of any leaked instances.
|
|
1340
|
+
*
|
|
1341
|
+
* Intended for use in test `afterAll()` / `afterEach()`
|
|
1342
|
+
* hooks to catch missing `dispose()` calls early.
|
|
1343
|
+
*
|
|
1344
|
+
* @example
|
|
1345
|
+
* ```ts
|
|
1346
|
+
* after(() => {
|
|
1347
|
+
* CertificateManager.checkAllDisposed();
|
|
1348
|
+
* });
|
|
1349
|
+
* ```
|
|
1350
|
+
*/
|
|
1351
|
+
static checkAllDisposed() {
|
|
1352
|
+
if (_CertificateManager.#activeInstances.size === 0) return;
|
|
1353
|
+
const locations = [..._CertificateManager.#activeInstances].map((cm) => cm.rootDir);
|
|
1354
|
+
throw new Error(
|
|
1355
|
+
`${_CertificateManager.#activeInstances.size} CertificateManager instance(s) not disposed:
|
|
1356
|
+
- ${locations.join("\n - ")}`
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
// ─────────────────────────────────────────────────────────────
|
|
1360
|
+
/**
|
|
1361
|
+
* When `true` (the default), any certificate that is not
|
|
1362
|
+
* already in the trusted or rejected store is automatically
|
|
1363
|
+
* written to the rejected folder the first time it is seen.
|
|
1364
|
+
*/
|
|
1390
1365
|
untrustUnknownCertificate = true;
|
|
1366
|
+
/** Current lifecycle state of this instance. */
|
|
1391
1367
|
state = 0 /* Uninitialized */;
|
|
1392
1368
|
/** @deprecated Use {@link folderPollingInterval} instead (typo fix). */
|
|
1393
1369
|
folderPoolingInterval = 5e3;
|
|
@@ -1398,13 +1374,14 @@ var CertificateManager = class {
|
|
|
1398
1374
|
set folderPollingInterval(value) {
|
|
1399
1375
|
this.folderPoolingInterval = value;
|
|
1400
1376
|
}
|
|
1377
|
+
/** RSA key size used when generating the private key. */
|
|
1401
1378
|
keySize;
|
|
1402
|
-
location;
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1379
|
+
#location;
|
|
1380
|
+
#watchers = [];
|
|
1381
|
+
#readCertificatesCalled = false;
|
|
1382
|
+
#filenameToHash = /* @__PURE__ */ new Map();
|
|
1383
|
+
#initializingPromise;
|
|
1384
|
+
#thumbs = {
|
|
1408
1385
|
rejected: /* @__PURE__ */ new Map(),
|
|
1409
1386
|
trusted: /* @__PURE__ */ new Map(),
|
|
1410
1387
|
issuers: {
|
|
@@ -1413,62 +1390,58 @@ var CertificateManager = class {
|
|
|
1413
1390
|
crl: /* @__PURE__ */ new Map(),
|
|
1414
1391
|
issuersCrl: /* @__PURE__ */ new Map()
|
|
1415
1392
|
};
|
|
1393
|
+
/**
|
|
1394
|
+
* Create a new CertificateManager.
|
|
1395
|
+
*
|
|
1396
|
+
* The constructor creates the root directory if it does not
|
|
1397
|
+
* exist but does **not** initialise the PKI store — call
|
|
1398
|
+
* {@link initialize} before using any other method.
|
|
1399
|
+
*
|
|
1400
|
+
* @param options - configuration options
|
|
1401
|
+
*/
|
|
1416
1402
|
constructor(options) {
|
|
1417
1403
|
options.keySize = options.keySize || 2048;
|
|
1418
1404
|
if (!options.location) {
|
|
1419
1405
|
throw new Error("CertificateManager: missing 'location' option");
|
|
1420
1406
|
}
|
|
1421
|
-
this
|
|
1407
|
+
this.#location = makePath(options.location, "");
|
|
1422
1408
|
this.keySize = options.keySize;
|
|
1423
1409
|
mkdirRecursiveSync(options.location);
|
|
1424
|
-
if (!import_node_fs9.default.existsSync(this
|
|
1425
|
-
throw new Error(`CertificateManager cannot access location ${this
|
|
1410
|
+
if (!import_node_fs9.default.existsSync(this.#location)) {
|
|
1411
|
+
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
1426
1412
|
}
|
|
1427
1413
|
}
|
|
1414
|
+
/** Path to the OpenSSL configuration file. */
|
|
1428
1415
|
get configFile() {
|
|
1429
1416
|
return import_node_path6.default.join(this.rootDir, "own/openssl.cnf");
|
|
1430
1417
|
}
|
|
1418
|
+
/** Root directory of the PKI store. */
|
|
1431
1419
|
get rootDir() {
|
|
1432
|
-
return this
|
|
1420
|
+
return this.#location;
|
|
1433
1421
|
}
|
|
1422
|
+
/** Path to the private key file (`own/private/private_key.pem`). */
|
|
1434
1423
|
get privateKey() {
|
|
1435
1424
|
return import_node_path6.default.join(this.rootDir, "own/private/private_key.pem");
|
|
1436
1425
|
}
|
|
1426
|
+
/** Path to the OpenSSL random seed file. */
|
|
1437
1427
|
get randomFile() {
|
|
1438
1428
|
return import_node_path6.default.join(this.rootDir, "./random.rnd");
|
|
1439
1429
|
}
|
|
1440
|
-
/**
|
|
1441
|
-
* returns the certificate status trusted/rejected
|
|
1442
|
-
* @param certificate
|
|
1443
|
-
*/
|
|
1444
|
-
async getCertificateStatus(certificate) {
|
|
1445
|
-
await this.initialize();
|
|
1446
|
-
let status = await this._checkRejectedOrTrusted(certificate);
|
|
1447
|
-
if (status === "unknown") {
|
|
1448
|
-
const pem = (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE");
|
|
1449
|
-
const fingerprint2 = makeFingerprint(certificate);
|
|
1450
|
-
const filename = import_node_path6.default.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
1451
|
-
await import_node_fs9.default.promises.writeFile(filename, pem);
|
|
1452
|
-
this._thumbs.rejected.set(fingerprint2, { certificate, filename });
|
|
1453
|
-
status = "rejected";
|
|
1454
|
-
}
|
|
1455
|
-
return status;
|
|
1456
|
-
}
|
|
1457
1430
|
/**
|
|
1458
1431
|
* Move a certificate to the rejected store.
|
|
1459
|
-
* If the certificate was previously trusted, it will be
|
|
1432
|
+
* If the certificate was previously trusted, it will be removed from the trusted folder.
|
|
1460
1433
|
* @param certificate - the DER-encoded certificate
|
|
1461
1434
|
*/
|
|
1462
1435
|
async rejectCertificate(certificate) {
|
|
1463
|
-
await this
|
|
1436
|
+
await this.#moveCertificate(certificate, "rejected");
|
|
1464
1437
|
}
|
|
1465
1438
|
/**
|
|
1466
1439
|
* Move a certificate to the trusted store.
|
|
1467
|
-
* If the certificate was previously rejected, it will be
|
|
1440
|
+
* If the certificate was previously rejected, it will be removed from the rejected folder.
|
|
1468
1441
|
* @param certificate - the DER-encoded certificate
|
|
1469
1442
|
*/
|
|
1470
1443
|
async trustCertificate(certificate) {
|
|
1471
|
-
await this
|
|
1444
|
+
await this.#moveCertificate(certificate, "trusted");
|
|
1472
1445
|
}
|
|
1473
1446
|
/** Path to the rejected certificates folder. */
|
|
1474
1447
|
get rejectedFolder() {
|
|
@@ -1490,6 +1463,13 @@ var CertificateManager = class {
|
|
|
1490
1463
|
get issuersCrlFolder() {
|
|
1491
1464
|
return import_node_path6.default.join(this.rootDir, "issuers/crl");
|
|
1492
1465
|
}
|
|
1466
|
+
/** Path to the own certificate folder. */
|
|
1467
|
+
get ownCertFolder() {
|
|
1468
|
+
return import_node_path6.default.join(this.rootDir, "own/certs");
|
|
1469
|
+
}
|
|
1470
|
+
get ownPrivateFolder() {
|
|
1471
|
+
return import_node_path6.default.join(this.rootDir, "own/private");
|
|
1472
|
+
}
|
|
1493
1473
|
/**
|
|
1494
1474
|
* Check if a certificate is in the trusted store.
|
|
1495
1475
|
* If the certificate is unknown and `untrustUnknownCertificate` is set,
|
|
@@ -1499,11 +1479,11 @@ var CertificateManager = class {
|
|
|
1499
1479
|
* or `"BadCertificateInvalid"` if the certificate cannot be parsed.
|
|
1500
1480
|
*/
|
|
1501
1481
|
async isCertificateTrusted(certificate) {
|
|
1502
|
-
const
|
|
1503
|
-
if (this.
|
|
1482
|
+
const fingerprint = makeFingerprint(certificate);
|
|
1483
|
+
if (this.#thumbs.trusted.has(fingerprint)) {
|
|
1504
1484
|
return "Good";
|
|
1505
1485
|
}
|
|
1506
|
-
if (!this.
|
|
1486
|
+
if (!this.#thumbs.rejected.has(fingerprint)) {
|
|
1507
1487
|
if (!this.untrustUnknownCertificate) {
|
|
1508
1488
|
return "Good";
|
|
1509
1489
|
}
|
|
@@ -1512,17 +1492,14 @@ var CertificateManager = class {
|
|
|
1512
1492
|
} catch (_err) {
|
|
1513
1493
|
return "BadCertificateInvalid";
|
|
1514
1494
|
}
|
|
1515
|
-
const filename = import_node_path6.default.join(
|
|
1516
|
-
this.rejectedFolder,
|
|
1517
|
-
`${buildIdealCertificateName(certificate)}.pem`
|
|
1518
|
-
);
|
|
1495
|
+
const filename = import_node_path6.default.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
1519
1496
|
debugLog("certificate has never been seen before and is now rejected (untrusted) ", filename);
|
|
1520
1497
|
await fsWriteFile(filename, (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE"));
|
|
1521
|
-
this.
|
|
1498
|
+
this.#thumbs.rejected.set(fingerprint, { certificate, filename });
|
|
1522
1499
|
}
|
|
1523
1500
|
return "BadCertificateUntrusted";
|
|
1524
1501
|
}
|
|
1525
|
-
async
|
|
1502
|
+
async #innerVerifyCertificateAsync(certificate, _isIssuer, level, options) {
|
|
1526
1503
|
if (level >= 5) {
|
|
1527
1504
|
return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
|
|
1528
1505
|
}
|
|
@@ -1557,7 +1534,7 @@ var CertificateManager = class {
|
|
|
1557
1534
|
} else {
|
|
1558
1535
|
debugLog(" the issuer certificate has been found in the issuer.cert folder !");
|
|
1559
1536
|
}
|
|
1560
|
-
const issuerStatus = await this
|
|
1537
|
+
const issuerStatus = await this.#innerVerifyCertificateAsync(issuerCertificate, true, level + 1, options);
|
|
1561
1538
|
if (issuerStatus === "BadCertificateRevocationUnknown" /* BadCertificateRevocationUnknown */) {
|
|
1562
1539
|
return "BadCertificateIssuerRevocationUnknown" /* BadCertificateIssuerRevocationUnknown */;
|
|
1563
1540
|
}
|
|
@@ -1591,7 +1568,7 @@ var CertificateManager = class {
|
|
|
1591
1568
|
debugLog("revokedStatus", revokedStatus);
|
|
1592
1569
|
return revokedStatus;
|
|
1593
1570
|
}
|
|
1594
|
-
const issuerTrustedStatus = await this
|
|
1571
|
+
const issuerTrustedStatus = await this.#checkRejectedOrTrusted(issuerCertificate);
|
|
1595
1572
|
debugLog("issuerTrustedStatus", issuerTrustedStatus);
|
|
1596
1573
|
if (issuerTrustedStatus === "unknown") {
|
|
1597
1574
|
hasTrustedIssuer = false;
|
|
@@ -1610,9 +1587,11 @@ var CertificateManager = class {
|
|
|
1610
1587
|
debugLog("revokedStatus of self signed certificate:", revokedStatus);
|
|
1611
1588
|
}
|
|
1612
1589
|
}
|
|
1613
|
-
const status = await this
|
|
1590
|
+
const status = await this.#checkRejectedOrTrusted(certificate);
|
|
1614
1591
|
if (status === "rejected") {
|
|
1615
|
-
|
|
1592
|
+
if (!(options.acceptCertificateWithValidIssuerChain && hasValidIssuer && hasTrustedIssuer)) {
|
|
1593
|
+
return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
|
|
1594
|
+
}
|
|
1616
1595
|
}
|
|
1617
1596
|
const _c2 = chain[1] ? (0, import_node_opcua_crypto5.exploreCertificateInfo)(chain[1]) : "non";
|
|
1618
1597
|
debugLog("chain[1] info=", _c2);
|
|
@@ -1645,49 +1624,74 @@ var CertificateManager = class {
|
|
|
1645
1624
|
if (!hasValidIssuer) {
|
|
1646
1625
|
return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
|
|
1647
1626
|
}
|
|
1627
|
+
if (!options.acceptCertificateWithValidIssuerChain) {
|
|
1628
|
+
return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
|
|
1629
|
+
}
|
|
1648
1630
|
return isTimeInvalid ? "BadCertificateTimeInvalid" /* BadCertificateTimeInvalid */ : "Good" /* Good */;
|
|
1649
1631
|
} else {
|
|
1650
1632
|
return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
|
|
1651
1633
|
}
|
|
1652
1634
|
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Internal verification hook called by {@link verifyCertificate}.
|
|
1637
|
+
*
|
|
1638
|
+
* Subclasses can override this to inject additional validation
|
|
1639
|
+
* logic (e.g. application-level policy checks) while still
|
|
1640
|
+
* delegating to the default chain/CRL/trust verification.
|
|
1641
|
+
*
|
|
1642
|
+
* @param certificate - the DER-encoded certificate to verify
|
|
1643
|
+
* @param options - verification options forwarded from the
|
|
1644
|
+
* public API
|
|
1645
|
+
* @returns the verification status code
|
|
1646
|
+
*/
|
|
1653
1647
|
async verifyCertificateAsync(certificate, options) {
|
|
1654
|
-
const status1 = await this
|
|
1648
|
+
const status1 = await this.#innerVerifyCertificateAsync(certificate, false, 0, options);
|
|
1655
1649
|
return status1;
|
|
1656
1650
|
}
|
|
1657
1651
|
/**
|
|
1658
|
-
* Verify certificate
|
|
1659
|
-
*
|
|
1660
|
-
*
|
|
1652
|
+
* Verify a certificate against the PKI trust store.
|
|
1653
|
+
*
|
|
1654
|
+
* This performs a full validation including trust status,
|
|
1655
|
+
* issuer chain, CRL revocation checks, and time validity.
|
|
1656
|
+
*
|
|
1657
|
+
* @param certificate - the DER-encoded certificate to verify
|
|
1658
|
+
* @param options - optional flags to relax validation rules
|
|
1659
|
+
* @returns the verification status code
|
|
1661
1660
|
*/
|
|
1662
1661
|
async verifyCertificate(certificate, options) {
|
|
1663
1662
|
if (!certificate) {
|
|
1664
1663
|
return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
|
|
1665
1664
|
}
|
|
1666
|
-
|
|
1665
|
+
try {
|
|
1666
|
+
const status = await this.verifyCertificateAsync(certificate, options || {});
|
|
1667
|
+
return status;
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
warningLog(`verifyCertificate error: ${error.message}`);
|
|
1670
|
+
return "BadCertificateInvalid" /* BadCertificateInvalid */;
|
|
1671
|
+
}
|
|
1667
1672
|
}
|
|
1668
|
-
|
|
1669
|
-
*
|
|
1670
|
-
*
|
|
1671
|
-
* +---> trusted
|
|
1672
|
-
* +---> rejected
|
|
1673
|
-
* +---> own
|
|
1674
|
-
* +---> cert
|
|
1675
|
-
* +---> own
|
|
1673
|
+
/**
|
|
1674
|
+
* Initialize the PKI directory structure, generate the
|
|
1675
|
+
* private key (if missing), and start file-system watchers.
|
|
1676
1676
|
*
|
|
1677
|
+
* This method is idempotent — subsequent calls are no-ops.
|
|
1678
|
+
* It must be called before any certificate operations.
|
|
1677
1679
|
*/
|
|
1678
1680
|
async initialize() {
|
|
1679
1681
|
if (this.state !== 0 /* Uninitialized */) {
|
|
1680
1682
|
return;
|
|
1681
1683
|
}
|
|
1682
1684
|
this.state = 1 /* Initializing */;
|
|
1683
|
-
this
|
|
1684
|
-
await this
|
|
1685
|
-
this
|
|
1685
|
+
this.#initializingPromise = this.#initialize();
|
|
1686
|
+
await this.#initializingPromise;
|
|
1687
|
+
this.#initializingPromise = void 0;
|
|
1686
1688
|
this.state = 2 /* Initialized */;
|
|
1689
|
+
_CertificateManager.#activeInstances.add(this);
|
|
1690
|
+
_CertificateManager.#installProcessCleanup();
|
|
1687
1691
|
}
|
|
1688
|
-
async
|
|
1692
|
+
async #initialize() {
|
|
1689
1693
|
this.state = 1 /* Initializing */;
|
|
1690
|
-
const pkiDir = this
|
|
1694
|
+
const pkiDir = this.#location;
|
|
1691
1695
|
mkdirRecursiveSync(pkiDir);
|
|
1692
1696
|
mkdirRecursiveSync(import_node_path6.default.join(pkiDir, "own"));
|
|
1693
1697
|
mkdirRecursiveSync(import_node_path6.default.join(pkiDir, "own/certs"));
|
|
@@ -1710,13 +1714,13 @@ var CertificateManager = class {
|
|
|
1710
1714
|
if (!import_node_fs9.default.existsSync(this.privateKey)) {
|
|
1711
1715
|
debugLog("generating private key ...");
|
|
1712
1716
|
await (0, import_node_opcua_crypto5.generatePrivateKeyFile)(this.privateKey, this.keySize);
|
|
1713
|
-
await this
|
|
1717
|
+
await this.#readCertificates();
|
|
1714
1718
|
} else {
|
|
1715
|
-
await this
|
|
1719
|
+
await this.#readCertificates();
|
|
1716
1720
|
}
|
|
1717
1721
|
});
|
|
1718
1722
|
} else {
|
|
1719
|
-
await this
|
|
1723
|
+
await this.#readCertificates();
|
|
1720
1724
|
}
|
|
1721
1725
|
}
|
|
1722
1726
|
/**
|
|
@@ -1733,19 +1737,20 @@ var CertificateManager = class {
|
|
|
1733
1737
|
return;
|
|
1734
1738
|
}
|
|
1735
1739
|
if (this.state === 1 /* Initializing */) {
|
|
1736
|
-
if (this
|
|
1737
|
-
await this
|
|
1740
|
+
if (this.#initializingPromise) {
|
|
1741
|
+
await this.#initializingPromise;
|
|
1738
1742
|
}
|
|
1739
1743
|
}
|
|
1740
1744
|
try {
|
|
1741
1745
|
this.state = 3 /* Disposing */;
|
|
1742
|
-
await Promise.all(this.
|
|
1743
|
-
this.
|
|
1746
|
+
await Promise.all(this.#watchers.map((w) => w.close()));
|
|
1747
|
+
this.#watchers.forEach((w) => {
|
|
1744
1748
|
w.removeAllListeners();
|
|
1745
1749
|
});
|
|
1746
|
-
this.
|
|
1750
|
+
this.#watchers.splice(0);
|
|
1747
1751
|
} finally {
|
|
1748
1752
|
this.state = 4 /* Disposed */;
|
|
1753
|
+
_CertificateManager.#activeInstances.delete(this);
|
|
1749
1754
|
}
|
|
1750
1755
|
}
|
|
1751
1756
|
/**
|
|
@@ -1758,19 +1763,19 @@ var CertificateManager = class {
|
|
|
1758
1763
|
* state without waiting for file-system events.
|
|
1759
1764
|
*/
|
|
1760
1765
|
async reloadCertificates() {
|
|
1761
|
-
await Promise.all(this.
|
|
1762
|
-
for (const w of this
|
|
1766
|
+
await Promise.all(this.#watchers.map((w) => w.close()));
|
|
1767
|
+
for (const w of this.#watchers) {
|
|
1763
1768
|
w.removeAllListeners();
|
|
1764
1769
|
}
|
|
1765
|
-
this.
|
|
1766
|
-
this.
|
|
1767
|
-
this.
|
|
1768
|
-
this.
|
|
1769
|
-
this.
|
|
1770
|
-
this.
|
|
1771
|
-
this.
|
|
1772
|
-
this
|
|
1773
|
-
await this
|
|
1770
|
+
this.#watchers.splice(0);
|
|
1771
|
+
this.#thumbs.rejected.clear();
|
|
1772
|
+
this.#thumbs.trusted.clear();
|
|
1773
|
+
this.#thumbs.issuers.certs.clear();
|
|
1774
|
+
this.#thumbs.crl.clear();
|
|
1775
|
+
this.#thumbs.issuersCrl.clear();
|
|
1776
|
+
this.#filenameToHash.clear();
|
|
1777
|
+
this.#readCertificatesCalled = false;
|
|
1778
|
+
await this.#readCertificates();
|
|
1774
1779
|
}
|
|
1775
1780
|
async withLock2(action) {
|
|
1776
1781
|
const lockFileName = import_node_path6.default.join(this.rootDir, "mutex.lock");
|
|
@@ -1779,9 +1784,13 @@ var CertificateManager = class {
|
|
|
1779
1784
|
});
|
|
1780
1785
|
}
|
|
1781
1786
|
/**
|
|
1787
|
+
* Create a self-signed certificate for this PKI's private key.
|
|
1782
1788
|
*
|
|
1783
|
-
*
|
|
1789
|
+
* The certificate is written to `params.outputFile` or
|
|
1790
|
+
* `own/certs/self_signed_certificate.pem` by default.
|
|
1784
1791
|
*
|
|
1792
|
+
* @param params - certificate parameters (subject, SANs,
|
|
1793
|
+
* validity, etc.)
|
|
1785
1794
|
*/
|
|
1786
1795
|
async createSelfSignedCertificate(params) {
|
|
1787
1796
|
if (typeof params.applicationUri !== "string") {
|
|
@@ -1801,6 +1810,16 @@ var CertificateManager = class {
|
|
|
1801
1810
|
await createSelfSignedCertificate(certificateFilename, _params);
|
|
1802
1811
|
});
|
|
1803
1812
|
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Create a Certificate Signing Request (CSR) using this
|
|
1815
|
+
* PKI's private key and configuration.
|
|
1816
|
+
*
|
|
1817
|
+
* The CSR file is written to `own/certs/` with a timestamped
|
|
1818
|
+
* filename.
|
|
1819
|
+
*
|
|
1820
|
+
* @param params - CSR parameters (subject, SANs)
|
|
1821
|
+
* @returns the filesystem path to the generated CSR file
|
|
1822
|
+
*/
|
|
1804
1823
|
async createCertificateRequest(params) {
|
|
1805
1824
|
if (!params) {
|
|
1806
1825
|
throw new Error("params is required");
|
|
@@ -1814,8 +1833,8 @@ var CertificateManager = class {
|
|
|
1814
1833
|
_params.privateKey = import_node_path6.default.resolve(this.privateKey);
|
|
1815
1834
|
return await this.withLock2(async () => {
|
|
1816
1835
|
const now = /* @__PURE__ */ new Date();
|
|
1817
|
-
const
|
|
1818
|
-
const certificateSigningRequestFilename = import_node_path6.default.join(this.rootDir, "own/certs", `certificate_${
|
|
1836
|
+
const today = `${now.toISOString().slice(0, 10)}_${now.getTime()}`;
|
|
1837
|
+
const certificateSigningRequestFilename = import_node_path6.default.join(this.rootDir, "own/certs", `certificate_${today}.csr`);
|
|
1819
1838
|
await createCertificateSigningRequestAsync(certificateSigningRequestFilename, _params);
|
|
1820
1839
|
return certificateSigningRequestFilename;
|
|
1821
1840
|
});
|
|
@@ -1836,13 +1855,13 @@ var CertificateManager = class {
|
|
|
1836
1855
|
}
|
|
1837
1856
|
}
|
|
1838
1857
|
const pemCertificate = (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE");
|
|
1839
|
-
const
|
|
1840
|
-
if (this.
|
|
1858
|
+
const fingerprint = makeFingerprint(certificate);
|
|
1859
|
+
if (this.#thumbs.issuers.certs.has(fingerprint)) {
|
|
1841
1860
|
return "Good" /* Good */;
|
|
1842
1861
|
}
|
|
1843
1862
|
const filename = import_node_path6.default.join(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
|
|
1844
1863
|
await import_node_fs9.default.promises.writeFile(filename, pemCertificate, "ascii");
|
|
1845
|
-
this.
|
|
1864
|
+
this.#thumbs.issuers.certs.set(fingerprint, { certificate, filename });
|
|
1846
1865
|
if (addInTrustList) {
|
|
1847
1866
|
await this.trustCertificate(certificate);
|
|
1848
1867
|
}
|
|
@@ -1856,7 +1875,7 @@ var CertificateManager = class {
|
|
|
1856
1875
|
async addRevocationList(crl, target = "issuers") {
|
|
1857
1876
|
return await this.withLock2(async () => {
|
|
1858
1877
|
try {
|
|
1859
|
-
const index = target === "trusted" ? this.
|
|
1878
|
+
const index = target === "trusted" ? this.#thumbs.crl : this.#thumbs.issuersCrl;
|
|
1860
1879
|
const folder = target === "trusted" ? this.crlFolder : this.issuersCrlFolder;
|
|
1861
1880
|
const crlInfo = (0, import_node_opcua_crypto5.exploreCertificateRevocationList)(crl);
|
|
1862
1881
|
const key = crlInfo.tbsCertList.issuerFingerprint;
|
|
@@ -1866,8 +1885,8 @@ var CertificateManager = class {
|
|
|
1866
1885
|
const pemCertificate = (0, import_node_opcua_crypto5.toPem)(crl, "X509 CRL");
|
|
1867
1886
|
const filename = import_node_path6.default.join(folder, `crl_${buildIdealCertificateName(crl)}.pem`);
|
|
1868
1887
|
await import_node_fs9.default.promises.writeFile(filename, pemCertificate, "ascii");
|
|
1869
|
-
await this
|
|
1870
|
-
await this
|
|
1888
|
+
await this.#onCrlFileAdded(index, filename);
|
|
1889
|
+
await this.#waitAndCheckCRLProcessingStatus();
|
|
1871
1890
|
return "Good" /* Good */;
|
|
1872
1891
|
} catch (err) {
|
|
1873
1892
|
debugLog(err);
|
|
@@ -1899,10 +1918,10 @@ var CertificateManager = class {
|
|
|
1899
1918
|
index.clear();
|
|
1900
1919
|
};
|
|
1901
1920
|
if (target === "issuers" || target === "all") {
|
|
1902
|
-
await clearFolder(this.issuersCrlFolder, this.
|
|
1921
|
+
await clearFolder(this.issuersCrlFolder, this.#thumbs.issuersCrl);
|
|
1903
1922
|
}
|
|
1904
1923
|
if (target === "trusted" || target === "all") {
|
|
1905
|
-
await clearFolder(this.crlFolder, this.
|
|
1924
|
+
await clearFolder(this.crlFolder, this.#thumbs.crl);
|
|
1906
1925
|
}
|
|
1907
1926
|
}
|
|
1908
1927
|
/**
|
|
@@ -1911,9 +1930,9 @@ var CertificateManager = class {
|
|
|
1911
1930
|
* @param thumbprint - hex-encoded SHA-1 thumbprint (lowercase)
|
|
1912
1931
|
*/
|
|
1913
1932
|
async hasIssuer(thumbprint) {
|
|
1914
|
-
await this
|
|
1933
|
+
await this.#readCertificates();
|
|
1915
1934
|
const normalized = thumbprint.toLowerCase();
|
|
1916
|
-
return this.
|
|
1935
|
+
return this.#thumbs.issuers.certs.has(normalized);
|
|
1917
1936
|
}
|
|
1918
1937
|
/**
|
|
1919
1938
|
* Remove a trusted certificate identified by its SHA-1 thumbprint.
|
|
@@ -1923,9 +1942,9 @@ var CertificateManager = class {
|
|
|
1923
1942
|
* @returns the removed certificate buffer, or `null` if not found
|
|
1924
1943
|
*/
|
|
1925
1944
|
async removeTrustedCertificate(thumbprint) {
|
|
1926
|
-
await this
|
|
1945
|
+
await this.#readCertificates();
|
|
1927
1946
|
const normalized = thumbprint.toLowerCase();
|
|
1928
|
-
const entry = this.
|
|
1947
|
+
const entry = this.#thumbs.trusted.get(normalized);
|
|
1929
1948
|
if (!entry) {
|
|
1930
1949
|
return null;
|
|
1931
1950
|
}
|
|
@@ -1936,7 +1955,7 @@ var CertificateManager = class {
|
|
|
1936
1955
|
throw err;
|
|
1937
1956
|
}
|
|
1938
1957
|
}
|
|
1939
|
-
this.
|
|
1958
|
+
this.#thumbs.trusted.delete(normalized);
|
|
1940
1959
|
return entry.certificate;
|
|
1941
1960
|
}
|
|
1942
1961
|
/**
|
|
@@ -1947,9 +1966,9 @@ var CertificateManager = class {
|
|
|
1947
1966
|
* @returns the removed certificate buffer, or `null` if not found
|
|
1948
1967
|
*/
|
|
1949
1968
|
async removeIssuer(thumbprint) {
|
|
1950
|
-
await this
|
|
1969
|
+
await this.#readCertificates();
|
|
1951
1970
|
const normalized = thumbprint.toLowerCase();
|
|
1952
|
-
const entry = this.
|
|
1971
|
+
const entry = this.#thumbs.issuers.certs.get(normalized);
|
|
1953
1972
|
if (!entry) {
|
|
1954
1973
|
return null;
|
|
1955
1974
|
}
|
|
@@ -1960,7 +1979,7 @@ var CertificateManager = class {
|
|
|
1960
1979
|
throw err;
|
|
1961
1980
|
}
|
|
1962
1981
|
}
|
|
1963
|
-
this.
|
|
1982
|
+
this.#thumbs.issuers.certs.delete(normalized);
|
|
1964
1983
|
return entry.certificate;
|
|
1965
1984
|
}
|
|
1966
1985
|
/**
|
|
@@ -1987,10 +2006,10 @@ var CertificateManager = class {
|
|
|
1987
2006
|
index.delete(issuerFingerprint);
|
|
1988
2007
|
};
|
|
1989
2008
|
if (target === "issuers" || target === "all") {
|
|
1990
|
-
await processIndex(this.
|
|
2009
|
+
await processIndex(this.#thumbs.issuersCrl);
|
|
1991
2010
|
}
|
|
1992
2011
|
if (target === "trusted" || target === "all") {
|
|
1993
|
-
await processIndex(this.
|
|
2012
|
+
await processIndex(this.#thumbs.crl);
|
|
1994
2013
|
}
|
|
1995
2014
|
}
|
|
1996
2015
|
/**
|
|
@@ -2059,8 +2078,8 @@ var CertificateManager = class {
|
|
|
2059
2078
|
* signed by this issuer.
|
|
2060
2079
|
*/
|
|
2061
2080
|
async isIssuerInUseByTrustedCertificate(issuerCertificate) {
|
|
2062
|
-
await this
|
|
2063
|
-
for (const entry of this.
|
|
2081
|
+
await this.#readCertificates();
|
|
2082
|
+
for (const entry of this.#thumbs.trusted.values()) {
|
|
2064
2083
|
if (!entry.certificate) continue;
|
|
2065
2084
|
try {
|
|
2066
2085
|
if ((0, import_node_opcua_crypto5.verifyCertificateSignature)(entry.certificate, issuerCertificate)) {
|
|
@@ -2095,7 +2114,7 @@ var CertificateManager = class {
|
|
|
2095
2114
|
debugLog("Certificate has no extension 3");
|
|
2096
2115
|
return null;
|
|
2097
2116
|
}
|
|
2098
|
-
const issuerCertificates = [...this.
|
|
2117
|
+
const issuerCertificates = [...this.#thumbs.issuers.certs.values()];
|
|
2099
2118
|
const selectedIssuerCertificates = findMatchingIssuerKey(issuerCertificates, wantedIssuerKey);
|
|
2100
2119
|
if (selectedIssuerCertificates.length > 0) {
|
|
2101
2120
|
if (selectedIssuerCertificates.length > 1) {
|
|
@@ -2103,7 +2122,7 @@ var CertificateManager = class {
|
|
|
2103
2122
|
}
|
|
2104
2123
|
return selectedIssuerCertificates[0].certificate || null;
|
|
2105
2124
|
}
|
|
2106
|
-
const trustedCertificates = [...this.
|
|
2125
|
+
const trustedCertificates = [...this.#thumbs.trusted.values()];
|
|
2107
2126
|
const selectedTrustedCertificates = findMatchingIssuerKey(trustedCertificates, wantedIssuerKey);
|
|
2108
2127
|
if (selectedTrustedCertificates.length > 1) {
|
|
2109
2128
|
warningLog(
|
|
@@ -2115,52 +2134,78 @@ var CertificateManager = class {
|
|
|
2115
2134
|
return selectedTrustedCertificates.length > 0 ? selectedTrustedCertificates[0].certificate : null;
|
|
2116
2135
|
}
|
|
2117
2136
|
/**
|
|
2137
|
+
*
|
|
2138
|
+
* check if the certificate explicitly appear in the trust list, the reject list or none.
|
|
2139
|
+
* In case of being in the reject and trusted list at the same time is consider: rejected.
|
|
2118
2140
|
* @internal
|
|
2119
2141
|
* @private
|
|
2120
2142
|
*/
|
|
2121
|
-
async
|
|
2122
|
-
const
|
|
2123
|
-
debugLog("
|
|
2124
|
-
await this
|
|
2125
|
-
if (this.
|
|
2143
|
+
async #checkRejectedOrTrusted(certificate) {
|
|
2144
|
+
const fingerprint = makeFingerprint(certificate);
|
|
2145
|
+
debugLog("#checkRejectedOrTrusted fingerprint ", short(fingerprint));
|
|
2146
|
+
await this.#readCertificates();
|
|
2147
|
+
if (this.#thumbs.rejected.has(fingerprint)) {
|
|
2126
2148
|
return "rejected";
|
|
2127
2149
|
}
|
|
2128
|
-
if (this.
|
|
2150
|
+
if (this.#thumbs.trusted.has(fingerprint)) {
|
|
2129
2151
|
return "trusted";
|
|
2130
2152
|
}
|
|
2131
2153
|
return "unknown";
|
|
2132
2154
|
}
|
|
2133
|
-
async
|
|
2155
|
+
async #moveCertificate(certificate, newStatus) {
|
|
2134
2156
|
await this.withLock2(async () => {
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
|
|
2157
|
+
const fingerprint = makeFingerprint(certificate);
|
|
2158
|
+
let status = await this.#checkRejectedOrTrusted(certificate);
|
|
2159
|
+
if (status === "unknown") {
|
|
2160
|
+
const pem = (0, import_node_opcua_crypto5.toPem)(certificate, "CERTIFICATE");
|
|
2161
|
+
const filename = import_node_path6.default.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
2162
|
+
await import_node_fs9.default.promises.writeFile(filename, pem);
|
|
2163
|
+
this.#thumbs.rejected.set(fingerprint, { certificate, filename });
|
|
2164
|
+
status = "rejected";
|
|
2165
|
+
}
|
|
2166
|
+
debugLog("#moveCertificate", fingerprint.substring(0, 10), "from", status, "to", newStatus);
|
|
2138
2167
|
if (status !== "rejected" && status !== "trusted") {
|
|
2139
|
-
throw new Error(
|
|
2168
|
+
throw new Error(`#moveCertificate: unexpected status '${status}' for certificate ${fingerprint.substring(0, 10)}`);
|
|
2140
2169
|
}
|
|
2141
2170
|
if (status !== newStatus) {
|
|
2142
|
-
const indexSrc = status === "rejected" ? this.
|
|
2143
|
-
const srcEntry = indexSrc.get(
|
|
2171
|
+
const indexSrc = status === "rejected" ? this.#thumbs.rejected : this.#thumbs.trusted;
|
|
2172
|
+
const srcEntry = indexSrc.get(fingerprint);
|
|
2144
2173
|
if (!srcEntry) {
|
|
2145
|
-
debugLog(" cannot find certificate ",
|
|
2146
|
-
throw new Error(
|
|
2174
|
+
debugLog(" cannot find certificate ", fingerprint.substring(0, 10), " in", status);
|
|
2175
|
+
throw new Error(`#moveCertificate: certificate ${fingerprint.substring(0, 10)} not found in ${status} index`);
|
|
2147
2176
|
}
|
|
2148
2177
|
const destFolder = newStatus === "trusted" ? this.trustedFolder : this.rejectedFolder;
|
|
2149
2178
|
const certificateDest = import_node_path6.default.join(destFolder, import_node_path6.default.basename(srcEntry.filename));
|
|
2150
|
-
debugLog("
|
|
2151
|
-
debugLog("
|
|
2179
|
+
debugLog("#moveCertificate", fingerprint.substring(0, 10), "old name", srcEntry.filename);
|
|
2180
|
+
debugLog("#moveCertificate", fingerprint.substring(0, 10), "new name", certificateDest);
|
|
2152
2181
|
await import_node_fs9.default.promises.rename(srcEntry.filename, certificateDest);
|
|
2153
|
-
indexSrc.delete(
|
|
2154
|
-
const indexDest = newStatus === "trusted" ? this.
|
|
2155
|
-
indexDest.set(
|
|
2182
|
+
indexSrc.delete(fingerprint);
|
|
2183
|
+
const indexDest = newStatus === "trusted" ? this.#thumbs.trusted : this.#thumbs.rejected;
|
|
2184
|
+
indexDest.set(fingerprint, { certificate, filename: certificateDest });
|
|
2156
2185
|
}
|
|
2157
2186
|
});
|
|
2158
2187
|
}
|
|
2159
|
-
|
|
2188
|
+
#findAssociatedCRLs(issuerCertificate) {
|
|
2160
2189
|
const issuerCertificateInfo = (0, import_node_opcua_crypto5.exploreCertificate)(issuerCertificate);
|
|
2161
2190
|
const key = issuerCertificateInfo.tbsCertificate.subjectFingerPrint;
|
|
2162
|
-
return this.
|
|
2191
|
+
return this.#thumbs.issuersCrl.get(key) ?? this.#thumbs.crl.get(key) ?? null;
|
|
2163
2192
|
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Check whether a certificate has been revoked by its issuer's CRL.
|
|
2195
|
+
*
|
|
2196
|
+
* - Self-signed certificates are never considered revoked.
|
|
2197
|
+
* - If no `issuerCertificate` is provided, the method attempts
|
|
2198
|
+
* to find it via {@link findIssuerCertificate}.
|
|
2199
|
+
*
|
|
2200
|
+
* @param certificate - the DER-encoded certificate to check
|
|
2201
|
+
* @param issuerCertificate - optional issuer certificate; looked
|
|
2202
|
+
* up automatically when omitted
|
|
2203
|
+
* @returns `Good` if not revoked, `BadCertificateRevoked` if the
|
|
2204
|
+
* serial number appears in a CRL,
|
|
2205
|
+
* `BadCertificateRevocationUnknown` if no CRL is available,
|
|
2206
|
+
* or `BadCertificateChainIncomplete` if the issuer cannot be
|
|
2207
|
+
* found.
|
|
2208
|
+
*/
|
|
2164
2209
|
async isCertificateRevoked(certificate, issuerCertificate) {
|
|
2165
2210
|
if (isSelfSigned3(certificate)) {
|
|
2166
2211
|
return "Good" /* Good */;
|
|
@@ -2171,42 +2216,42 @@ var CertificateManager = class {
|
|
|
2171
2216
|
if (!issuerCertificate) {
|
|
2172
2217
|
return "BadCertificateChainIncomplete" /* BadCertificateChainIncomplete */;
|
|
2173
2218
|
}
|
|
2174
|
-
const crls = this
|
|
2219
|
+
const crls = this.#findAssociatedCRLs(issuerCertificate);
|
|
2175
2220
|
if (!crls) {
|
|
2176
2221
|
return "BadCertificateRevocationUnknown" /* BadCertificateRevocationUnknown */;
|
|
2177
2222
|
}
|
|
2178
2223
|
const certInfo = (0, import_node_opcua_crypto5.exploreCertificate)(certificate);
|
|
2179
2224
|
const serialNumber = certInfo.tbsCertificate.serialNumber || certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.serial || "";
|
|
2180
2225
|
const key = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint || "<unknown>";
|
|
2181
|
-
const crl2 = this.
|
|
2226
|
+
const crl2 = this.#thumbs.crl.get(key) ?? null;
|
|
2182
2227
|
if (crls.serialNumbers[serialNumber] || crl2?.serialNumbers[serialNumber]) {
|
|
2183
2228
|
return "BadCertificateRevoked" /* BadCertificateRevoked */;
|
|
2184
2229
|
}
|
|
2185
2230
|
return "Good" /* Good */;
|
|
2186
2231
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
queue = [];
|
|
2190
|
-
|
|
2191
|
-
this
|
|
2192
|
-
this
|
|
2193
|
-
if (this
|
|
2194
|
-
this
|
|
2232
|
+
#pendingCrlToProcess = 0;
|
|
2233
|
+
#onCrlProcess;
|
|
2234
|
+
#queue = [];
|
|
2235
|
+
#onCrlFileAdded(index, filename) {
|
|
2236
|
+
this.#queue.push({ index, filename });
|
|
2237
|
+
this.#pendingCrlToProcess += 1;
|
|
2238
|
+
if (this.#pendingCrlToProcess === 1) {
|
|
2239
|
+
this.#processNextCrl();
|
|
2195
2240
|
}
|
|
2196
2241
|
}
|
|
2197
|
-
async
|
|
2242
|
+
async #processNextCrl() {
|
|
2198
2243
|
try {
|
|
2199
|
-
const nextCRL = this
|
|
2244
|
+
const nextCRL = this.#queue.shift();
|
|
2200
2245
|
if (!nextCRL) return;
|
|
2201
2246
|
const { index, filename } = nextCRL;
|
|
2202
2247
|
const crl = await (0, import_node_opcua_crypto5.readCertificateRevocationList)(filename);
|
|
2203
2248
|
const crlInfo = (0, import_node_opcua_crypto5.exploreCertificateRevocationList)(crl);
|
|
2204
2249
|
debugLog(import_chalk6.default.cyan("add CRL in folder "), filename);
|
|
2205
|
-
const
|
|
2206
|
-
if (!index.has(
|
|
2207
|
-
index.set(
|
|
2250
|
+
const fingerprint = crlInfo.tbsCertList.issuerFingerprint;
|
|
2251
|
+
if (!index.has(fingerprint)) {
|
|
2252
|
+
index.set(fingerprint, { crls: [], serialNumbers: {} });
|
|
2208
2253
|
}
|
|
2209
|
-
const data = index.get(
|
|
2254
|
+
const data = index.get(fingerprint) || { crls: [], serialNumbers: {} };
|
|
2210
2255
|
data.crls.push({ crlInfo, filename });
|
|
2211
2256
|
for (const revokedCertificate of crlInfo.tbsCertList.revokedCertificates) {
|
|
2212
2257
|
const serialNumber = revokedCertificate.userCertificate;
|
|
@@ -2214,875 +2259,208 @@ var CertificateManager = class {
|
|
|
2214
2259
|
data.serialNumbers[serialNumber] = revokedCertificate.revocationDate;
|
|
2215
2260
|
}
|
|
2216
2261
|
}
|
|
2217
|
-
debugLog(import_chalk6.default.cyan("CRL"),
|
|
2262
|
+
debugLog(import_chalk6.default.cyan("CRL"), fingerprint, "serial numbers = ", Object.keys(data.serialNumbers));
|
|
2218
2263
|
} catch (err) {
|
|
2219
2264
|
debugLog("CRL filename error =");
|
|
2220
2265
|
debugLog(err);
|
|
2221
2266
|
}
|
|
2222
|
-
this
|
|
2223
|
-
if (this
|
|
2224
|
-
if (this
|
|
2225
|
-
this
|
|
2226
|
-
this
|
|
2267
|
+
this.#pendingCrlToProcess -= 1;
|
|
2268
|
+
if (this.#pendingCrlToProcess === 0) {
|
|
2269
|
+
if (this.#onCrlProcess) {
|
|
2270
|
+
this.#onCrlProcess();
|
|
2271
|
+
this.#onCrlProcess = void 0;
|
|
2227
2272
|
}
|
|
2228
2273
|
} else {
|
|
2229
|
-
this
|
|
2274
|
+
this.#processNextCrl();
|
|
2230
2275
|
}
|
|
2231
2276
|
}
|
|
2232
|
-
async
|
|
2233
|
-
if (this
|
|
2277
|
+
async #readCertificates() {
|
|
2278
|
+
if (this.#readCertificatesCalled) {
|
|
2234
2279
|
return;
|
|
2235
2280
|
}
|
|
2236
|
-
this
|
|
2281
|
+
this.#readCertificatesCalled = true;
|
|
2237
2282
|
const usePolling = process.env.OPCUA_PKI_USE_POLLING === "true";
|
|
2238
|
-
const
|
|
2283
|
+
const chokidarOptions = {
|
|
2239
2284
|
usePolling,
|
|
2240
2285
|
...usePolling ? { interval: Math.min(10 * 60 * 1e3, Math.max(100, this.folderPoolingInterval)) } : {},
|
|
2241
2286
|
persistent: false
|
|
2242
2287
|
};
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
index.delete(key);
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
});
|
|
2254
|
-
w.on("add", (filename) => {
|
|
2255
|
-
this._on_crl_file_added(index, filename);
|
|
2256
|
-
});
|
|
2257
|
-
w.on("change", (changedPath) => {
|
|
2258
|
-
debugLog("change in folder ", folder, changedPath);
|
|
2259
|
-
});
|
|
2260
|
-
this._watchers.push(w);
|
|
2261
|
-
w.on("ready", () => {
|
|
2262
|
-
resolve();
|
|
2263
|
-
});
|
|
2288
|
+
const createUnreffedWatcher = (folder) => {
|
|
2289
|
+
const capturedHandles = [];
|
|
2290
|
+
const origWatch = import_node_fs9.default.watch;
|
|
2291
|
+
import_node_fs9.default.watch = ((...args) => {
|
|
2292
|
+
const handle = origWatch.apply(import_node_fs9.default, args);
|
|
2293
|
+
capturedHandles.push(handle);
|
|
2294
|
+
return handle;
|
|
2264
2295
|
});
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2296
|
+
const w = import_chokidar.default.watch(folder, chokidarOptions);
|
|
2297
|
+
const unreffAll = () => {
|
|
2298
|
+
import_node_fs9.default.watch = origWatch;
|
|
2299
|
+
for (const h of capturedHandles) {
|
|
2300
|
+
h.unref();
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
return { w, capturedHandles, unreffAll };
|
|
2304
|
+
};
|
|
2305
|
+
const promises = [
|
|
2306
|
+
this.#walkAllFiles(this.trustedFolder, this.#thumbs.trusted, createUnreffedWatcher),
|
|
2307
|
+
this.#walkAllFiles(this.issuersCertFolder, this.#thumbs.issuers.certs, createUnreffedWatcher),
|
|
2308
|
+
this.#walkAllFiles(this.rejectedFolder, this.#thumbs.rejected, createUnreffedWatcher),
|
|
2309
|
+
this.#walkCRLFiles(this.crlFolder, this.#thumbs.crl, createUnreffedWatcher),
|
|
2310
|
+
this.#walkCRLFiles(this.issuersCrlFolder, this.#thumbs.issuersCrl, createUnreffedWatcher)
|
|
2311
|
+
];
|
|
2312
|
+
await Promise.all(promises);
|
|
2313
|
+
await this.#waitAndCheckCRLProcessingStatus();
|
|
2314
|
+
}
|
|
2315
|
+
async #walkCRLFiles(folder, index, createUnreffedWatcher) {
|
|
2316
|
+
await new Promise((resolve, _reject) => {
|
|
2317
|
+
const { w, unreffAll } = createUnreffedWatcher(folder);
|
|
2268
2318
|
w.on("unlink", (filename) => {
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2319
|
+
for (const [key, data] of index.entries()) {
|
|
2320
|
+
data.crls = data.crls.filter((c) => c.filename !== filename);
|
|
2321
|
+
if (data.crls.length === 0) {
|
|
2322
|
+
index.delete(key);
|
|
2323
|
+
}
|
|
2273
2324
|
}
|
|
2274
2325
|
});
|
|
2275
2326
|
w.on("add", (filename) => {
|
|
2276
|
-
|
|
2277
|
-
try {
|
|
2278
|
-
const certificate = (0, import_node_opcua_crypto5.readCertificate)(filename);
|
|
2279
|
-
const info = (0, import_node_opcua_crypto5.exploreCertificate)(certificate);
|
|
2280
|
-
const fingerprint2 = makeFingerprint(certificate);
|
|
2281
|
-
index.set(fingerprint2, { certificate, filename, info });
|
|
2282
|
-
this._filenameToHash.set(filename, fingerprint2);
|
|
2283
|
-
debugLog(
|
|
2284
|
-
import_chalk6.default.magenta("CERT"),
|
|
2285
|
-
info.tbsCertificate.subjectFingerPrint,
|
|
2286
|
-
info.tbsCertificate.serialNumber,
|
|
2287
|
-
info.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint
|
|
2288
|
-
);
|
|
2289
|
-
} catch (err) {
|
|
2290
|
-
debugLog(`Walk files in folder ${folder} with file ${filename}`);
|
|
2291
|
-
debugLog(err);
|
|
2292
|
-
}
|
|
2327
|
+
this.#onCrlFileAdded(index, filename);
|
|
2293
2328
|
});
|
|
2294
2329
|
w.on("change", (changedPath) => {
|
|
2295
|
-
debugLog(
|
|
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
|
-
}
|
|
2330
|
+
debugLog("change in folder ", folder, changedPath);
|
|
2308
2331
|
});
|
|
2309
|
-
this.
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
debugLog([...index.keys()].map((k) => k.substring(0, 10)));
|
|
2314
|
-
resolve();
|
|
2315
|
-
});
|
|
2332
|
+
this.#watchers.push(w);
|
|
2333
|
+
w.on("ready", () => {
|
|
2334
|
+
unreffAll();
|
|
2335
|
+
resolve();
|
|
2316
2336
|
});
|
|
2317
|
-
}
|
|
2318
|
-
const promises = [
|
|
2319
|
-
_walkAllFiles.bind(this, this.trustedFolder, this._thumbs.trusted)(),
|
|
2320
|
-
_walkAllFiles.bind(this, this.issuersCertFolder, this._thumbs.issuers.certs)(),
|
|
2321
|
-
_walkAllFiles.bind(this, this.rejectedFolder, this._thumbs.rejected)(),
|
|
2322
|
-
_walkCRLFiles.bind(this, this.crlFolder, this._thumbs.crl)(),
|
|
2323
|
-
_walkCRLFiles.bind(this, this.issuersCrlFolder, this._thumbs.issuersCrl)()
|
|
2324
|
-
];
|
|
2325
|
-
await Promise.all(promises);
|
|
2326
|
-
await this.waitAndCheckCRLProcessingStatus();
|
|
2327
|
-
}
|
|
2328
|
-
// make sure that all crls have been processed.
|
|
2329
|
-
async waitAndCheckCRLProcessingStatus() {
|
|
2330
|
-
return new Promise((resolve, reject) => {
|
|
2331
|
-
if (this._pending_crl_to_process === 0) {
|
|
2332
|
-
setImmediate(resolve);
|
|
2333
|
-
return;
|
|
2334
|
-
}
|
|
2335
|
-
if (this._on_crl_process) {
|
|
2336
|
-
return reject(new Error("Internal Error"));
|
|
2337
|
-
}
|
|
2338
|
-
this._on_crl_process = resolve;
|
|
2339
|
-
});
|
|
2340
|
-
}
|
|
2341
|
-
};
|
|
2342
|
-
|
|
2343
|
-
// packages/node-opcua-pki/lib/ca/crypto_create_CA.ts
|
|
2344
|
-
var import_command_line_args = __toESM(require("command-line-args"));
|
|
2345
|
-
var import_command_line_usage = __toESM(require("command-line-usage"));
|
|
2346
|
-
var epilog = "Copyright (c) sterfive - node-opcua - 2017-2026";
|
|
2347
|
-
function get_offset_date(date, nbDays) {
|
|
2348
|
-
const d = new Date(date.getTime());
|
|
2349
|
-
d.setDate(d.getDate() + nbDays);
|
|
2350
|
-
return d;
|
|
2351
|
-
}
|
|
2352
|
-
var today = /* @__PURE__ */ new Date();
|
|
2353
|
-
var yesterday = get_offset_date(today, -1);
|
|
2354
|
-
var two_years_ago = get_offset_date(today, -2 * 365);
|
|
2355
|
-
var next_year = get_offset_date(today, 365);
|
|
2356
|
-
var gLocalConfig = {};
|
|
2357
|
-
var g_certificateAuthority;
|
|
2358
|
-
async function construct_CertificateAuthority2(subject) {
|
|
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");
|
|
2361
|
-
if (!g_certificateAuthority) {
|
|
2362
|
-
g_certificateAuthority = new CertificateAuthority({
|
|
2363
|
-
keySize: gLocalConfig.keySize,
|
|
2364
|
-
location: gLocalConfig.CAFolder,
|
|
2365
|
-
subject
|
|
2366
2337
|
});
|
|
2367
|
-
await g_certificateAuthority.initialize();
|
|
2368
2338
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
location: gLocalConfig.PKIFolder
|
|
2377
|
-
});
|
|
2378
|
-
await certificateManager.initialize();
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
function default_template_content() {
|
|
2382
|
-
if (process.pkg?.entrypoint) {
|
|
2383
|
-
const a = import_node_fs10.default.readFileSync(import_node_path7.default.join(__dirname, "../../bin/pki_config.example.js"), "utf8");
|
|
2384
|
-
return a;
|
|
2385
|
-
}
|
|
2386
|
-
function find_default_config_template() {
|
|
2387
|
-
const rootFolder = find_module_root_folder();
|
|
2388
|
-
const configName = "pki_config.example.js";
|
|
2389
|
-
let default_config_template2 = import_node_path7.default.join(rootFolder, "bin", configName);
|
|
2390
|
-
if (!import_node_fs10.default.existsSync(default_config_template2)) {
|
|
2391
|
-
default_config_template2 = import_node_path7.default.join(__dirname, "..", configName);
|
|
2392
|
-
if (!import_node_fs10.default.existsSync(default_config_template2)) {
|
|
2393
|
-
default_config_template2 = import_node_path7.default.join(__dirname, `../bin/${configName}`);
|
|
2339
|
+
async #walkAllFiles(folder, index, createUnreffedWatcher) {
|
|
2340
|
+
const { w, unreffAll } = createUnreffedWatcher(folder);
|
|
2341
|
+
w.on("unlink", (filename) => {
|
|
2342
|
+
debugLog(import_chalk6.default.cyan(`unlink in folder ${folder}`), filename);
|
|
2343
|
+
const h = this.#filenameToHash.get(filename);
|
|
2344
|
+
if (h && index.has(h)) {
|
|
2345
|
+
index.delete(h);
|
|
2394
2346
|
}
|
|
2395
|
-
}
|
|
2396
|
-
return default_config_template2;
|
|
2397
|
-
}
|
|
2398
|
-
const default_config_template = find_default_config_template();
|
|
2399
|
-
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(default_config_template));
|
|
2400
|
-
const default_config_template_content = import_node_fs10.default.readFileSync(default_config_template, "utf8");
|
|
2401
|
-
return default_config_template_content;
|
|
2402
|
-
}
|
|
2403
|
-
function find_module_root_folder() {
|
|
2404
|
-
let rootFolder = import_node_path7.default.join(__dirname);
|
|
2405
|
-
for (let i = 0; i < 4; i++) {
|
|
2406
|
-
if (import_node_fs10.default.existsSync(import_node_path7.default.join(rootFolder, "package.json"))) {
|
|
2407
|
-
return rootFolder;
|
|
2408
|
-
}
|
|
2409
|
-
rootFolder = import_node_path7.default.join(rootFolder, "..");
|
|
2410
|
-
}
|
|
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");
|
|
2412
|
-
return rootFolder;
|
|
2413
|
-
}
|
|
2414
|
-
async function readConfiguration(argv) {
|
|
2415
|
-
if (argv.silent) {
|
|
2416
|
-
g_config.silent = true;
|
|
2417
|
-
} else {
|
|
2418
|
-
g_config.silent = false;
|
|
2419
|
-
}
|
|
2420
|
-
const fqdn2 = await extractFullyQualifiedDomainName();
|
|
2421
|
-
const hostname = import_node_os4.default.hostname();
|
|
2422
|
-
let certificateDir;
|
|
2423
|
-
function performSubstitution(str) {
|
|
2424
|
-
str = str.replace("{CWD}", process.cwd());
|
|
2425
|
-
if (certificateDir) {
|
|
2426
|
-
str = str.replace("{root}", certificateDir);
|
|
2427
|
-
}
|
|
2428
|
-
if (gLocalConfig?.PKIFolder) {
|
|
2429
|
-
str = str.replace("{PKIFolder}", gLocalConfig.PKIFolder);
|
|
2430
|
-
}
|
|
2431
|
-
str = str.replace("{hostname}", hostname);
|
|
2432
|
-
str = str.replace("%FQDN%", fqdn2);
|
|
2433
|
-
return str;
|
|
2434
|
-
}
|
|
2435
|
-
function prepare(file) {
|
|
2436
|
-
const tmp = import_node_path7.default.resolve(performSubstitution(file));
|
|
2437
|
-
return makePath(tmp);
|
|
2438
|
-
}
|
|
2439
|
-
certificateDir = argv.root;
|
|
2440
|
-
(0, import_node_assert10.default)(typeof certificateDir === "string");
|
|
2441
|
-
certificateDir = prepare(certificateDir);
|
|
2442
|
-
mkdirRecursiveSync(certificateDir);
|
|
2443
|
-
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(certificateDir));
|
|
2444
|
-
const default_config = import_node_path7.default.join(certificateDir, "config.js");
|
|
2445
|
-
if (!import_node_fs10.default.existsSync(default_config)) {
|
|
2446
|
-
debugLog(import_chalk7.default.yellow(" Creating default g_config file "), import_chalk7.default.cyan(default_config));
|
|
2447
|
-
const default_config_template_content = default_template_content();
|
|
2448
|
-
import_node_fs10.default.writeFileSync(default_config, default_config_template_content);
|
|
2449
|
-
} else {
|
|
2450
|
-
debugLog(import_chalk7.default.yellow(" using g_config file "), import_chalk7.default.cyan(default_config));
|
|
2451
|
-
}
|
|
2452
|
-
if (!import_node_fs10.default.existsSync(default_config)) {
|
|
2453
|
-
debugLog(import_chalk7.default.redBright(" cannot find config file ", default_config));
|
|
2454
|
-
}
|
|
2455
|
-
const defaultRandomFile = import_node_path7.default.join(import_node_path7.default.dirname(default_config), "random.rnd");
|
|
2456
|
-
setEnv("RANDFILE", defaultRandomFile);
|
|
2457
|
-
const _require = (0, import_node_module.createRequire)(__filename);
|
|
2458
|
-
gLocalConfig = _require(default_config);
|
|
2459
|
-
gLocalConfig.subject = new import_node_opcua_crypto6.Subject(gLocalConfig.subject || "");
|
|
2460
|
-
if (argv.subject) {
|
|
2461
|
-
gLocalConfig.subject = new import_node_opcua_crypto6.Subject(argv.subject);
|
|
2462
|
-
}
|
|
2463
|
-
if (!gLocalConfig.subject.commonName) {
|
|
2464
|
-
throw new Error("subject must have a Common Name");
|
|
2465
|
-
}
|
|
2466
|
-
gLocalConfig.certificateDir = certificateDir;
|
|
2467
|
-
let CAFolder = argv.CAFolder || import_node_path7.default.join(certificateDir, "CA");
|
|
2468
|
-
CAFolder = prepare(CAFolder);
|
|
2469
|
-
gLocalConfig.CAFolder = CAFolder;
|
|
2470
|
-
gLocalConfig.PKIFolder = import_node_path7.default.join(gLocalConfig.certificateDir, "PKI");
|
|
2471
|
-
if (argv.PKIFolder) {
|
|
2472
|
-
gLocalConfig.PKIFolder = prepare(argv.PKIFolder);
|
|
2473
|
-
}
|
|
2474
|
-
gLocalConfig.PKIFolder = prepare(gLocalConfig.PKIFolder);
|
|
2475
|
-
if (argv.privateKey) {
|
|
2476
|
-
gLocalConfig.privateKey = prepare(argv.privateKey);
|
|
2477
|
-
}
|
|
2478
|
-
if (argv.applicationUri) {
|
|
2479
|
-
gLocalConfig.applicationUri = performSubstitution(argv.applicationUri);
|
|
2480
|
-
}
|
|
2481
|
-
if (argv.output) {
|
|
2482
|
-
gLocalConfig.outputFile = argv.output;
|
|
2483
|
-
}
|
|
2484
|
-
gLocalConfig.altNames = [];
|
|
2485
|
-
if (argv.altNames) {
|
|
2486
|
-
gLocalConfig.altNames = argv.altNames.split(";");
|
|
2487
|
-
}
|
|
2488
|
-
gLocalConfig.dns = [getFullyQualifiedDomainName()];
|
|
2489
|
-
if (argv.dns) {
|
|
2490
|
-
gLocalConfig.dns = argv.dns.split(",").map(performSubstitution);
|
|
2491
|
-
}
|
|
2492
|
-
gLocalConfig.ip = [];
|
|
2493
|
-
if (argv.ip) {
|
|
2494
|
-
gLocalConfig.ip = argv.ip.split(",");
|
|
2495
|
-
}
|
|
2496
|
-
if (argv.keySize) {
|
|
2497
|
-
const v = argv.keySize;
|
|
2498
|
-
if (v !== 1024 && v !== 2048 && v !== 3072 && v !== 4096) {
|
|
2499
|
-
throw new Error(`invalid keysize specified ${v} should be 1024,2048,3072 or 4096`);
|
|
2500
|
-
}
|
|
2501
|
-
gLocalConfig.keySize = argv.keySize;
|
|
2502
|
-
}
|
|
2503
|
-
if (argv.validity) {
|
|
2504
|
-
gLocalConfig.validity = argv.validity;
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
async function createDefaultCertificate(base_name, prefix, key_length, applicationUri, dev) {
|
|
2508
|
-
(0, import_node_assert10.default)(key_length === 1024 || key_length === 2048 || key_length === 3072 || key_length === 4096);
|
|
2509
|
-
const private_key_file = makePath(base_name, `${prefix}key_${key_length}.pem`);
|
|
2510
|
-
const public_key_file = makePath(base_name, `${prefix}public_key_${key_length}.pub`);
|
|
2511
|
-
const certificate_file = makePath(base_name, `${prefix}cert_${key_length}.pem`);
|
|
2512
|
-
const certificate_file_outofdate = makePath(base_name, `${prefix}cert_${key_length}_outofdate.pem`);
|
|
2513
|
-
const certificate_file_not_active_yet = makePath(base_name, `${prefix}cert_${key_length}_not_active_yet.pem`);
|
|
2514
|
-
const certificate_revoked = makePath(base_name, `${prefix}cert_${key_length}_revoked.pem`);
|
|
2515
|
-
const self_signed_certificate_file = makePath(base_name, `${prefix}selfsigned_cert_${key_length}.pem`);
|
|
2516
|
-
const fqdn2 = getFullyQualifiedDomainName();
|
|
2517
|
-
const hostname = import_node_os4.default.hostname();
|
|
2518
|
-
const dns2 = [
|
|
2519
|
-
// for conformance reason, localhost shall not be present in the DNS field of COP
|
|
2520
|
-
// ***FORBIDEN** "localhost",
|
|
2521
|
-
getFullyQualifiedDomainName()
|
|
2522
|
-
];
|
|
2523
|
-
if (hostname !== fqdn2) {
|
|
2524
|
-
dns2.push(hostname);
|
|
2525
|
-
}
|
|
2526
|
-
const ip = [];
|
|
2527
|
-
async function createCertificateIfNotExist(certificate, private_key, applicationUri2, startDate, validity) {
|
|
2528
|
-
if (import_node_fs10.default.existsSync(certificate)) {
|
|
2529
|
-
warningLog(import_chalk7.default.yellow(" certificate"), import_chalk7.default.cyan(certificate), import_chalk7.default.yellow(" already exists => skipping"));
|
|
2530
|
-
return "";
|
|
2531
|
-
} else {
|
|
2532
|
-
return await createCertificate(certificate, private_key, applicationUri2, startDate, validity);
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
async function createCertificate(certificate, privateKey, applicationUri2, startDate, validity) {
|
|
2536
|
-
const certificateSigningRequestFile = `${certificate}.csr`;
|
|
2537
|
-
const configFile = makePath(base_name, "../certificates/PKI/own/openssl.cnf");
|
|
2538
|
-
const dns3 = [import_node_os4.default.hostname()];
|
|
2539
|
-
const ip2 = ["127.0.0.1"];
|
|
2540
|
-
const params = {
|
|
2541
|
-
applicationUri: applicationUri2,
|
|
2542
|
-
privateKey,
|
|
2543
|
-
rootDir: ".",
|
|
2544
|
-
configFile,
|
|
2545
|
-
dns: dns3,
|
|
2546
|
-
ip: ip2,
|
|
2547
|
-
purpose: import_node_opcua_crypto6.CertificatePurpose.ForApplication
|
|
2548
|
-
};
|
|
2549
|
-
await createCertificateSigningRequestWithOpenSSL(certificateSigningRequestFile, params);
|
|
2550
|
-
return await g_certificateAuthority.signCertificateRequest(certificate, certificateSigningRequestFile, {
|
|
2551
|
-
applicationUri: applicationUri2,
|
|
2552
|
-
dns: dns3,
|
|
2553
|
-
ip: ip2,
|
|
2554
|
-
startDate,
|
|
2555
|
-
validity
|
|
2556
2347
|
});
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2348
|
+
w.on("add", (filename) => {
|
|
2349
|
+
debugLog(import_chalk6.default.cyan(`add in folder ${folder}`), filename);
|
|
2350
|
+
try {
|
|
2351
|
+
const certificate = (0, import_node_opcua_crypto5.readCertificate)(filename);
|
|
2352
|
+
const info = (0, import_node_opcua_crypto5.exploreCertificate)(certificate);
|
|
2353
|
+
const fingerprint = makeFingerprint(certificate);
|
|
2354
|
+
index.set(fingerprint, { certificate, filename, info });
|
|
2355
|
+
this.#filenameToHash.set(filename, fingerprint);
|
|
2356
|
+
debugLog(
|
|
2357
|
+
import_chalk6.default.magenta("CERT"),
|
|
2358
|
+
info.tbsCertificate.subjectFingerPrint,
|
|
2359
|
+
info.tbsCertificate.serialNumber,
|
|
2360
|
+
info.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint
|
|
2361
|
+
);
|
|
2362
|
+
} catch (err) {
|
|
2363
|
+
debugLog(`Walk files in folder ${folder} with file ${filename}`);
|
|
2364
|
+
debugLog(err);
|
|
2365
|
+
}
|
|
2565
2366
|
});
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
} else {
|
|
2575
|
-
await (0, import_node_opcua_crypto6.generatePrivateKeyFile)(privateKey, keyLength);
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
displaySubtitle(` create private key :${private_key_file}`);
|
|
2579
|
-
await createPrivateKeyIfNotExist(private_key_file, key_length);
|
|
2580
|
-
displaySubtitle(` extract public key ${public_key_file} from private key `);
|
|
2581
|
-
await getPublicKeyFromPrivateKey(private_key_file, public_key_file);
|
|
2582
|
-
displaySubtitle(` create Certificate ${certificate_file}`);
|
|
2583
|
-
await createCertificateIfNotExist(certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
2584
|
-
displaySubtitle(` create self signed Certificate ${self_signed_certificate_file}`);
|
|
2585
|
-
if (import_node_fs10.default.existsSync(self_signed_certificate_file)) {
|
|
2586
|
-
return;
|
|
2587
|
-
}
|
|
2588
|
-
await createSelfSignedCertificate2(self_signed_certificate_file, private_key_file, applicationUri, yesterday, 365);
|
|
2589
|
-
if (dev) {
|
|
2590
|
-
await createCertificateIfNotExist(certificate_file_outofdate, private_key_file, applicationUri, two_years_ago, 365);
|
|
2591
|
-
await createCertificateIfNotExist(certificate_file_not_active_yet, private_key_file, applicationUri, next_year, 365);
|
|
2592
|
-
if (!import_node_fs10.default.existsSync(certificate_revoked)) {
|
|
2593
|
-
const certificate = await createCertificateIfNotExist(
|
|
2594
|
-
certificate_revoked,
|
|
2595
|
-
private_key_file,
|
|
2596
|
-
`${applicationUri}Revoked`,
|
|
2597
|
-
// make sure we used a uniq URI here
|
|
2598
|
-
yesterday,
|
|
2599
|
-
365
|
|
2600
|
-
);
|
|
2601
|
-
warningLog(" certificate to revoke => ", certificate);
|
|
2602
|
-
revoke_certificate(certificate_revoked);
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
}
|
|
2606
|
-
async function wrap(func) {
|
|
2607
|
-
try {
|
|
2608
|
-
await func();
|
|
2609
|
-
} catch (err) {
|
|
2610
|
-
console.log(err.message);
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
async function create_default_certificates(dev) {
|
|
2614
|
-
(0, import_node_assert10.default)(gLocalConfig);
|
|
2615
|
-
const base_name = gLocalConfig.certificateDir || "";
|
|
2616
|
-
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(base_name));
|
|
2617
|
-
let clientURN;
|
|
2618
|
-
let serverURN;
|
|
2619
|
-
let discoveryServerURN;
|
|
2620
|
-
wrap(async () => {
|
|
2621
|
-
await extractFullyQualifiedDomainName();
|
|
2622
|
-
const hostname = import_node_os4.default.hostname();
|
|
2623
|
-
const fqdn2 = getFullyQualifiedDomainName();
|
|
2624
|
-
warningLog(import_chalk7.default.yellow(" hostname = "), import_chalk7.default.cyan(hostname));
|
|
2625
|
-
warningLog(import_chalk7.default.yellow(" fqdn = "), import_chalk7.default.cyan(fqdn2));
|
|
2626
|
-
clientURN = makeApplicationUrn(hostname, "NodeOPCUA-Client");
|
|
2627
|
-
serverURN = makeApplicationUrn(hostname, "NodeOPCUA-Server");
|
|
2628
|
-
discoveryServerURN = makeApplicationUrn(hostname, "NodeOPCUA-DiscoveryServer");
|
|
2629
|
-
displayTitle("Create Application Certificate for Server & its private key");
|
|
2630
|
-
await createDefaultCertificate(base_name, "client_", 1024, clientURN, dev);
|
|
2631
|
-
await createDefaultCertificate(base_name, "client_", 2048, clientURN, dev);
|
|
2632
|
-
await createDefaultCertificate(base_name, "client_", 3072, clientURN, dev);
|
|
2633
|
-
await createDefaultCertificate(base_name, "client_", 4096, clientURN, dev);
|
|
2634
|
-
displayTitle("Create Application Certificate for Client & its private key");
|
|
2635
|
-
await createDefaultCertificate(base_name, "server_", 1024, serverURN, dev);
|
|
2636
|
-
await createDefaultCertificate(base_name, "server_", 2048, serverURN, dev);
|
|
2637
|
-
await createDefaultCertificate(base_name, "server_", 3072, serverURN, dev);
|
|
2638
|
-
await createDefaultCertificate(base_name, "server_", 4096, serverURN, dev);
|
|
2639
|
-
displayTitle("Create Application Certificate for DiscoveryServer & its private key");
|
|
2640
|
-
await createDefaultCertificate(base_name, "discoveryServer_", 1024, discoveryServerURN, dev);
|
|
2641
|
-
await createDefaultCertificate(base_name, "discoveryServer_", 2048, discoveryServerURN, dev);
|
|
2642
|
-
await createDefaultCertificate(base_name, "discoveryServer_", 3072, discoveryServerURN, dev);
|
|
2643
|
-
await createDefaultCertificate(base_name, "discoveryServer_", 4096, discoveryServerURN, dev);
|
|
2644
|
-
});
|
|
2645
|
-
}
|
|
2646
|
-
async function createDefaultCertificates(dev) {
|
|
2647
|
-
await construct_CertificateAuthority2("");
|
|
2648
|
-
await construct_CertificateManager();
|
|
2649
|
-
await create_default_certificates(dev);
|
|
2650
|
-
}
|
|
2651
|
-
var commonOptions = [
|
|
2652
|
-
{
|
|
2653
|
-
name: "root",
|
|
2654
|
-
alias: "r",
|
|
2655
|
-
type: String,
|
|
2656
|
-
defaultValue: "{CWD}/certificates",
|
|
2657
|
-
description: "the location of the Certificate folder"
|
|
2658
|
-
},
|
|
2659
|
-
{
|
|
2660
|
-
name: "CAFolder",
|
|
2661
|
-
alias: "c",
|
|
2662
|
-
type: String,
|
|
2663
|
-
defaultValue: "{root}/CA",
|
|
2664
|
-
description: "the location of the Certificate Authority folder"
|
|
2665
|
-
},
|
|
2666
|
-
{ name: "PKIFolder", type: String, defaultValue: "{root}/PKI", description: "the location of the Public Key Infrastructure" },
|
|
2667
|
-
{ name: "silent", type: Boolean, defaultValue: false, description: "minimize output" },
|
|
2668
|
-
{
|
|
2669
|
-
name: "privateKey",
|
|
2670
|
-
alias: "p",
|
|
2671
|
-
type: String,
|
|
2672
|
-
defaultValue: "{PKIFolder}/own/private_key.pem",
|
|
2673
|
-
description: "the private key to use to generate certificate"
|
|
2674
|
-
},
|
|
2675
|
-
{
|
|
2676
|
-
name: "keySize",
|
|
2677
|
-
alias: "k",
|
|
2678
|
-
type: Number,
|
|
2679
|
-
defaultValue: 2048,
|
|
2680
|
-
description: "the private key size in bits (1024|2048|3072|4096)"
|
|
2681
|
-
},
|
|
2682
|
-
{ name: "help", alias: "h", type: Boolean, description: "display this help" }
|
|
2683
|
-
];
|
|
2684
|
-
function getOptions(names) {
|
|
2685
|
-
return commonOptions.filter((o) => names.includes(o.name) || o.name === "help" || o.name === "silent");
|
|
2686
|
-
}
|
|
2687
|
-
function showHelp(command, description, options, usage) {
|
|
2688
|
-
const sections = [
|
|
2689
|
-
{
|
|
2690
|
-
header: `Command: ${command}`,
|
|
2691
|
-
content: description
|
|
2692
|
-
},
|
|
2693
|
-
{
|
|
2694
|
-
header: "Usage",
|
|
2695
|
-
content: usage || `$0 ${command} [options]`
|
|
2696
|
-
},
|
|
2697
|
-
{
|
|
2698
|
-
header: "Options",
|
|
2699
|
-
optionList: options
|
|
2700
|
-
}
|
|
2701
|
-
];
|
|
2702
|
-
console.log((0, import_command_line_usage.default)(sections));
|
|
2703
|
-
}
|
|
2704
|
-
async function main(argumentsList) {
|
|
2705
|
-
const mainDefinitions = [{ name: "command", defaultOption: true }];
|
|
2706
|
-
let mainOptions;
|
|
2707
|
-
try {
|
|
2708
|
-
mainOptions = (0, import_command_line_args.default)(mainDefinitions, { argv: argumentsList, stopAtFirstUnknown: true });
|
|
2709
|
-
} catch (err) {
|
|
2710
|
-
console.log(err.message);
|
|
2711
|
-
return;
|
|
2712
|
-
}
|
|
2713
|
-
const argv = mainOptions._unknown || [];
|
|
2714
|
-
const command = mainOptions.command;
|
|
2715
|
-
if (!command || command === "help") {
|
|
2716
|
-
console.log(
|
|
2717
|
-
(0, import_command_line_usage.default)([
|
|
2718
|
-
{
|
|
2719
|
-
header: "node-opcua-pki",
|
|
2720
|
-
content: `PKI management for node-opcua
|
|
2721
|
-
|
|
2722
|
-
${epilog}`
|
|
2723
|
-
},
|
|
2724
|
-
{
|
|
2725
|
-
header: "Commands",
|
|
2726
|
-
content: [
|
|
2727
|
-
{ name: "demo", summary: "create default certificate for node-opcua demos" },
|
|
2728
|
-
{ name: "createCA", summary: "create a Certificate Authority" },
|
|
2729
|
-
{ name: "createPKI", summary: "create a Public Key Infrastructure" },
|
|
2730
|
-
{ name: "certificate", summary: "create a new certificate" },
|
|
2731
|
-
{ name: "revoke <certificateFile>", summary: "revoke a existing certificate" },
|
|
2732
|
-
{ name: "csr", summary: "create a certificate signing request" },
|
|
2733
|
-
{ name: "sign", summary: "validate a certificate signing request and generate a certificate" },
|
|
2734
|
-
{ name: "dump <certificateFile>", summary: "display a certificate" },
|
|
2735
|
-
{ name: "toder <pemCertificate>", summary: "convert a certificate to a DER format with finger print" },
|
|
2736
|
-
{ name: "fingerprint <certificateFile>", summary: "print the certificate fingerprint" },
|
|
2737
|
-
{ name: "version", summary: "display the version number" }
|
|
2738
|
-
]
|
|
2739
|
-
}
|
|
2740
|
-
])
|
|
2741
|
-
);
|
|
2742
|
-
return;
|
|
2743
|
-
}
|
|
2744
|
-
if (command === "version") {
|
|
2745
|
-
const rootFolder = find_module_root_folder();
|
|
2746
|
-
const pkg = JSON.parse(import_node_fs10.default.readFileSync(import_node_path7.default.join(rootFolder, "package.json"), "utf-8"));
|
|
2747
|
-
console.log(pkg.version);
|
|
2748
|
-
return;
|
|
2749
|
-
}
|
|
2750
|
-
if (command === "demo") {
|
|
2751
|
-
const optionsDef = [
|
|
2752
|
-
...getOptions(["root", "silent"]),
|
|
2753
|
-
{ name: "dev", type: Boolean, description: "create all sort of fancy certificates for dev testing purposes" },
|
|
2754
|
-
{ name: "clean", type: Boolean, description: "Purge existing directory [use with care!]" }
|
|
2755
|
-
];
|
|
2756
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
2757
|
-
if (local_argv.help)
|
|
2758
|
-
return showHelp(
|
|
2759
|
-
"demo",
|
|
2760
|
-
"create default certificate for node-opcua demos",
|
|
2761
|
-
optionsDef,
|
|
2762
|
-
"$0 demo [--dev] [--silent] [--clean]"
|
|
2763
|
-
);
|
|
2764
|
-
await wrap(async () => {
|
|
2765
|
-
await ensure_openssl_installed();
|
|
2766
|
-
displayChapter("Create Demo certificates");
|
|
2767
|
-
displayTitle("reading configuration");
|
|
2768
|
-
await readConfiguration(local_argv);
|
|
2769
|
-
if (local_argv.clean) {
|
|
2770
|
-
displayTitle("Cleaning old certificates");
|
|
2771
|
-
(0, import_node_assert10.default)(gLocalConfig);
|
|
2772
|
-
const certificateDir = gLocalConfig.certificateDir || "";
|
|
2773
|
-
const files = await import_node_fs10.default.promises.readdir(certificateDir);
|
|
2774
|
-
for (const file of files) {
|
|
2775
|
-
if (file.includes(".pem") || file.includes(".pub")) {
|
|
2776
|
-
await import_node_fs10.default.promises.unlink(import_node_path7.default.join(certificateDir, file));
|
|
2777
|
-
}
|
|
2367
|
+
w.on("change", (changedPath) => {
|
|
2368
|
+
debugLog(import_chalk6.default.cyan(`change in folder ${folder}`), changedPath);
|
|
2369
|
+
try {
|
|
2370
|
+
const certificate = (0, import_node_opcua_crypto5.readCertificate)(changedPath);
|
|
2371
|
+
const newFingerprint = makeFingerprint(certificate);
|
|
2372
|
+
const oldHash = this.#filenameToHash.get(changedPath);
|
|
2373
|
+
if (oldHash && oldHash !== newFingerprint) {
|
|
2374
|
+
index.delete(oldHash);
|
|
2778
2375
|
}
|
|
2779
|
-
|
|
2376
|
+
index.set(newFingerprint, { certificate, filename: changedPath, info: (0, import_node_opcua_crypto5.exploreCertificate)(certificate) });
|
|
2377
|
+
this.#filenameToHash.set(changedPath, newFingerprint);
|
|
2378
|
+
} catch (err) {
|
|
2379
|
+
debugLog(`change event: failed to re-read ${changedPath}`, err);
|
|
2780
2380
|
}
|
|
2781
|
-
displayTitle("create certificates");
|
|
2782
|
-
await createDefaultCertificates(local_argv.dev);
|
|
2783
|
-
displayChapter("Demo certificates CREATED");
|
|
2784
2381
|
});
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
if (local_argv.help) return showHelp("createCA", "create a Certificate Authority", optionsDef);
|
|
2794
|
-
await wrap(async () => {
|
|
2795
|
-
await ensure_openssl_installed();
|
|
2796
|
-
await readConfiguration(local_argv);
|
|
2797
|
-
await construct_CertificateAuthority2(local_argv.subject);
|
|
2798
|
-
});
|
|
2799
|
-
return;
|
|
2800
|
-
}
|
|
2801
|
-
if (command === "createPKI") {
|
|
2802
|
-
const optionsDef = getOptions(["root", "PKIFolder", "keySize", "silent"]);
|
|
2803
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
2804
|
-
if (local_argv.help) return showHelp("createPKI", "create a Public Key Infrastructure", optionsDef);
|
|
2805
|
-
await wrap(async () => {
|
|
2806
|
-
await readConfiguration(local_argv);
|
|
2807
|
-
await construct_CertificateManager();
|
|
2808
|
-
});
|
|
2809
|
-
return;
|
|
2810
|
-
}
|
|
2811
|
-
if (command === "certificate") {
|
|
2812
|
-
const optionsDef = [
|
|
2813
|
-
...getOptions(["root", "CAFolder", "PKIFolder", "privateKey", "silent"]),
|
|
2814
|
-
{
|
|
2815
|
-
name: "applicationUri",
|
|
2816
|
-
alias: "a",
|
|
2817
|
-
type: String,
|
|
2818
|
-
defaultValue: "urn:{hostname}:Node-OPCUA-Server",
|
|
2819
|
-
description: "the application URI"
|
|
2820
|
-
},
|
|
2821
|
-
{
|
|
2822
|
-
name: "output",
|
|
2823
|
-
alias: "o",
|
|
2824
|
-
type: String,
|
|
2825
|
-
defaultValue: "my_certificate.pem",
|
|
2826
|
-
description: "the name of the generated certificate =>"
|
|
2827
|
-
},
|
|
2828
|
-
{
|
|
2829
|
-
name: "selfSigned",
|
|
2830
|
-
alias: "s",
|
|
2831
|
-
type: Boolean,
|
|
2832
|
-
defaultValue: false,
|
|
2833
|
-
description: "if true, certificate will be self-signed"
|
|
2834
|
-
},
|
|
2835
|
-
{ name: "validity", alias: "v", type: Number, description: "the certificate validity in days" },
|
|
2836
|
-
{
|
|
2837
|
-
name: "dns",
|
|
2838
|
-
type: String,
|
|
2839
|
-
defaultValue: "{hostname}",
|
|
2840
|
-
description: "the list of valid domain name (comma separated)"
|
|
2841
|
-
},
|
|
2842
|
-
{ name: "ip", type: String, defaultValue: "", description: "the list of valid IPs (comma separated)" },
|
|
2843
|
-
{
|
|
2844
|
-
name: "subject",
|
|
2845
|
-
type: String,
|
|
2846
|
-
defaultValue: "",
|
|
2847
|
-
description: "the certificate subject ( for instance C=FR/ST=Centre/L=Orleans/O=SomeOrganization/CN=Hello )"
|
|
2848
|
-
}
|
|
2849
|
-
];
|
|
2850
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
2851
|
-
if (local_argv.help || !local_argv.applicationUri || !local_argv.output)
|
|
2852
|
-
return showHelp("certificate", "create a new certificate", optionsDef);
|
|
2853
|
-
async function command_certificate(local_argv2) {
|
|
2854
|
-
const selfSigned = !!local_argv2.selfSigned;
|
|
2855
|
-
if (!selfSigned) {
|
|
2856
|
-
await command_full_certificate(local_argv2);
|
|
2857
|
-
} else {
|
|
2858
|
-
await command_selfsigned_certificate(local_argv2);
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
async function command_selfsigned_certificate(local_argv2) {
|
|
2862
|
-
const _fqdn = await extractFullyQualifiedDomainName();
|
|
2863
|
-
await readConfiguration(local_argv2);
|
|
2864
|
-
await construct_CertificateManager();
|
|
2865
|
-
displaySubtitle(` create self signed Certificate ${gLocalConfig.outputFile}`);
|
|
2866
|
-
let subject = local_argv2.subject && local_argv2.subject.length > 1 ? new import_node_opcua_crypto6.Subject(local_argv2.subject) : gLocalConfig.subject || "";
|
|
2867
|
-
subject = JSON.parse(JSON.stringify(subject));
|
|
2868
|
-
const params = {
|
|
2869
|
-
applicationUri: gLocalConfig.applicationUri || "",
|
|
2870
|
-
dns: gLocalConfig.dns || [],
|
|
2871
|
-
ip: gLocalConfig.ip || [],
|
|
2872
|
-
outputFile: gLocalConfig.outputFile || "self_signed_certificate.pem",
|
|
2873
|
-
startDate: gLocalConfig.startDate || /* @__PURE__ */ new Date(),
|
|
2874
|
-
subject,
|
|
2875
|
-
validity: gLocalConfig.validity || 365
|
|
2876
|
-
};
|
|
2877
|
-
await certificateManager.createSelfSignedCertificate(params);
|
|
2878
|
-
}
|
|
2879
|
-
async function command_full_certificate(local_argv2) {
|
|
2880
|
-
await readConfiguration(local_argv2);
|
|
2881
|
-
await construct_CertificateManager();
|
|
2882
|
-
await construct_CertificateAuthority2("");
|
|
2883
|
-
(0, import_node_assert10.default)(import_node_fs10.default.existsSync(gLocalConfig.CAFolder || ""), " CA folder must exist");
|
|
2884
|
-
gLocalConfig.privateKey = void 0;
|
|
2885
|
-
gLocalConfig.subject = local_argv2.subject && local_argv2.subject.length > 1 ? local_argv2.subject : gLocalConfig.subject;
|
|
2886
|
-
const csr_file = await certificateManager.createCertificateRequest(
|
|
2887
|
-
gLocalConfig
|
|
2888
|
-
);
|
|
2889
|
-
if (!csr_file) {
|
|
2890
|
-
return;
|
|
2891
|
-
}
|
|
2892
|
-
warningLog(" csr_file = ", csr_file);
|
|
2893
|
-
const certificate = csr_file.replace(".csr", ".pem");
|
|
2894
|
-
if (import_node_fs10.default.existsSync(certificate)) {
|
|
2895
|
-
throw new Error(` File ${certificate} already exist`);
|
|
2896
|
-
}
|
|
2897
|
-
await g_certificateAuthority.signCertificateRequest(
|
|
2898
|
-
certificate,
|
|
2899
|
-
csr_file,
|
|
2900
|
-
gLocalConfig
|
|
2901
|
-
);
|
|
2902
|
-
(0, import_node_assert10.default)(typeof gLocalConfig.outputFile === "string");
|
|
2903
|
-
import_node_fs10.default.writeFileSync(gLocalConfig.outputFile || "", import_node_fs10.default.readFileSync(certificate, "ascii"));
|
|
2904
|
-
}
|
|
2905
|
-
await wrap(async () => await command_certificate(local_argv));
|
|
2906
|
-
return;
|
|
2907
|
-
}
|
|
2908
|
-
if (command === "revoke") {
|
|
2909
|
-
const optionsDef = [{ name: "certificateFile", type: String, defaultOption: true }, ...getOptions(["root", "CAFolder"])];
|
|
2910
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
2911
|
-
if (local_argv.help || !local_argv.certificateFile)
|
|
2912
|
-
return showHelp(
|
|
2913
|
-
"revoke <certificateFile>",
|
|
2914
|
-
"revoke a existing certificate",
|
|
2915
|
-
optionsDef,
|
|
2916
|
-
"$0 revoke my_certificate.pem"
|
|
2917
|
-
);
|
|
2918
|
-
async function revoke_certificate(certificate) {
|
|
2919
|
-
await g_certificateAuthority.revokeCertificate(certificate, {});
|
|
2920
|
-
}
|
|
2921
|
-
await wrap(async () => {
|
|
2922
|
-
const certificate = import_node_path7.default.resolve(local_argv.certificateFile);
|
|
2923
|
-
warningLog(import_chalk7.default.yellow(" Certificate to revoke : "), import_chalk7.default.cyan(certificate));
|
|
2924
|
-
if (!import_node_fs10.default.existsSync(certificate)) {
|
|
2925
|
-
throw new Error(`cannot find certificate to revoke ${certificate}`);
|
|
2926
|
-
}
|
|
2927
|
-
await readConfiguration(local_argv);
|
|
2928
|
-
await construct_CertificateAuthority2("");
|
|
2929
|
-
await revoke_certificate(certificate);
|
|
2930
|
-
warningLog("done ... ");
|
|
2931
|
-
warningLog(" crl = ", g_certificateAuthority.revocationList);
|
|
2932
|
-
warningLog("\nyou should now publish the new Certificate Revocation List");
|
|
2382
|
+
this.#watchers.push(w);
|
|
2383
|
+
await new Promise((resolve, _reject) => {
|
|
2384
|
+
w.on("ready", () => {
|
|
2385
|
+
unreffAll();
|
|
2386
|
+
debugLog("ready");
|
|
2387
|
+
debugLog([...index.keys()].map((k) => k.substring(0, 10)));
|
|
2388
|
+
resolve();
|
|
2389
|
+
});
|
|
2933
2390
|
});
|
|
2934
|
-
return;
|
|
2935
2391
|
}
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
{
|
|
2940
|
-
|
|
2941
|
-
alias: "a",
|
|
2942
|
-
type: String,
|
|
2943
|
-
defaultValue: "urn:{hostname}:Node-OPCUA-Server",
|
|
2944
|
-
description: "the application URI"
|
|
2945
|
-
},
|
|
2946
|
-
{
|
|
2947
|
-
name: "output",
|
|
2948
|
-
alias: "o",
|
|
2949
|
-
type: String,
|
|
2950
|
-
defaultValue: "my_certificate_signing_request.csr",
|
|
2951
|
-
description: "the name of the generated signing_request"
|
|
2952
|
-
},
|
|
2953
|
-
{
|
|
2954
|
-
name: "dns",
|
|
2955
|
-
type: String,
|
|
2956
|
-
defaultValue: "{hostname}",
|
|
2957
|
-
description: "the list of valid domain name (comma separated)"
|
|
2958
|
-
},
|
|
2959
|
-
{ name: "ip", type: String, defaultValue: "", description: "the list of valid IPs (comma separated)" },
|
|
2960
|
-
{
|
|
2961
|
-
name: "subject",
|
|
2962
|
-
type: String,
|
|
2963
|
-
defaultValue: "/CN=Certificate",
|
|
2964
|
-
description: "the certificate subject ( for instance /C=FR/ST=Centre/L=Orleans/O=SomeOrganization/CN=Hello )"
|
|
2965
|
-
}
|
|
2966
|
-
];
|
|
2967
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
2968
|
-
if (local_argv.help) return showHelp("csr", "create a certificate signing request", optionsDef);
|
|
2969
|
-
await wrap(async () => {
|
|
2970
|
-
await readConfiguration(local_argv);
|
|
2971
|
-
if (!import_node_fs10.default.existsSync(gLocalConfig.PKIFolder || "")) {
|
|
2972
|
-
warningLog("PKI folder must exist");
|
|
2973
|
-
}
|
|
2974
|
-
await construct_CertificateManager();
|
|
2975
|
-
if (!gLocalConfig.outputFile || import_node_fs10.default.existsSync(gLocalConfig.outputFile)) {
|
|
2976
|
-
throw new Error(` File ${gLocalConfig.outputFile} already exist`);
|
|
2977
|
-
}
|
|
2978
|
-
gLocalConfig.privateKey = void 0;
|
|
2979
|
-
gLocalConfig.subject = local_argv.subject && local_argv.subject.length > 1 ? local_argv.subject : gLocalConfig.subject;
|
|
2980
|
-
const internal_csr_file = await certificateManager.createCertificateRequest(
|
|
2981
|
-
gLocalConfig
|
|
2982
|
-
);
|
|
2983
|
-
if (!internal_csr_file) {
|
|
2984
|
-
return;
|
|
2985
|
-
}
|
|
2986
|
-
if (!gLocalConfig.outputFile) {
|
|
2987
|
-
warningLog("please specify a output file");
|
|
2392
|
+
// make sure that all crls have been processed.
|
|
2393
|
+
async #waitAndCheckCRLProcessingStatus() {
|
|
2394
|
+
return new Promise((resolve, reject) => {
|
|
2395
|
+
if (this.#pendingCrlToProcess === 0) {
|
|
2396
|
+
setImmediate(resolve);
|
|
2988
2397
|
return;
|
|
2989
2398
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
warningLog("Subject = ", gLocalConfig.subject);
|
|
2993
|
-
warningLog("applicationUri = ", gLocalConfig.applicationUri);
|
|
2994
|
-
warningLog("altNames = ", gLocalConfig.altNames);
|
|
2995
|
-
warningLog("dns = ", gLocalConfig.dns);
|
|
2996
|
-
warningLog("ip = ", gLocalConfig.ip);
|
|
2997
|
-
warningLog("CSR file = ", gLocalConfig.outputFile);
|
|
2998
|
-
});
|
|
2999
|
-
return;
|
|
3000
|
-
}
|
|
3001
|
-
if (command === "sign") {
|
|
3002
|
-
const optionsDef = [
|
|
3003
|
-
...getOptions(["root", "CAFolder", "silent"]),
|
|
3004
|
-
{ name: "csr", alias: "i", type: String, defaultValue: "my_certificate_signing_request.csr", description: "the csr" },
|
|
3005
|
-
{
|
|
3006
|
-
name: "output",
|
|
3007
|
-
alias: "o",
|
|
3008
|
-
type: String,
|
|
3009
|
-
defaultValue: "my_certificate.pem",
|
|
3010
|
-
description: "the name of the generated certificate"
|
|
3011
|
-
},
|
|
3012
|
-
{ name: "validity", alias: "v", type: Number, defaultValue: 365, description: "the certificate validity in days" }
|
|
3013
|
-
];
|
|
3014
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
3015
|
-
if (local_argv.help || !local_argv.csr || !local_argv.output)
|
|
3016
|
-
return showHelp("sign", "validate a certificate signing request and generate a certificate", optionsDef);
|
|
3017
|
-
await wrap(async () => {
|
|
3018
|
-
await readConfiguration(local_argv);
|
|
3019
|
-
if (!import_node_fs10.default.existsSync(gLocalConfig.CAFolder || "")) {
|
|
3020
|
-
throw new Error(`CA folder must exist:${gLocalConfig.CAFolder}`);
|
|
3021
|
-
}
|
|
3022
|
-
await construct_CertificateAuthority2("");
|
|
3023
|
-
const csr_file = import_node_path7.default.resolve(local_argv.csr || "");
|
|
3024
|
-
if (!import_node_fs10.default.existsSync(csr_file)) {
|
|
3025
|
-
throw new Error(`Certificate signing request doesn't exist: ${csr_file}`);
|
|
3026
|
-
}
|
|
3027
|
-
const certificate = import_node_path7.default.resolve(local_argv.output || csr_file.replace(".csr", ".pem"));
|
|
3028
|
-
if (import_node_fs10.default.existsSync(certificate)) {
|
|
3029
|
-
throw new Error(` File ${certificate} already exist`);
|
|
2399
|
+
if (this.#onCrlProcess) {
|
|
2400
|
+
return reject(new Error("Internal Error"));
|
|
3030
2401
|
}
|
|
3031
|
-
|
|
3032
|
-
certificate,
|
|
3033
|
-
csr_file,
|
|
3034
|
-
gLocalConfig
|
|
3035
|
-
);
|
|
3036
|
-
(0, import_node_assert10.default)(typeof gLocalConfig.outputFile === "string");
|
|
3037
|
-
import_node_fs10.default.writeFileSync(gLocalConfig.outputFile || "", import_node_fs10.default.readFileSync(certificate, "ascii"));
|
|
3038
|
-
});
|
|
3039
|
-
return;
|
|
3040
|
-
}
|
|
3041
|
-
if (command === "dump") {
|
|
3042
|
-
const optionsDef = [
|
|
3043
|
-
{ name: "certificateFile", type: String, defaultOption: true },
|
|
3044
|
-
{ name: "help", alias: "h", type: Boolean }
|
|
3045
|
-
];
|
|
3046
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
3047
|
-
if (local_argv.help || !local_argv.certificateFile)
|
|
3048
|
-
return showHelp("dump <certificateFile>", "display a certificate", optionsDef);
|
|
3049
|
-
await wrap(async () => {
|
|
3050
|
-
const data = await dumpCertificate(local_argv.certificateFile);
|
|
3051
|
-
warningLog(data);
|
|
3052
|
-
});
|
|
3053
|
-
return;
|
|
3054
|
-
}
|
|
3055
|
-
if (command === "toder") {
|
|
3056
|
-
const optionsDef = [
|
|
3057
|
-
{ name: "pemCertificate", type: String, defaultOption: true },
|
|
3058
|
-
{ name: "help", alias: "h", type: Boolean }
|
|
3059
|
-
];
|
|
3060
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
3061
|
-
if (local_argv.help || !local_argv.pemCertificate)
|
|
3062
|
-
return showHelp("toder <pemCertificate>", "convert a certificate to a DER format with finger print", optionsDef);
|
|
3063
|
-
await wrap(async () => {
|
|
3064
|
-
await toDer(local_argv.pemCertificate);
|
|
3065
|
-
});
|
|
3066
|
-
return;
|
|
3067
|
-
}
|
|
3068
|
-
if (command === "fingerprint") {
|
|
3069
|
-
const optionsDef = [
|
|
3070
|
-
{ name: "certificateFile", type: String, defaultOption: true },
|
|
3071
|
-
{ name: "help", alias: "h", type: Boolean }
|
|
3072
|
-
];
|
|
3073
|
-
const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
|
|
3074
|
-
if (local_argv.help || !local_argv.certificateFile)
|
|
3075
|
-
return showHelp("fingerprint <certificateFile>", "print the certificate fingerprint", optionsDef);
|
|
3076
|
-
await wrap(async () => {
|
|
3077
|
-
const certificate = local_argv.certificateFile;
|
|
3078
|
-
const data = await fingerprint(certificate);
|
|
3079
|
-
if (!data) return;
|
|
3080
|
-
const s = data.split("=")[1].split(":").join("").trim();
|
|
3081
|
-
warningLog(s);
|
|
2402
|
+
this.#onCrlProcess = resolve;
|
|
3082
2403
|
});
|
|
3083
|
-
return;
|
|
3084
2404
|
}
|
|
3085
|
-
|
|
2405
|
+
};
|
|
2406
|
+
|
|
2407
|
+
// packages/node-opcua-pki/lib/pki/toolbox_pfx.ts
|
|
2408
|
+
var import_node_assert9 = __toESM(require("assert"));
|
|
2409
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
2410
|
+
var q2 = quote;
|
|
2411
|
+
var n3 = makePath;
|
|
2412
|
+
async function createPFX(options) {
|
|
2413
|
+
const { certificateFile, privateKeyFile, outputFile, passphrase = "", caCertificateFiles } = options;
|
|
2414
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);
|
|
2415
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);
|
|
2416
|
+
let cmd = `pkcs12 -export`;
|
|
2417
|
+
cmd += ` -in ${q2(n3(certificateFile))}`;
|
|
2418
|
+
cmd += ` -inkey ${q2(n3(privateKeyFile))}`;
|
|
2419
|
+
if (caCertificateFiles) {
|
|
2420
|
+
for (const caFile of caCertificateFiles) {
|
|
2421
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);
|
|
2422
|
+
cmd += ` -certfile ${q2(n3(caFile))}`;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
cmd += ` -out ${q2(n3(outputFile))}`;
|
|
2426
|
+
cmd += ` -passout pass:${passphrase}`;
|
|
2427
|
+
await execute_openssl(cmd, {});
|
|
2428
|
+
}
|
|
2429
|
+
async function extractCertificateFromPFX(options) {
|
|
2430
|
+
const { pfxFile, passphrase = "" } = options;
|
|
2431
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2432
|
+
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -clcerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
2433
|
+
return await execute_openssl(cmd, {});
|
|
2434
|
+
}
|
|
2435
|
+
async function extractPrivateKeyFromPFX(options) {
|
|
2436
|
+
const { pfxFile, passphrase = "" } = options;
|
|
2437
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2438
|
+
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -nocerts -nodes -passin pass:${passphrase}`;
|
|
2439
|
+
return await execute_openssl(cmd, {});
|
|
2440
|
+
}
|
|
2441
|
+
async function extractCACertificatesFromPFX(options) {
|
|
2442
|
+
const { pfxFile, passphrase = "" } = options;
|
|
2443
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2444
|
+
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -cacerts -nokeys -nodes -passin pass:${passphrase}`;
|
|
2445
|
+
return await execute_openssl(cmd, {});
|
|
2446
|
+
}
|
|
2447
|
+
async function extractAllFromPFX(options) {
|
|
2448
|
+
const [certificate, privateKey, caCertificates] = await Promise.all([
|
|
2449
|
+
extractCertificateFromPFX(options),
|
|
2450
|
+
extractPrivateKeyFromPFX(options),
|
|
2451
|
+
extractCACertificatesFromPFX(options)
|
|
2452
|
+
]);
|
|
2453
|
+
return { certificate, privateKey, caCertificates };
|
|
2454
|
+
}
|
|
2455
|
+
async function convertPFXtoPEM(pfxFile, pemFile, passphrase = "") {
|
|
2456
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2457
|
+
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -out ${q2(n3(pemFile))} -nodes -passin pass:${passphrase}`;
|
|
2458
|
+
await execute_openssl(cmd, {});
|
|
2459
|
+
}
|
|
2460
|
+
async function dumpPFX(pfxFile, passphrase = "") {
|
|
2461
|
+
(0, import_node_assert9.default)(import_node_fs10.default.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);
|
|
2462
|
+
const cmd = `pkcs12 -in ${q2(n3(pfxFile))} -info -nodes -passin pass:${passphrase}`;
|
|
2463
|
+
return await execute_openssl(cmd, {});
|
|
3086
2464
|
}
|
|
3087
2465
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3088
2466
|
0 && (module.exports = {
|
|
@@ -3093,22 +2471,15 @@ ${epilog}`
|
|
|
3093
2471
|
VerificationStatus,
|
|
3094
2472
|
adjustApplicationUri,
|
|
3095
2473
|
adjustDate,
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
displayTitle,
|
|
3104
|
-
doDebug,
|
|
2474
|
+
convertPFXtoPEM,
|
|
2475
|
+
createPFX,
|
|
2476
|
+
dumpPFX,
|
|
2477
|
+
extractAllFromPFX,
|
|
2478
|
+
extractCACertificatesFromPFX,
|
|
2479
|
+
extractCertificateFromPFX,
|
|
2480
|
+
extractPrivateKeyFromPFX,
|
|
3105
2481
|
findIssuerCertificateInChain,
|
|
3106
|
-
g_config,
|
|
3107
2482
|
install_prerequisite,
|
|
3108
|
-
|
|
3109
|
-
mkdirRecursiveSync,
|
|
3110
|
-
pki_main,
|
|
3111
|
-
quote,
|
|
3112
|
-
warningLog
|
|
2483
|
+
quote
|
|
3113
2484
|
});
|
|
3114
2485
|
//# sourceMappingURL=index.js.map
|