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/dist/index.js CHANGED
@@ -37,23 +37,16 @@ __export(lib_exports, {
37
37
  VerificationStatus: () => VerificationStatus,
38
38
  adjustApplicationUri: () => adjustApplicationUri,
39
39
  adjustDate: () => adjustDate,
40
- certificateFileExist: () => certificateFileExist,
41
- debugLog: () => debugLog,
42
- display: () => display,
43
- displayChapter: () => displayChapter,
44
- displayDebug: () => displayDebug,
45
- displayError: () => displayError,
46
- displaySubtitle: () => displaySubtitle,
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
- makePath: () => makePath,
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 n4 = makePath;
850
- var q3 = quote;
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 ${q3(n4(configFile))}`;
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 " + q3(n4(privateKeyFilename)) + " -out " + q3(n4(csrFilename)) + " " + subjectOpt,
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 " + q3(n4(configFile)) + " -in private/cakey.csr -signkey " + q3(n4(privateKeyFilename)) + " -out public/cacert.pem",
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 ${q3(n4(revocationList))} -text -noout`, options);
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
- * the file name where the current Certificate Revocation List is stored (in DER format)
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
- * the file name where the current Certificate Revocation List is stored (in PEM format)
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 " + q3(n4(privateKey)) + " -out " + q3(n4(csrFile)),
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 " + q3(n4(privateKey)) + " -startdate " + x509Date(params.startDate) + " -enddate " + x509Date(params.endDate) + " -batch -out " + q3(n4(certificateFile)) + " -in " + q3(n4(csrFile)),
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 ${q3(n4(certificateFile))} -dates -fingerprint -purpose -noout`, {});
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 ${q3(n4(certificateFile))} ${q3(n4(certificateFile))}`, options);
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
- * revoke a certificate and update the CRL
1001
+ * Revoke a certificate and regenerate the CRL.
1024
1002
  *
1025
- * @method revokeCertificate
1026
- * @param certificate - the certificate to revoke
1027
- * @param params
1028
- * @param [params.reason = "keyCompromise" {String}]
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 ${q3(n4(configFile))}`;
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 ${q3(certificate)} -crl_reason ${reason}`, options);
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 " + q3(n4(this.revocationList)) + " -CAfile " + q3(n4(this.caCertificate)) + " -crl_check " + q3(n4(certificate)),
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 ${q3(n4(this.revocationList))} -out crl/revocation_list.der -outform der`, options);
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 ${q3(n4(this.revocationList))} -out crl/revocation_list.pem -outform pem -text `, options);
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 - the certificate filename to generate
1070
- * @param certificateSigningRequestFilename - the certificate signing request
1071
- * @param params - parameters
1072
- * @param params.applicationUri - the applicationUri
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 dns2 = csrInfo.extensionRequest.subjectAltName.dNSName || [];
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: dns2,
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 " + q3(n4(certificate)) + " -in " + q3(n4(certificateSigningRequestFilename)),
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 ${q3(n4(certificate))} -dates -fingerprint -purpose -noout`, options);
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 ${q3(n4(this.caCertificateWithCrl))} ${q3(n4(certificate))}`,
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 import_node_assert8 = __toESM(require("assert"));
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, import_node_assert8.default)(params);
1229
- (0, import_node_assert8.default)(params.rootDir);
1230
- (0, import_node_assert8.default)(params.configFile);
1231
- (0, import_node_assert8.default)(params.privateKey);
1232
- (0, import_node_assert8.default)(typeof params.privateKey === "string");
1233
- (0, import_node_assert8.default)(import_node_fs7.default.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);
1234
- (0, import_node_assert8.default)(import_node_fs7.default.existsSync(params.rootDir), "RootDir key must exist");
1235
- (0, import_node_assert8.default)(typeof certificateSigningRequestFilename === "string");
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 import_node_assert9 = __toESM(require("assert"));
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, import_node_assert9.default)(params.purpose, "Please provide a Certificate Purpose");
1260
- (0, import_node_assert9.default)(import_node_fs8.default.existsSync(params.configFile));
1261
- (0, import_node_assert9.default)(import_node_fs8.default.existsSync(params.rootDir));
1262
- (0, import_node_assert9.default)(import_node_fs8.default.existsSync(params.privateKey));
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, import_node_assert9.default)(typeof params.applicationUri === "string");
1267
- (0, import_node_assert9.default)(Array.isArray(params.dns));
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, import_node_assert9.default)(Object.prototype.hasOwnProperty.call(params, "validity"));
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
- return (0, import_node_opcua_crypto5.makeSHA1Thumbprint)(certificate).toString("hex");
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 fingerprint2 = makeFingerprint(certificate);
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}[${fingerprint2}]`;
1242
+ return `${sanitizedCommonName}[${fingerprint}]`;
1338
1243
  } catch (_err) {
1339
- return `invalid_certificate_[${fingerprint2}]`;
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
- _watchers = [];
1404
- _readCertificatesCalled = false;
1405
- _filenameToHash = /* @__PURE__ */ new Map();
1406
- _initializingPromise;
1407
- _thumbs = {
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.location = makePath(options.location, "");
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.location)) {
1425
- throw new Error(`CertificateManager cannot access location ${this.location}`);
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.location;
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 moved.
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._moveCertificate(certificate, "rejected");
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 moved.
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._moveCertificate(certificate, "trusted");
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 fingerprint2 = makeFingerprint(certificate);
1503
- if (this._thumbs.trusted.has(fingerprint2)) {
1482
+ const fingerprint = makeFingerprint(certificate);
1483
+ if (this.#thumbs.trusted.has(fingerprint)) {
1504
1484
  return "Good";
1505
1485
  }
1506
- if (!this._thumbs.rejected.has(fingerprint2)) {
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._thumbs.rejected.set(fingerprint2, { certificate, filename });
1498
+ this.#thumbs.rejected.set(fingerprint, { certificate, filename });
1522
1499
  }
1523
1500
  return "BadCertificateUntrusted";
1524
1501
  }
1525
- async _innerVerifyCertificateAsync(certificate, _isIssuer, level, options) {
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._innerVerifyCertificateAsync(issuerCertificate, true, level + 1, options);
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._checkRejectedOrTrusted(issuerCertificate);
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._checkRejectedOrTrusted(certificate);
1590
+ const status = await this.#checkRejectedOrTrusted(certificate);
1614
1591
  if (status === "rejected") {
1615
- return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
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._innerVerifyCertificateAsync(certificate, false, 0, options);
1648
+ const status1 = await this.#innerVerifyCertificateAsync(certificate, false, 0, options);
1655
1649
  return status1;
1656
1650
  }
1657
1651
  /**
1658
- * Verify certificate validity
1659
- * @method verifyCertificate
1660
- * @param certificate
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
- return await this.verifyCertificateAsync(certificate, options || {});
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
- * PKI
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._initializingPromise = this._initialize();
1684
- await this._initializingPromise;
1685
- this._initializingPromise = void 0;
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 _initialize() {
1692
+ async #initialize() {
1689
1693
  this.state = 1 /* Initializing */;
1690
- const pkiDir = this.location;
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._readCertificates();
1717
+ await this.#readCertificates();
1714
1718
  } else {
1715
- await this._readCertificates();
1719
+ await this.#readCertificates();
1716
1720
  }
1717
1721
  });
1718
1722
  } else {
1719
- await this._readCertificates();
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._initializingPromise) {
1737
- await this._initializingPromise;
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._watchers.map((w) => w.close()));
1743
- this._watchers.forEach((w) => {
1746
+ await Promise.all(this.#watchers.map((w) => w.close()));
1747
+ this.#watchers.forEach((w) => {
1744
1748
  w.removeAllListeners();
1745
1749
  });
1746
- this._watchers.splice(0);
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._watchers.map((w) => w.close()));
1762
- for (const w of this._watchers) {
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._watchers.splice(0);
1766
- this._thumbs.rejected.clear();
1767
- this._thumbs.trusted.clear();
1768
- this._thumbs.issuers.certs.clear();
1769
- this._thumbs.crl.clear();
1770
- this._thumbs.issuersCrl.clear();
1771
- this._filenameToHash.clear();
1772
- this._readCertificatesCalled = false;
1773
- await this._readCertificates();
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
- * create a self-signed certificate for the CertificateManager private key
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 today2 = `${now.toISOString().slice(0, 10)}_${now.getTime()}`;
1818
- const certificateSigningRequestFilename = import_node_path6.default.join(this.rootDir, "own/certs", `certificate_${today2}.csr`);
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 fingerprint2 = makeFingerprint(certificate);
1840
- if (this._thumbs.issuers.certs.has(fingerprint2)) {
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._thumbs.issuers.certs.set(fingerprint2, { certificate, filename });
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._thumbs.crl : this._thumbs.issuersCrl;
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._on_crl_file_added(index, filename);
1870
- await this.waitAndCheckCRLProcessingStatus();
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._thumbs.issuersCrl);
1921
+ await clearFolder(this.issuersCrlFolder, this.#thumbs.issuersCrl);
1903
1922
  }
1904
1923
  if (target === "trusted" || target === "all") {
1905
- await clearFolder(this.crlFolder, this._thumbs.crl);
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._readCertificates();
1933
+ await this.#readCertificates();
1915
1934
  const normalized = thumbprint.toLowerCase();
1916
- return this._thumbs.issuers.certs.has(normalized);
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._readCertificates();
1945
+ await this.#readCertificates();
1927
1946
  const normalized = thumbprint.toLowerCase();
1928
- const entry = this._thumbs.trusted.get(normalized);
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._thumbs.trusted.delete(normalized);
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._readCertificates();
1969
+ await this.#readCertificates();
1951
1970
  const normalized = thumbprint.toLowerCase();
1952
- const entry = this._thumbs.issuers.certs.get(normalized);
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._thumbs.issuers.certs.delete(normalized);
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._thumbs.issuersCrl);
2009
+ await processIndex(this.#thumbs.issuersCrl);
1991
2010
  }
1992
2011
  if (target === "trusted" || target === "all") {
1993
- await processIndex(this._thumbs.crl);
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._readCertificates();
2063
- for (const entry of this._thumbs.trusted.values()) {
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._thumbs.issuers.certs.values()];
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._thumbs.trusted.values()];
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 _checkRejectedOrTrusted(certificate) {
2122
- const fingerprint2 = makeFingerprint(certificate);
2123
- debugLog("_checkRejectedOrTrusted fingerprint ", short(fingerprint2));
2124
- await this._readCertificates();
2125
- if (this._thumbs.rejected.has(fingerprint2)) {
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._thumbs.trusted.has(fingerprint2)) {
2150
+ if (this.#thumbs.trusted.has(fingerprint)) {
2129
2151
  return "trusted";
2130
2152
  }
2131
2153
  return "unknown";
2132
2154
  }
2133
- async _moveCertificate(certificate, newStatus) {
2155
+ async #moveCertificate(certificate, newStatus) {
2134
2156
  await this.withLock2(async () => {
2135
- const fingerprint2 = makeFingerprint(certificate);
2136
- const status = await this.getCertificateStatus(certificate);
2137
- debugLog("_moveCertificate", fingerprint2.substring(0, 10), "from", status, "to", newStatus);
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(`_moveCertificate: unexpected status '${status}' for certificate ${fingerprint2.substring(0, 10)}`);
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._thumbs.rejected : this._thumbs.trusted;
2143
- const srcEntry = indexSrc.get(fingerprint2);
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 ", fingerprint2.substring(0, 10), " in", status);
2146
- throw new Error(`_moveCertificate: certificate ${fingerprint2.substring(0, 10)} not found in ${status} index`);
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("_moveCertificate", fingerprint2.substring(0, 10), "old name", srcEntry.filename);
2151
- debugLog("_moveCertificate", fingerprint2.substring(0, 10), "new name", certificateDest);
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(fingerprint2);
2154
- const indexDest = newStatus === "trusted" ? this._thumbs.trusted : this._thumbs.rejected;
2155
- indexDest.set(fingerprint2, { certificate, filename: certificateDest });
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
- _findAssociatedCRLs(issuerCertificate) {
2188
+ #findAssociatedCRLs(issuerCertificate) {
2160
2189
  const issuerCertificateInfo = (0, import_node_opcua_crypto5.exploreCertificate)(issuerCertificate);
2161
2190
  const key = issuerCertificateInfo.tbsCertificate.subjectFingerPrint;
2162
- return this._thumbs.issuersCrl.get(key) ?? this._thumbs.crl.get(key) ?? null;
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._findAssociatedCRLs(issuerCertificate);
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._thumbs.crl.get(key) ?? null;
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
- _pending_crl_to_process = 0;
2188
- _on_crl_process;
2189
- queue = [];
2190
- _on_crl_file_added(index, filename) {
2191
- this.queue.push({ index, filename });
2192
- this._pending_crl_to_process += 1;
2193
- if (this._pending_crl_to_process === 1) {
2194
- this._process_next_crl();
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 _process_next_crl() {
2242
+ async #processNextCrl() {
2198
2243
  try {
2199
- const nextCRL = this.queue.shift();
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 fingerprint2 = crlInfo.tbsCertList.issuerFingerprint;
2206
- if (!index.has(fingerprint2)) {
2207
- index.set(fingerprint2, { crls: [], serialNumbers: {} });
2250
+ const fingerprint = crlInfo.tbsCertList.issuerFingerprint;
2251
+ if (!index.has(fingerprint)) {
2252
+ index.set(fingerprint, { crls: [], serialNumbers: {} });
2208
2253
  }
2209
- const data = index.get(fingerprint2) || { crls: [], serialNumbers: {} };
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"), fingerprint2, "serial numbers = ", Object.keys(data.serialNumbers));
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._pending_crl_to_process -= 1;
2223
- if (this._pending_crl_to_process === 0) {
2224
- if (this._on_crl_process) {
2225
- this._on_crl_process();
2226
- this._on_crl_process = void 0;
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._process_next_crl();
2274
+ this.#processNextCrl();
2230
2275
  }
2231
2276
  }
2232
- async _readCertificates() {
2233
- if (this._readCertificatesCalled) {
2277
+ async #readCertificates() {
2278
+ if (this.#readCertificatesCalled) {
2234
2279
  return;
2235
2280
  }
2236
- this._readCertificatesCalled = true;
2281
+ this.#readCertificatesCalled = true;
2237
2282
  const usePolling = process.env.OPCUA_PKI_USE_POLLING === "true";
2238
- const options = {
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
- async function _walkCRLFiles(folder, index) {
2244
- await new Promise((resolve, _reject) => {
2245
- const w = import_chokidar.default.watch(folder, options);
2246
- w.on("unlink", (filename) => {
2247
- for (const [key, data] of index.entries()) {
2248
- data.crls = data.crls.filter((c) => c.filename !== filename);
2249
- if (data.crls.length === 0) {
2250
- index.delete(key);
2251
- }
2252
- }
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
- async function _walkAllFiles(folder, index) {
2267
- const w = import_chokidar.default.watch(folder, options);
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
- debugLog(import_chalk6.default.cyan(`unlink in folder ${folder}`), filename);
2270
- const h = this._filenameToHash.get(filename);
2271
- if (h && index.has(h)) {
2272
- index.delete(h);
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
- debugLog(import_chalk6.default.cyan(`add in folder ${folder}`), filename);
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(import_chalk6.default.cyan(`change in folder ${folder}`), changedPath);
2296
- try {
2297
- const certificate = (0, import_node_opcua_crypto5.readCertificate)(changedPath);
2298
- const newFingerprint = makeFingerprint(certificate);
2299
- const oldHash = this._filenameToHash.get(changedPath);
2300
- if (oldHash && oldHash !== newFingerprint) {
2301
- index.delete(oldHash);
2302
- }
2303
- index.set(newFingerprint, { certificate, filename: changedPath, info: (0, import_node_opcua_crypto5.exploreCertificate)(certificate) });
2304
- this._filenameToHash.set(changedPath, newFingerprint);
2305
- } catch (err) {
2306
- debugLog(`change event: failed to re-read ${changedPath}`, err);
2307
- }
2330
+ debugLog("change in folder ", folder, changedPath);
2308
2331
  });
2309
- this._watchers.push(w);
2310
- await new Promise((resolve, _reject) => {
2311
- w.on("ready", () => {
2312
- debugLog("ready");
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
- var certificateManager;
2371
- async function construct_CertificateManager() {
2372
- (0, import_node_assert10.default)(typeof gLocalConfig.PKIFolder === "string", "expecting a PKIFolder in config");
2373
- if (!certificateManager) {
2374
- certificateManager = new CertificateManager({
2375
- keySize: gLocalConfig.keySize,
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
- async function createSelfSignedCertificate2(certificate, private_key, applicationUri2, startDate, validity) {
2559
- await g_certificateAuthority.createSelfSignedCertificate(certificate, private_key, {
2560
- applicationUri: applicationUri2,
2561
- dns: dns2,
2562
- ip,
2563
- startDate,
2564
- validity
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
- async function revoke_certificate(certificate) {
2568
- await g_certificateAuthority.revokeCertificate(certificate, {});
2569
- }
2570
- async function createPrivateKeyIfNotExist(privateKey, keyLength) {
2571
- if (import_node_fs10.default.existsSync(privateKey)) {
2572
- warningLog(import_chalk7.default.yellow(" privateKey"), import_chalk7.default.cyan(privateKey), import_chalk7.default.yellow(" already exists => skipping"));
2573
- return;
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
- mkdirRecursiveSync(certificateDir);
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
- return;
2786
- }
2787
- if (command === "createCA") {
2788
- const optionsDef = [
2789
- ...getOptions(["root", "CAFolder", "keySize", "silent"]),
2790
- { name: "subject", type: String, defaultValue: defaultSubject, description: "the CA certificate subject" }
2791
- ];
2792
- const local_argv = (0, import_command_line_args.default)(optionsDef, { argv });
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
- if (command === "csr") {
2937
- const optionsDef = [
2938
- ...getOptions(["root", "PKIFolder", "privateKey", "silent"]),
2939
- {
2940
- name: "applicationUri",
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
- const csr = await import_node_fs10.default.promises.readFile(internal_csr_file, "utf-8");
2991
- import_node_fs10.default.writeFileSync(gLocalConfig.outputFile || "", csr, "utf-8");
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
- await g_certificateAuthority.signCertificateRequest(
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
- console.log(`Unknown command: ${command}`);
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
- certificateFileExist,
3097
- debugLog,
3098
- display,
3099
- displayChapter,
3100
- displayDebug,
3101
- displayError,
3102
- displaySubtitle,
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
- makePath,
3109
- mkdirRecursiveSync,
3110
- pki_main,
3111
- quote,
3112
- warningLog
2483
+ quote
3113
2484
  });
3114
2485
  //# sourceMappingURL=index.js.map