node-opcua-pki 6.11.0 → 6.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/pki.mjs CHANGED
@@ -354,38 +354,21 @@ import {
354
354
  exploreCertificateRevocationList,
355
355
  generatePrivateKeyFile,
356
356
  makeSHA1Thumbprint,
357
- readCertificate,
358
- readCertificateAsync,
357
+ readCertificateChain,
358
+ readCertificateChainAsync,
359
359
  readCertificateRevocationList,
360
360
  split_der,
361
361
  toPem,
362
- verifyCertificateChain,
363
362
  verifyCertificateSignature
364
363
  } from "node-opcua-crypto";
365
364
  function getOrComputeInfo(entry) {
366
365
  if (!entry.info) {
367
- entry.info = exploreCertificateCached(entry.certificate);
366
+ entry.info = exploreCertificate(entry.certificate);
368
367
  }
369
368
  return entry.info;
370
369
  }
371
- function exploreCertificateCached(certificate) {
372
- const key = makeSHA1Thumbprint(certificate).toString("hex");
373
- const cached = _exploreCache.get(key);
374
- if (cached) {
375
- _exploreCache.delete(key);
376
- _exploreCache.set(key, cached);
377
- return cached;
378
- }
379
- const info = exploreCertificate(certificate);
380
- _exploreCache.set(key, info);
381
- if (_exploreCache.size > EXPLORE_CACHE_MAX) {
382
- const oldest = _exploreCache.keys().next().value;
383
- if (oldest) _exploreCache.delete(oldest);
384
- }
385
- return info;
386
- }
387
370
  function makeFingerprint(certificate) {
388
- const chain = split_der(certificate);
371
+ const chain = Array.isArray(certificate) ? certificate : split_der(certificate);
389
372
  return makeSHA1Thumbprint(chain[0]).toString("hex");
390
373
  }
391
374
  function short(stringToShorten) {
@@ -394,7 +377,7 @@ function short(stringToShorten) {
394
377
  function buildIdealCertificateName(certificate) {
395
378
  const fingerprint2 = makeFingerprint(certificate);
396
379
  try {
397
- const commonName = exploreCertificateCached(certificate).tbsCertificate.subject.commonName || "";
380
+ const commonName = exploreCertificate(certificate).tbsCertificate.subject.commonName || "";
398
381
  const sanitizedCommonName = commonName.replace(forbiddenChars, "_");
399
382
  return `${sanitizedCommonName}[${fingerprint2}]`;
400
383
  } catch (_err) {
@@ -411,16 +394,36 @@ function isSelfSigned2(info) {
411
394
  return info.tbsCertificate.extensions?.subjectKeyIdentifier === info.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;
412
395
  }
413
396
  function isSelfSigned3(certificate) {
414
- const info = exploreCertificateCached(certificate);
397
+ const info = exploreCertificate(certificate);
415
398
  return isSelfSigned2(info);
416
399
  }
400
+ function _isIssuerInfo(info) {
401
+ const basicConstraints = info.tbsCertificate.extensions?.basicConstraints;
402
+ if (basicConstraints?.cA) {
403
+ return true;
404
+ }
405
+ const keyUsage = info.tbsCertificate.extensions?.keyUsage;
406
+ if (keyUsage?.keyCertSign) {
407
+ return true;
408
+ }
409
+ return false;
410
+ }
411
+ function isIssuer(certificate) {
412
+ try {
413
+ const info = exploreCertificate(certificate);
414
+ return _isIssuerInfo(info);
415
+ } catch (_err) {
416
+ return false;
417
+ }
418
+ }
417
419
  function findIssuerCertificateInChain(certificate, chain) {
418
- if (!certificate) {
420
+ const firstCertificate = Array.isArray(certificate) ? certificate[0] : certificate;
421
+ if (!firstCertificate) {
419
422
  return null;
420
423
  }
421
- const certInfo = exploreCertificateCached(certificate);
424
+ const certInfo = exploreCertificate(firstCertificate);
422
425
  if (isSelfSigned2(certInfo)) {
423
- return certificate;
426
+ return firstCertificate;
424
427
  }
425
428
  const wantedIssuerKey = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;
426
429
  if (!wantedIssuerKey) {
@@ -428,7 +431,7 @@ function findIssuerCertificateInChain(certificate, chain) {
428
431
  return null;
429
432
  }
430
433
  const potentialIssuers = chain.filter((c) => {
431
- const info = exploreCertificateCached(c);
434
+ const info = exploreCertificate(c);
432
435
  return info.tbsCertificate.extensions && info.tbsCertificate.extensions.subjectKeyIdentifier === wantedIssuerKey;
433
436
  });
434
437
  if (potentialIssuers.length === 1) {
@@ -440,7 +443,7 @@ function findIssuerCertificateInChain(certificate, chain) {
440
443
  }
441
444
  return null;
442
445
  }
443
- var configurationFileSimpleTemplate, fsWriteFile, EXPLORE_CACHE_MAX, _exploreCache, forbiddenChars, CertificateManager;
446
+ var configurationFileSimpleTemplate, fsWriteFile, forbiddenChars, CertificateManager;
444
447
  var init_certificate_manager = __esm({
445
448
  "packages/node-opcua-pki/lib/pki/certificate_manager.ts"() {
446
449
  "use strict";
@@ -451,8 +454,6 @@ var init_certificate_manager = __esm({
451
454
  init_simple_config_template_cnf();
452
455
  configurationFileSimpleTemplate = simple_config_template_cnf_default;
453
456
  fsWriteFile = fs4.promises.writeFile;
454
- EXPLORE_CACHE_MAX = 8;
455
- _exploreCache = /* @__PURE__ */ new Map();
456
457
  forbiddenChars = /[\x00-\x1F<>:"/\\|?*]/g;
457
458
  CertificateManager = class _CertificateManager extends EventEmitter {
458
459
  // ── Global instance registry ─────────────────────────────────
@@ -545,6 +546,7 @@ var init_certificate_manager = __esm({
545
546
  #readCertificatesCalled = false;
546
547
  #filenameToHash = /* @__PURE__ */ new Map();
547
548
  #initializingPromise;
549
+ #addCertValidation;
548
550
  #thumbs = {
549
551
  rejected: /* @__PURE__ */ new Map(),
550
552
  trusted: /* @__PURE__ */ new Map(),
@@ -571,6 +573,13 @@ var init_certificate_manager = __esm({
571
573
  }
572
574
  this.#location = makePath(options.location, "");
573
575
  this.keySize = options.keySize;
576
+ const v = options.addCertificateValidationOptions ?? {};
577
+ this.#addCertValidation = {
578
+ acceptExpiredCertificate: v.acceptExpiredCertificate ?? false,
579
+ acceptRevokedCertificate: v.acceptRevokedCertificate ?? false,
580
+ ignoreMissingRevocationList: v.ignoreMissingRevocationList ?? false,
581
+ maxChainLength: v.maxChainLength ?? 5
582
+ };
574
583
  mkdirRecursiveSync(options.location);
575
584
  if (!fs4.existsSync(this.#location)) {
576
585
  throw new Error(`CertificateManager cannot access location ${this.#location}`);
@@ -661,7 +670,12 @@ var init_certificate_manager = __esm({
661
670
  * or `"BadCertificateInvalid"` if the certificate cannot be parsed.
662
671
  */
663
672
  async isCertificateTrusted(certificate) {
664
- const fingerprint2 = makeFingerprint(certificate);
673
+ let fingerprint2;
674
+ try {
675
+ fingerprint2 = makeFingerprint(certificate);
676
+ } catch (_err) {
677
+ return "BadCertificateInvalid";
678
+ }
665
679
  if (this.#thumbs.trusted.has(fingerprint2)) {
666
680
  return "Good";
667
681
  }
@@ -681,13 +695,13 @@ var init_certificate_manager = __esm({
681
695
  }
682
696
  return "BadCertificateUntrusted";
683
697
  }
684
- async #innerVerifyCertificateAsync(certificate, _isIssuer, level, options) {
698
+ async #innerVerifyCertificateAsync(certificateOrChain, _isIssuer, level, options) {
685
699
  if (level >= 5) {
686
700
  return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
687
701
  }
688
- const chain = split_der(certificate);
702
+ const chain = Array.isArray(certificateOrChain) ? certificateOrChain : [certificateOrChain];
689
703
  debugLog("NB CERTIFICATE IN CHAIN = ", chain.length);
690
- const info = exploreCertificateCached(chain[0]);
704
+ const info = exploreCertificate(chain[0]);
691
705
  let hasValidIssuer = false;
692
706
  let hasTrustedIssuer = false;
693
707
  const hasIssuerKey = info.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;
@@ -724,7 +738,7 @@ var init_certificate_manager = __esm({
724
738
  return "BadCertificateIssuerRevocationUnknown" /* BadCertificateIssuerRevocationUnknown */;
725
739
  }
726
740
  if (issuerStatus === "BadCertificateTimeInvalid" /* BadCertificateTimeInvalid */) {
727
- if (!options || !options.acceptOutDatedIssuerCertificate) {
741
+ if (!options?.acceptOutDatedIssuerCertificate) {
728
742
  return "BadCertificateIssuerTimeInvalid" /* BadCertificateIssuerTimeInvalid */;
729
743
  }
730
744
  }
@@ -734,13 +748,13 @@ var init_certificate_manager = __esm({
734
748
  if (issuerStatus !== "Good" /* Good */ && issuerStatus !== "BadCertificateUntrusted" /* BadCertificateUntrusted */) {
735
749
  return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
736
750
  }
737
- const isCertificateSignatureOK = verifyCertificateSignature(certificate, issuerCertificate);
751
+ const isCertificateSignatureOK = verifyCertificateSignature(chain[0], issuerCertificate);
738
752
  if (!isCertificateSignatureOK) {
739
753
  debugLog(" the certificate was not signed by the issuer as it claim to be ! Danger");
740
754
  return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
741
755
  }
742
756
  hasValidIssuer = true;
743
- let revokedStatus = await this.isCertificateRevoked(certificate);
757
+ let revokedStatus = await this.isCertificateRevoked(certificateOrChain);
744
758
  if (revokedStatus === "BadCertificateRevocationUnknown" /* BadCertificateRevocationUnknown */) {
745
759
  if (options?.ignoreMissingRevocationList) {
746
760
  revokedStatus = "Good" /* Good */;
@@ -760,16 +774,16 @@ var init_certificate_manager = __esm({
760
774
  return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
761
775
  }
762
776
  } else {
763
- const isCertificateSignatureOK = verifyCertificateSignature(certificate, certificate);
777
+ const isCertificateSignatureOK = verifyCertificateSignature(chain[0], chain[0]);
764
778
  if (!isCertificateSignatureOK) {
765
779
  debugLog("Self-signed Certificate signature is not valid");
766
780
  return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
767
781
  }
768
- const revokedStatus = await this.isCertificateRevoked(certificate);
782
+ const revokedStatus = await this.isCertificateRevoked(certificateOrChain);
769
783
  debugLog("revokedStatus of self signed certificate:", revokedStatus);
770
784
  }
771
785
  }
772
- const status = await this.#checkRejectedOrTrusted(certificate);
786
+ const status = await this.#checkRejectedOrTrusted(certificateOrChain);
773
787
  if (status === "rejected") {
774
788
  if (!(options.acceptCertificateWithValidIssuerChain && hasValidIssuer && hasTrustedIssuer)) {
775
789
  return "BadCertificateUntrusted" /* BadCertificateUntrusted */;
@@ -777,7 +791,7 @@ var init_certificate_manager = __esm({
777
791
  }
778
792
  const _c2 = chain[1] ? exploreCertificateInfo(chain[1]) : "non";
779
793
  debugLog("chain[1] info=", _c2);
780
- const certificateInfo = exploreCertificateInfo(certificate);
794
+ const certificateInfo = exploreCertificateInfo(chain[0]);
781
795
  const now = /* @__PURE__ */ new Date();
782
796
  let isTimeInvalid = false;
783
797
  if (certificateInfo.notBefore.getTime() > now.getTime()) {
@@ -827,6 +841,16 @@ var init_certificate_manager = __esm({
827
841
  * @returns the verification status code
828
842
  */
829
843
  async verifyCertificateAsync(certificate, options) {
844
+ if (!Array.isArray(certificate)) {
845
+ try {
846
+ const derElements = split_der(certificate);
847
+ for (const element of derElements) {
848
+ exploreCertificateInfo(element);
849
+ }
850
+ } catch (_err) {
851
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
852
+ }
853
+ }
830
854
  const status1 = await this.#innerVerifyCertificateAsync(certificate, false, 0, options);
831
855
  return status1;
832
856
  }
@@ -1054,6 +1078,23 @@ var init_certificate_manager = __esm({
1054
1078
  }
1055
1079
  return "Good" /* Good */;
1056
1080
  }
1081
+ /**
1082
+ * Add multiple CA (issuer) certificates to the issuers store.
1083
+ * @param certificates - the DER-encoded CA certificates
1084
+ * @param validate - if `true`, verify each certificate before adding
1085
+ * @param addInTrustList - if `true`, also add each certificate to the trusted store
1086
+ * @returns `VerificationStatus.Good` on success
1087
+ */
1088
+ async addIssuers(certificates, validate = false, addInTrustList = false) {
1089
+ for (const certificate of certificates) {
1090
+ if (!isIssuer(certificate)) {
1091
+ warningLog(`Certificate ${makeFingerprint(certificate)} is not a issuer certificate`);
1092
+ continue;
1093
+ }
1094
+ await this.addIssuer(certificate, validate, addInTrustList);
1095
+ }
1096
+ return "Good" /* Good */;
1097
+ }
1057
1098
  /**
1058
1099
  * Add a CRL to the certificate manager.
1059
1100
  * @param crl - the CRL to add
@@ -1177,7 +1218,7 @@ var init_certificate_manager = __esm({
1177
1218
  * @param target - "issuers", "trusted", or "all" (default "all")
1178
1219
  */
1179
1220
  async removeRevocationListsForIssuer(issuerCertificate, target = "all") {
1180
- const issuerInfo = exploreCertificateCached(issuerCertificate);
1221
+ const issuerInfo = exploreCertificate(issuerCertificate);
1181
1222
  const issuerFingerprint = issuerInfo.tbsCertificate.subjectFingerPrint;
1182
1223
  const processIndex = async (index) => {
1183
1224
  const crlData = index.get(issuerFingerprint);
@@ -1204,41 +1245,125 @@ var init_certificate_manager = __esm({
1204
1245
  * Validate a certificate (optionally with its chain) and add
1205
1246
  * the leaf certificate to the trusted store.
1206
1247
  *
1207
- * The certificate buffer may contain a single certificate or a
1208
- * full chain (leaf + issuer certificates concatenated in DER).
1209
- * Only the leaf certificate is added to the trusted store.
1248
+ * Performs OPC UA Part 4, Table 100 validation:
1210
1249
  *
1211
- * When the chain contains issuer certificates, this method
1212
- * verifies that each issuer is already registered via
1213
- * {@link addIssuer} before trusting the leaf.
1250
+ * 1. **Certificate Structure** parse the DER encoding.
1251
+ * 2. **Build Certificate Chain** walk from the leaf to a
1252
+ * self-signed root CA, using the provided chain and the
1253
+ * issuers store.
1254
+ * 3. **Signature** — verify each certificate's signature
1255
+ * against its issuer.
1256
+ * 4. **Issuer Presence** — every issuer in the chain must
1257
+ * already be registered in the issuers store (per GDS
1258
+ * 7.8.2.6).
1259
+ * 5. **Validity Period** — each certificate must be within
1260
+ * its validity window (overridable via
1261
+ * {@link AddCertificateValidationOptions.acceptExpiredCertificate}).
1262
+ * 6. **Revocation Check** — each certificate is checked
1263
+ * against its issuer's CRL (overridable via
1264
+ * {@link AddCertificateValidationOptions.acceptRevokedCertificate}
1265
+ * and {@link AddCertificateValidationOptions.ignoreMissingRevocationList}).
1214
1266
  *
1215
- * If one of the certificates in the chain is not registered in the issuers store,
1216
- * the leaf certificate will be rejected.
1267
+ * Only the leaf certificate is added to the trusted store.
1217
1268
  *
1218
1269
  * @param certificateChain - DER-encoded certificate or chain
1219
1270
  * @returns `VerificationStatus.Good` on success, or an error
1220
1271
  * status indicating why the certificate was rejected.
1221
1272
  */
1222
1273
  async addTrustedCertificateFromChain(certificateChain) {
1223
- const certificates = split_der(certificateChain);
1224
- const leafCertificate = certificates[0];
1225
1274
  try {
1226
- exploreCertificateCached(leafCertificate);
1275
+ return await this.#addTrustedCertificateFromChainImpl(certificateChain);
1227
1276
  } catch (_err) {
1277
+ warningLog("addTrustedCertificateFromChain: unexpected error", _err);
1228
1278
  return "BadCertificateInvalid" /* BadCertificateInvalid */;
1229
1279
  }
1230
- const result = await verifyCertificateChain([leafCertificate]);
1231
- if (result.status !== "Good") {
1280
+ }
1281
+ async #addTrustedCertificateFromChainImpl(certificateChain) {
1282
+ let certificates;
1283
+ try {
1284
+ certificates = Array.isArray(certificateChain) ? certificateChain : split_der(certificateChain);
1285
+ } catch (_err) {
1286
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1287
+ }
1288
+ if (certificates.length === 0) {
1232
1289
  return "BadCertificateInvalid" /* BadCertificateInvalid */;
1233
1290
  }
1234
- if (certificates.length > 1) {
1235
- await this.#scanCertFolder(this.issuersCertFolder, this.#thumbs.issuers.certs);
1236
- for (const issuerCert of certificates.slice(1)) {
1237
- const thumbprint = makeFingerprint(issuerCert);
1238
- if (!await this.hasIssuer(thumbprint)) {
1291
+ const leafCertificate = certificates[0];
1292
+ const opts = this.#addCertValidation;
1293
+ let leafInfo;
1294
+ try {
1295
+ leafInfo = exploreCertificate(leafCertificate);
1296
+ } catch (_err) {
1297
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1298
+ }
1299
+ await this.#scanCertFolder(this.issuersCertFolder, this.#thumbs.issuers.certs);
1300
+ let currentCert = leafCertificate;
1301
+ let currentInfo = leafInfo;
1302
+ let depth = 0;
1303
+ while (true) {
1304
+ depth++;
1305
+ if (depth > opts.maxChainLength) {
1306
+ return "BadSecurityChecksFailed" /* BadSecurityChecksFailed */;
1307
+ }
1308
+ if (!opts.acceptExpiredCertificate) {
1309
+ let certDetails;
1310
+ try {
1311
+ certDetails = exploreCertificateInfo(currentCert);
1312
+ } catch (_err) {
1313
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1314
+ }
1315
+ const now = /* @__PURE__ */ new Date();
1316
+ if (certDetails.notBefore.getTime() > now.getTime()) {
1317
+ return "BadCertificateTimeInvalid" /* BadCertificateTimeInvalid */;
1318
+ }
1319
+ if (certDetails.notAfter.getTime() <= now.getTime()) {
1320
+ return depth === 1 ? "BadCertificateTimeInvalid" /* BadCertificateTimeInvalid */ : "BadCertificateIssuerTimeInvalid" /* BadCertificateIssuerTimeInvalid */;
1321
+ }
1322
+ }
1323
+ if (isSelfSigned2(currentInfo)) {
1324
+ try {
1325
+ if (!verifyCertificateSignature(currentCert, currentCert)) {
1326
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1327
+ }
1328
+ } catch (_err) {
1329
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1330
+ }
1331
+ break;
1332
+ }
1333
+ let issuerCert = await this.findIssuerCertificate(currentCert);
1334
+ if (!issuerCert) {
1335
+ issuerCert = findIssuerCertificateInChain(currentCert, certificates);
1336
+ if (!issuerCert || issuerCert === currentCert) {
1239
1337
  return "BadCertificateChainIncomplete" /* BadCertificateChainIncomplete */;
1240
1338
  }
1241
1339
  }
1340
+ try {
1341
+ if (!verifyCertificateSignature(currentCert, issuerCert)) {
1342
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1343
+ }
1344
+ } catch (_err) {
1345
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1346
+ }
1347
+ const issuerThumbprint = makeFingerprint(issuerCert);
1348
+ if (!await this.hasIssuer(issuerThumbprint)) {
1349
+ return "BadCertificateChainIncomplete" /* BadCertificateChainIncomplete */;
1350
+ }
1351
+ const revokedStatus = await this.isCertificateRevoked(currentCert, issuerCert);
1352
+ if (revokedStatus === "BadCertificateRevoked" /* BadCertificateRevoked */) {
1353
+ if (!opts.acceptRevokedCertificate) {
1354
+ return "BadCertificateRevoked" /* BadCertificateRevoked */;
1355
+ }
1356
+ } else if (revokedStatus === "BadCertificateRevocationUnknown" /* BadCertificateRevocationUnknown */) {
1357
+ if (!opts.ignoreMissingRevocationList) {
1358
+ return "BadCertificateRevocationUnknown" /* BadCertificateRevocationUnknown */;
1359
+ }
1360
+ }
1361
+ currentCert = issuerCert;
1362
+ try {
1363
+ currentInfo = exploreCertificate(currentCert);
1364
+ } catch (_err) {
1365
+ return "BadCertificateInvalid" /* BadCertificateInvalid */;
1366
+ }
1242
1367
  }
1243
1368
  await this.trustCertificate(leafCertificate);
1244
1369
  return "Good" /* Good */;
@@ -1283,9 +1408,10 @@ var init_certificate_manager = __esm({
1283
1408
  *
1284
1409
  */
1285
1410
  async findIssuerCertificate(certificate) {
1286
- const certInfo = exploreCertificateCached(certificate);
1411
+ const firstCertificate = Array.isArray(certificate) ? certificate[0] : certificate;
1412
+ const certInfo = exploreCertificate(firstCertificate);
1287
1413
  if (isSelfSigned2(certInfo)) {
1288
- return certificate;
1414
+ return firstCertificate;
1289
1415
  }
1290
1416
  const wantedIssuerKey = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;
1291
1417
  if (!wantedIssuerKey) {
@@ -1319,7 +1445,8 @@ var init_certificate_manager = __esm({
1319
1445
  * @private
1320
1446
  */
1321
1447
  async #checkRejectedOrTrusted(certificate) {
1322
- const fingerprint2 = makeFingerprint(certificate);
1448
+ const firstCertificate = Array.isArray(certificate) ? certificate[0] : certificate;
1449
+ const fingerprint2 = makeFingerprint(firstCertificate);
1323
1450
  debugLog("#checkRejectedOrTrusted fingerprint ", short(fingerprint2));
1324
1451
  await this.#readCertificates();
1325
1452
  if (this.#thumbs.rejected.has(fingerprint2)) {
@@ -1364,7 +1491,7 @@ var init_certificate_manager = __esm({
1364
1491
  });
1365
1492
  }
1366
1493
  #findAssociatedCRLs(issuerCertificate) {
1367
- const issuerCertificateInfo = exploreCertificateCached(issuerCertificate);
1494
+ const issuerCertificateInfo = exploreCertificate(issuerCertificate);
1368
1495
  const key = issuerCertificateInfo.tbsCertificate.subjectFingerPrint;
1369
1496
  return this.#thumbs.issuersCrl.get(key) ?? this.#thumbs.crl.get(key) ?? null;
1370
1497
  }
@@ -1385,11 +1512,12 @@ var init_certificate_manager = __esm({
1385
1512
  * found.
1386
1513
  */
1387
1514
  async isCertificateRevoked(certificate, issuerCertificate) {
1388
- if (isSelfSigned3(certificate)) {
1515
+ const firstCertificate = Array.isArray(certificate) ? certificate[0] : certificate;
1516
+ if (isSelfSigned3(firstCertificate)) {
1389
1517
  return "Good" /* Good */;
1390
1518
  }
1391
1519
  if (!issuerCertificate) {
1392
- issuerCertificate = await this.findIssuerCertificate(certificate);
1520
+ issuerCertificate = await this.findIssuerCertificate(firstCertificate);
1393
1521
  }
1394
1522
  if (!issuerCertificate) {
1395
1523
  return "BadCertificateChainIncomplete" /* BadCertificateChainIncomplete */;
@@ -1398,7 +1526,7 @@ var init_certificate_manager = __esm({
1398
1526
  if (!crls) {
1399
1527
  return "BadCertificateRevocationUnknown" /* BadCertificateRevocationUnknown */;
1400
1528
  }
1401
- const certInfo = exploreCertificateCached(certificate);
1529
+ const certInfo = exploreCertificate(firstCertificate);
1402
1530
  const serialNumber = certInfo.tbsCertificate.serialNumber || certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.serial || "";
1403
1531
  const key = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint || "<unknown>";
1404
1532
  const crl2 = this.#thumbs.crl.get(key) ?? null;
@@ -1517,8 +1645,8 @@ var init_certificate_manager = __esm({
1517
1645
  try {
1518
1646
  const stat = await fs4.promises.stat(filename);
1519
1647
  if (!stat.isFile()) continue;
1520
- const certificate = await readCertificateAsync(filename);
1521
- const info = exploreCertificateCached(certificate);
1648
+ const certificate = (await readCertificateChainAsync(filename))[0];
1649
+ const info = exploreCertificate(certificate);
1522
1650
  const fingerprint2 = makeFingerprint(certificate);
1523
1651
  index.set(fingerprint2, { certificate, filename, info });
1524
1652
  this.#filenameToHash.set(filename, fingerprint2);
@@ -1604,8 +1732,8 @@ var init_certificate_manager = __esm({
1604
1732
  w.on("add", (filename) => {
1605
1733
  debugLog(chalk3.cyan(`add in folder ${folder}`), filename);
1606
1734
  try {
1607
- const certificate = readCertificate(filename);
1608
- const info = exploreCertificateCached(certificate);
1735
+ const certificate = readCertificateChain(filename)[0];
1736
+ const info = exploreCertificate(certificate);
1609
1737
  const fingerprint2 = makeFingerprint(certificate);
1610
1738
  const isNew = !index.has(fingerprint2);
1611
1739
  index.set(fingerprint2, { certificate, filename, info });
@@ -1627,13 +1755,13 @@ var init_certificate_manager = __esm({
1627
1755
  w.on("change", (changedPath) => {
1628
1756
  debugLog(chalk3.cyan(`change in folder ${folder}`), changedPath);
1629
1757
  try {
1630
- const certificate = readCertificate(changedPath);
1758
+ const certificate = readCertificateChain(changedPath)[0];
1631
1759
  const newFingerprint = makeFingerprint(certificate);
1632
1760
  const oldHash = this.#filenameToHash.get(changedPath);
1633
1761
  if (oldHash && oldHash !== newFingerprint) {
1634
1762
  index.delete(oldHash);
1635
1763
  }
1636
- index.set(newFingerprint, { certificate, filename: changedPath, info: exploreCertificateCached(certificate) });
1764
+ index.set(newFingerprint, { certificate, filename: changedPath, info: exploreCertificate(certificate) });
1637
1765
  this.#filenameToHash.set(changedPath, newFingerprint);
1638
1766
  this.emit("certificateChange", { store, certificate, fingerprint: newFingerprint, filename: changedPath });
1639
1767
  } catch (err) {