Skip to content

Commit c67c30c

Browse files
committed
tls: honour Mozilla distrust-after dates for bundled roots
Mozilla keeps emSign Root CA - G1, Izenpe.com and Firmaprofesional CIF A62634068 in its root store but distrusts TLS server certificates they issued after 2026-11-16, 2026-04-15 and 2026-08-06 respectively (CKA_NSS_SERVER_DISTRUST_AFTER). tools/mk-ca-bundle.pl does not carry that attribute into node_root_certs.h, so Node.js keeps accepting new certificates from these roots that browsers reject. Add a distrust-after table next to the SPKI blocklist in the certificate verify callback. When a client verifies a server chain through a listed key and the leaf's notBefore is after the key's cutoff, the chain fails with CERT_REVOKED; certificates issued up to the cutoff keep working. Client certificates are not affected: the attribute is Mozilla's server authentication policy, the roots stay trusted anchors otherwise, and this matches the scope of the 2016 StartCom/WoSign cutoff. Node.js has no Certificate Transparency checking, so a backdated notBefore is not detected, as with that cutoff. Fixes: #67391 Refs: https://bugzilla.mozilla.org/show_bug.cgi?id=2089115 Assisted-by: Claude, Codex, Mistral Signed-off-by: Ulises Gascon <ulisesgascongonzalez@gmail.com>
1 parent 2aef805 commit c67c30c

23 files changed

Lines changed: 867 additions & 26 deletions

‎src/crypto/crypto_context.cc‎

Lines changed: 160 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,32 +1438,137 @@ constexpr SpkiHash kBlockedSPKIs[] = {
14381438
0xfc, 0xd8, 0xdd, 0x55, 0xa8, 0x75, 0xd7, 0x20, 0xaa, 0xc2},
14391439
};
14401440

1441+
// Roots that stay in the bundle but whose trust for TLS server certificates
1442+
// is limited by issuance date, mirroring Mozilla's
1443+
// CKA_NSS_SERVER_DISTRUST_AFTER in certdata.txt. When a client verifies a
1444+
// server chain through one of these keys and the leaf certificate's
1445+
// notBefore is after the cutoff, the chain is rejected; certificates issued
1446+
// up to the cutoff keep working. Client certificates are not affected, see
1447+
// CertVerifyCallback. tools/mk-ca-bundle.pl does not carry this attribute
1448+
// into node_root_certs.h, so the dates are maintained here. The cutoff is
1449+
// the last valid second, as UTC seconds since the epoch.
1450+
//
1451+
// Node.js has no Certificate Transparency checking, so unlike Firefox it
1452+
// cannot detect a backdated notBefore. The same limitation applied to the
1453+
// StartCom/WoSign cutoff Node.js enforced in 2016.
1454+
struct DistrustAfter {
1455+
SpkiHash spki;
1456+
time_t cutoff;
1457+
};
1458+
1459+
constexpr DistrustAfter kDistrustAfter[] = {
1460+
// emSign Root CA - G1, cutoff 2026-11-16 23:59:59 UTC.
1461+
// https://bugzilla.mozilla.org/show_bug.cgi?id=2089115
1462+
// 40f6af0346a99aa1cd1d555a4e9cce62c7f9634603ee406615833dc8c8d00367
1463+
// C=IN, OU=emSign PKI, O=eMudhra Technologies Limited,
1464+
// CN=emSign Root CA - G1
1465+
{{0x37, 0x6a, 0x1a, 0x70, 0x82, 0xa5, 0x93, 0xdc, 0xcc, 0x20, 0xd5,
1466+
0x61, 0xd1, 0x19, 0xe9, 0xab, 0x8d, 0x30, 0xf1, 0x1c, 0xc3, 0x21,
1467+
0xd0, 0xa3, 0x7f, 0xa4, 0x1f, 0x0d, 0xf2, 0x84, 0xe0, 0x1c},
1468+
1794873599}, // $ date -u -d @1794873599 = 2026-11-16 23:59:59 UTC
1469+
// Izenpe.com, cutoff 2026-04-15 23:59:59 UTC.
1470+
// CKA_NSS_SERVER_DISTRUST_AFTER in tools/certdata.txt
1471+
// 2530cc8e98321502bad96f9b1fba1b099e2d299e0f4548bb914f363bc0d4531f
1472+
// C=ES, O=IZENPE S.A., CN=Izenpe.com
1473+
{{0x95, 0x2c, 0x20, 0x39, 0xc0, 0x24, 0x3e, 0xb5, 0x15, 0xdd, 0x73,
1474+
0xd8, 0x3f, 0xc3, 0x64, 0x31, 0x84, 0x87, 0x4f, 0xeb, 0x08, 0x62,
1475+
0xa9, 0x83, 0x77, 0x31, 0xed, 0x9b, 0x47, 0x42, 0xe1, 0x7a},
1476+
1776297599}, // $ date -u -d @1776297599 = 2026-04-15 23:59:59 UTC
1477+
// Autoridad de Certificacion Firmaprofesional CIF A62634068 (2014 root),
1478+
// cutoff 2026-08-06 07:01:00 UTC.
1479+
// CKA_NSS_SERVER_DISTRUST_AFTER in Mozilla's certdata.txt, newer than the
1480+
// copy in tools/certdata.txt
1481+
// 57de0583efd2b26e0361da99da9df4648def7ee8441c3b728afa9bcde0f9b26a
1482+
// C=ES, CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
1483+
{{0x3b, 0x0d, 0x73, 0xb4, 0xbe, 0x4a, 0x85, 0x4a, 0xdc, 0x3e, 0x51,
1484+
0xd7, 0xef, 0x9f, 0xa4, 0x8a, 0xef, 0xbb, 0x2c, 0xdd, 0x82, 0x4d,
1485+
0x67, 0xbd, 0xc7, 0xd7, 0xd0, 0x9a, 0x2a, 0xbc, 0x2d, 0x43},
1486+
1785999660}, // $ date -u -d @1785999660 = 2026-08-06 07:01:00 UTC
1487+
// Test fixture, not a real CA: test/fixtures/keys/distrust-after-ca-key.pem
1488+
// with a cutoff of 2019-12-31 23:59:59 UTC. Its private key is public, so
1489+
// date-limiting it costs nothing and lets
1490+
// test/parallel/test-tls-distrust-after.js exercise the real list. The
1491+
// key is pinned in the fixtures Makefile; rotating it means updating this
1492+
// entry too.
1493+
// C=US, ST=CA, L=SF, O=Joyent, OU=Node.js, CN=distrust-after-ca
1494+
{{0x76, 0xf8, 0xf8, 0x4a, 0xd8, 0x4f, 0x02, 0x4a, 0x56, 0xc7, 0x0c,
1495+
0x0f, 0xd5, 0xff, 0x21, 0x72, 0x18, 0x11, 0x3b, 0xca, 0x81, 0x44,
1496+
0x5f, 0xe9, 0x23, 0x8d, 0xc1, 0xad, 0x06, 0xb9, 0xa5, 0xd8},
1497+
1577836799}, // $ date -u -d @1577836799 = 2019-12-31 23:59:59 UTC
1498+
};
1499+
14411500
enum class SpkiCheck { kAllowed, kBlocked, kError };
14421501

1443-
// Hashes the DER-encoded SubjectPublicKeyInfo of `cert` and looks it up in
1444-
// the blocklist. A failure to encode or hash is reported as kError rather
1445-
// than kAllowed so that it cannot be used to slip past the check.
1446-
SpkiCheck CheckSPKI(X509* cert) {
1447-
if (cert == nullptr) return SpkiCheck::kAllowed;
1502+
// Hashes the DER-encoded SubjectPublicKeyInfo of `cert` into `*digest`.
1503+
// Returns false if the key could not be encoded or hashed.
1504+
bool HashSPKI(X509* cert, SpkiHash* digest) {
14481505
uint8_t* spki = nullptr;
14491506
int spki_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &spki);
1450-
if (spki_len <= 0) return SpkiCheck::kError;
1451-
SpkiHash digest;
1507+
if (spki_len <= 0) return false;
14521508
unsigned int digest_len = 0;
14531509
int ok = EVP_Digest(spki,
14541510
static_cast<size_t>(spki_len),
1455-
digest.data(),
1511+
digest->data(),
14561512
&digest_len,
14571513
Digest::SHA256,
14581514
nullptr);
14591515
OPENSSL_free(spki);
1460-
if (ok != 1 || digest_len != digest.size()) return SpkiCheck::kError;
1516+
return ok == 1 && digest_len == digest->size();
1517+
}
1518+
1519+
// Looks up the SubjectPublicKeyInfo of `cert` in the blocklist. A failure
1520+
// to encode or hash is reported as kError rather than kAllowed so that it
1521+
// cannot be used to slip past the check.
1522+
SpkiCheck CheckSPKI(X509* cert) {
1523+
if (cert == nullptr) return SpkiCheck::kAllowed;
1524+
SpkiHash digest;
1525+
if (!HashSPKI(cert, &digest)) return SpkiCheck::kError;
14611526
for (const auto& blocked : kBlockedSPKIs) {
14621527
if (digest == blocked) return SpkiCheck::kBlocked;
14631528
}
14641529
return SpkiCheck::kAllowed;
14651530
}
14661531

1532+
// Applies kDistrustAfter to `leaf` for a chain containing `cert`: kBlocked
1533+
// when `cert` is a date-limited root and `leaf` was issued after its cutoff,
1534+
// kError when the key or the date could not be read.
1535+
SpkiCheck CheckDistrustAfter(X509* cert, X509* leaf) {
1536+
if (cert == nullptr || leaf == nullptr) return SpkiCheck::kAllowed;
1537+
SpkiHash digest;
1538+
if (!HashSPKI(cert, &digest)) return SpkiCheck::kError;
1539+
for (const auto& entry : kDistrustAfter) {
1540+
if (digest != entry.spki) continue;
1541+
time_t cutoff = entry.cutoff;
1542+
// X509_cmp_time returns 1 when notBefore is later than cutoff, -1 when
1543+
// earlier or equal, and 0 when the time could not be parsed. A leaf
1544+
// issued exactly at the cutoff second is therefore still accepted.
1545+
int cmp = X509_cmp_time(X509_get0_notBefore(leaf), &cutoff);
1546+
if (cmp == 0) return SpkiCheck::kError;
1547+
if (cmp > 0) return SpkiCheck::kBlocked;
1548+
}
1549+
return SpkiCheck::kAllowed;
1550+
}
1551+
1552+
// Checks every certificate in `certs` against kDistrustAfter for `leaf`. On
1553+
// kBlocked or kError, `*offender` points at the date-limited certificate and
1554+
// `*depth` is its index in `certs`.
1555+
SpkiCheck CheckDistrustAfterChain(const STACK_OF(X509) * certs,
1556+
X509* leaf,
1557+
X509** offender,
1558+
int* depth) {
1559+
if (certs == nullptr) return SpkiCheck::kAllowed;
1560+
for (OPENSSL_SIZE_T i = 0; i < sk_X509_num(certs); i++) {
1561+
X509* cert = sk_X509_value(certs, i);
1562+
SpkiCheck result = CheckDistrustAfter(cert, leaf);
1563+
if (result != SpkiCheck::kAllowed) {
1564+
*offender = cert;
1565+
*depth = i;
1566+
return result;
1567+
}
1568+
}
1569+
return SpkiCheck::kAllowed;
1570+
}
1571+
14671572
// Checks every certificate in `certs`. On kBlocked or kError, `*offender`
14681573
// points at the certificate the result applies to and `*depth` is its index
14691574
// in `certs`.
@@ -1485,12 +1590,13 @@ SpkiCheck CheckSPKIs(const STACK_OF(X509) * certs,
14851590

14861591
// Installed with SSL_CTX_set_cert_verify_callback on every SSL_CTX Node.js
14871592
// creates. It wraps X509_verify_cert so that a chain through a blocked
1488-
// public key is reported as X509_V_ERR_CERT_REVOKED even when the trust
1489-
// store would accept it. The built chain is always checked; when
1490-
// verification did not fully succeed (TLSWrap's per-certificate callback
1491-
// continues past errors so JS can apply rejectUnauthorized, leaving the
1492-
// error set on the context), the leaf and every peer-sent certificate are
1493-
// checked too, so the blocklist takes precedence over the original error.
1593+
// public key, or through a date-limited root with a leaf issued after the
1594+
// cutoff, is reported as X509_V_ERR_CERT_REVOKED even when the trust store
1595+
// would accept it. The built chain is always checked; when verification did
1596+
// not fully succeed (TLSWrap's per-certificate callback continues past
1597+
// errors so JS can apply rejectUnauthorized, leaving the error set on the
1598+
// context), the leaf and every peer-sent certificate are checked too, so
1599+
// these checks take precedence over the original error.
14941600
//
14951601
// A hit is reported like any other X509_verify_cert error: the error and
14961602
// offending certificate are recorded on the context and the per-certificate
@@ -1531,22 +1637,54 @@ int CertVerifyCallback(X509_STORE_CTX* ctx, void*) {
15311637
int ok = X509_verify_cert(ctx) > 0 ? 1 : 0;
15321638
const bool verified = ok == 1 && X509_STORE_CTX_get_error(ctx) == X509_V_OK;
15331639

1640+
// The SSL object this verification belongs to. The library installs its
1641+
// per-certificate verify callback on the store context before calling this
1642+
// hook, and SSL_is_server tells which side of the handshake is verifying.
1643+
SSL* ssl = static_cast<SSL*>(
1644+
X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
1645+
15341646
// The depth is the offender's index in the built chain (0 = leaf), as
15351647
// X509_verify_cert would report it. For certificates found only in the
15361648
// peer-sent stack their index there is the closest equivalent.
15371649
X509* offender = nullptr;
15381650
int depth = 0;
1539-
SpkiCheck result =
1540-
CheckSPKIs(X509_STORE_CTX_get0_chain(ctx), &offender, &depth);
1651+
X509* leaf = X509_STORE_CTX_get0_cert(ctx);
1652+
const STACK_OF(X509)* chain = X509_STORE_CTX_get0_chain(ctx);
1653+
const STACK_OF(X509)* untrusted = X509_STORE_CTX_get0_untrusted(ctx);
1654+
1655+
// The SPKI blocklist applies to every verification: those keys are not
1656+
// trusted for anything.
1657+
SpkiCheck result = CheckSPKIs(chain, &offender, &depth);
15411658
if (result == SpkiCheck::kAllowed && !verified) {
1542-
X509* leaf = X509_STORE_CTX_get0_cert(ctx);
15431659
result = CheckSPKI(leaf);
15441660
if (result != SpkiCheck::kAllowed) {
15451661
offender = leaf;
15461662
depth = 0;
15471663
} else {
1548-
result =
1549-
CheckSPKIs(X509_STORE_CTX_get0_untrusted(ctx), &offender, &depth);
1664+
result = CheckSPKIs(untrusted, &offender, &depth);
1665+
}
1666+
}
1667+
1668+
// The distrust-after dates apply only when this side is a client verifying
1669+
// a server certificate. CKA_NSS_SERVER_DISTRUST_AFTER is Mozilla's TLS
1670+
// server authentication policy; Firefox does not validate client
1671+
// certificates against its root store at all, and the roots stay fully
1672+
// trusted anchors for everything else. Several of the date-limited CAs are
1673+
// large issuers of personal certificates used for client authentication,
1674+
// so a server validating client certificates against the bundled store
1675+
// keeps accepting them. This is the same scope the 2016 StartCom/WoSign
1676+
// cutoff had ("Server does not need to check the whitelist", 89217d14509).
1677+
const bool verifying_server_cert = ssl == nullptr || !SSL_is_server(ssl);
1678+
if (result == SpkiCheck::kAllowed && verifying_server_cert) {
1679+
result = CheckDistrustAfterChain(chain, leaf, &offender, &depth);
1680+
if (result == SpkiCheck::kAllowed && !verified) {
1681+
result = CheckDistrustAfter(leaf, leaf);
1682+
if (result != SpkiCheck::kAllowed) {
1683+
offender = leaf;
1684+
depth = 0;
1685+
} else {
1686+
result = CheckDistrustAfterChain(untrusted, leaf, &offender, &depth);
1687+
}
15501688
}
15511689
}
15521690
if (result == SpkiCheck::kAllowed) return ok;
@@ -1565,10 +1703,8 @@ int CertVerifyCallback(X509_STORE_CTX* ctx, void*) {
15651703
X509_STORE_CTX_set_error_depth(ctx, depth);
15661704
X509_STORE_CTX_set_current_cert(ctx, offender);
15671705
#endif
1568-
// The per-certificate callback is the one carried by the SSL object; it is
1569-
// what the library installs on the store context before calling this hook.
1570-
SSL* ssl = static_cast<SSL*>(
1571-
X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
1706+
// Hand the error to the per-certificate callback carried by the SSL object
1707+
// so that rejectUnauthorized and authorizationError behave as usual.
15721708
auto verify_cb = ssl != nullptr ? SSL_get_verify_callback(ssl) : nullptr;
15731709
if (verify_cb == nullptr) return 0;
15741710
return verify_cb(0, ctx) ? 1 : 0;

‎test/fixtures/keys/Makefile‎

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ all: \
1515
blocked-ca-cert.pem \
1616
blocked-ca-leaf-cert.pem \
1717
blocked-key-leaf-cert.pem \
18+
distrust-after-ca-cert.pem \
19+
distrust-after-pre-leaf-cert.pem \
20+
distrust-after-post-leaf-cert.pem \
21+
distrust-after-cross-cert.pem \
1822
cert-without-key-fips.pfx \
1923
agent1-cert.pem \
2024
agent1-fips.pfx \
@@ -301,6 +305,93 @@ blocked-key-leaf-cert.pem: blocked-key-leaf-csr.pem ca1-cert.pem ca1-key.pem
301305
-extfile blocked-key-leaf.cnf \
302306
-out blocked-key-leaf-cert.pem
303307

308+
#
309+
# distrust-after-ca is a self-signed root whose public key is on the
310+
# distrust-after list in src/crypto/crypto_context.cc with a cutoff of
311+
# 2019-12-31 23:59:59 UTC. distrust-after-pre-leaf is a server cert it issued
312+
# with notBefore exactly at the cutoff second (the last accepted instant);
313+
# distrust-after-post-leaf is the same with notBefore one second after it.
314+
# The leaves are issued with `openssl ca` because `openssl x509 -req` cannot
315+
# set notBefore. distrust-after-cross-cert is the same CA key cross-signed by
316+
# ca1 as an intermediate, so a chain leaf -> cross -> ca1 carries the
317+
# date-limited key without the self-signed root.
318+
#
319+
# The SHA-256 of the CA key's SubjectPublicKeyInfo is compiled into
320+
# src/crypto/crypto_context.cc, so the key is pinned: it is kept across
321+
# `make clean` and never regenerated here. Rotating it means updating the
322+
# digest and fingerprint in that file as well.
323+
distrust-after-ca-key.pem:
324+
@echo "$@ is pinned (see src/crypto/crypto_context.cc); restore it with: git checkout -- $@" >&2
325+
@exit 1
326+
327+
distrust-after-ca-cert.pem: distrust-after-ca.cnf distrust-after-ca-key.pem
328+
openssl req -new -x509 -days 99999 \
329+
-config distrust-after-ca.cnf \
330+
-key distrust-after-ca-key.pem \
331+
-out distrust-after-ca-cert.pem
332+
echo '01' > distrust-after-ca-serial
333+
touch distrust-after-ca-database.txt
334+
echo 'unique_subject = no' > distrust-after-ca-database.txt.attr
335+
mkdir -p distrust-after-ca-issued-certs
336+
337+
distrust-after-pre-leaf-key.pem:
338+
openssl genrsa -out distrust-after-pre-leaf-key.pem 2048
339+
340+
distrust-after-pre-leaf-csr.pem: distrust-after-pre-leaf.cnf distrust-after-pre-leaf-key.pem
341+
openssl req -new \
342+
-config distrust-after-pre-leaf.cnf \
343+
-key distrust-after-pre-leaf-key.pem \
344+
-out distrust-after-pre-leaf-csr.pem
345+
346+
distrust-after-pre-leaf-cert.pem: distrust-after-pre-leaf-csr.pem distrust-after-ca-cert.pem distrust-after-ca-key.pem
347+
openssl ca \
348+
-config distrust-after-ca.cnf \
349+
-keyfile distrust-after-ca-key.pem \
350+
-cert distrust-after-ca-cert.pem \
351+
-batch \
352+
-days 99999 \
353+
-in distrust-after-pre-leaf-csr.pem \
354+
-startdate 20191231235959Z \
355+
-notext -out distrust-after-pre-leaf-cert.pem
356+
357+
distrust-after-post-leaf-key.pem:
358+
openssl genrsa -out distrust-after-post-leaf-key.pem 2048
359+
360+
distrust-after-post-leaf-csr.pem: distrust-after-post-leaf.cnf distrust-after-post-leaf-key.pem
361+
openssl req -new \
362+
-config distrust-after-post-leaf.cnf \
363+
-key distrust-after-post-leaf-key.pem \
364+
-out distrust-after-post-leaf-csr.pem
365+
366+
distrust-after-post-leaf-cert.pem: distrust-after-post-leaf-csr.pem distrust-after-ca-cert.pem distrust-after-ca-key.pem
367+
openssl ca \
368+
-config distrust-after-ca.cnf \
369+
-keyfile distrust-after-ca-key.pem \
370+
-cert distrust-after-ca-cert.pem \
371+
-batch \
372+
-days 99999 \
373+
-in distrust-after-post-leaf-csr.pem \
374+
-startdate 20200101000000Z \
375+
-notext -out distrust-after-post-leaf-cert.pem
376+
377+
distrust-after-cross-csr.pem: distrust-after-cross.cnf distrust-after-ca-key.pem
378+
openssl req -new \
379+
-config distrust-after-cross.cnf \
380+
-key distrust-after-ca-key.pem \
381+
-out distrust-after-cross-csr.pem
382+
383+
distrust-after-cross-cert.pem: distrust-after-cross-csr.pem distrust-after-cross.cnf ca1-cert.pem ca1-key.pem
384+
openssl x509 -req \
385+
-extfile distrust-after-cross.cnf \
386+
-extensions v3_ca \
387+
-days 99999 \
388+
-passin "pass:password" \
389+
-in distrust-after-cross-csr.pem \
390+
-CA ca1-cert.pem \
391+
-CAkey ca1-key.pem \
392+
-CAcreateserial \
393+
-out distrust-after-cross-cert.pem
394+
304395
#
305396
# Create Subordinate Certificate Authority: ca4 issued by ca2
306397
# ('password' is used for the CA password.)
@@ -1388,7 +1479,7 @@ non-canonical-ip-dns-key.pem:
13881479
openssl ecparam -name prime256v1 -genkey -noout -out non-canonical-ip-dns-key.pem
13891480

13901481
clean:
1391-
rm -f *.pfx $(filter-out blocked-ca-key.pem,$(wildcard *.pem)) *.srl ca2-database.txt ca2-crl-agent3-database.txt* ca2-crl-agent3.cnf ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem
1482+
rm -f *.pfx $(filter-out blocked-ca-key.pem distrust-after-ca-key.pem,$(wildcard *.pem)) *.srl ca2-database.txt ca2-crl-agent3-database.txt* ca2-crl-agent3.cnf ca2-serial fake-startcom-root-serial distrust-after-ca-serial distrust-after-ca-database.txt* *.print *.old fake-startcom-root-issued-certs/*.pem distrust-after-ca-issued-certs/*.pem
13921483
@> fake-startcom-root-database.txt
13931484

13941485
test: agent1-verify agent2-verify agent3-verify agent4-verify agent5-verify agent6-verify agent7-verify agent8-verify agent10-verify ec10-verify

‎test/fixtures/keys/ca1-cert.srl‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
147D36C1C2F74206DE9FAB5F2226D78ADB00A42C
1+
147D36C1C2F74206DE9FAB5F2226D78ADB00A42E
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIID6TCCAtGgAwIBAgIUZTthWINR9jw2kSHogvUpSk1mjD4wDQYJKoZIhvcNAQEL
3+
BQAwgZQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzAN
4+
BgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczEaMBgGA1UEAwwRZGlzdHJ1
5+
c3QtYWZ0ZXItY2ExLDAqBgkqhkiG9w0BCQEWHWRpc3RydXN0LWFmdGVyLWNhQGV4
6+
YW1wbGUub3JnMCAXDTI2MTExMDIxMjQyNFoYDzIzMDAwODI1MjEyNDI0WjCBlDEL
7+
MAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0GA1UECgwG
8+
Sm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMRowGAYDVQQDDBFkaXN0cnVzdC1hZnRl
9+
ci1jYTEsMCoGCSqGSIb3DQEJARYdZGlzdHJ1c3QtYWZ0ZXItY2FAZXhhbXBsZS5v
10+
cmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfF9UotYQq2U7A5ZX3
11+
4AgyJlq7A0eMkZOX+crwv523bYzny3dd49cqv7i+u66I17+CsJ5To+Oqqo23PZgT
12+
GYBwQHIxktVRYidR184WsaT0m/xZgRqbAWP69T02fsslUc6/xnN20EICWQX0OSLE
13+
kBLZVHJmM5bs/foCA3v/EW/d0eGFFzQctFW0qJ5wcTpSdp1sqD04rOy7vsCFxqqS
14+
Zgs5qK2Je0GeAKTRjRVCz99Z3O3hY9sEL5yqpqx1sAhIGEIUMWsk9SLhLstXZ/03
15+
xJjbGmm9BOIbO3bBQMNoi5Ef2fPPrxbX/r0X4SC4tQfgx2d0rMfQx77AlC+RfWa2
16+
Tkt1AgMBAAGjLzAtMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFDvOXAcrAS908Iti
17+
MOuZwudinzSbMA0GCSqGSIb3DQEBCwUAA4IBAQAS1DgvZYd+97lEkGUI2a/40sdD
18+
mu3ZCU5je0qeEFX2Gr5RJ3lclvNoWYLTJGts/cq69b3xRKjsBdDym7Sk6MrvcLuS
19+
Bppk5Gzd4MonzoLzCV+B4/GGu/iwjunaYAXZMw3voC/Ibl4JV1RvWL58PoyOqkY6
20+
3K5PGHfkh1lESpvxmmcdtRc/XGawIqBJTZS8lhnk+M91jg4TvRdHX/vkYvJEFozz
21+
YBgIEp6YjMZMN4QGGnt4iXa+9zVZZnniAxPoeavy7OU4KRttv7I9oimfifcXDeFE
22+
DTFCQhGZZbLMo97HVF8rh6TvX9exCbVSyJzeUYvkJvlgFRRWNeJdnySYO1Xi
23+
-----END CERTIFICATE-----
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
V 23000825212424Z 01 unknown /C=US/ST=CA/L=SF/O=Joyent/OU=Node.js/CN=localhost
2+
V 23000825212424Z 02 unknown /C=US/ST=CA/L=SF/O=Joyent/OU=Node.js/CN=localhost
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unique_subject = no

0 commit comments

Comments
 (0)