diff --git a/net/cert/cert_verify_proc_nss.cc b/net/cert/cert_verify_proc_nss.cc index ca8e8116f0ef52..cad20b9fa95fcc 100644 --- a/net/cert/cert_verify_proc_nss.cc +++ b/net/cert/cert_verify_proc_nss.cc @@ -279,7 +279,7 @@ enum CRLSetResult { // that some EV sites would otherwise take the hit of an OCSP lookup for // no reason. // kCRLSetOk: otherwise. -CRLSetResult CheckRevocationWithCRLSet(CERTCertList* cert_list, +CRLSetResult CheckRevocationWithCRLSet(const CERTCertList* cert_list, CERTCertificate* root, CRLSet* crl_set) { std::vector certs; @@ -352,6 +352,50 @@ CRLSetResult CheckRevocationWithCRLSet(CERTCertList* cert_list, return kCRLSetOk; } +// Arguments for CheckChainRevocationWithCRLSet that are curried within the +// CERTChainVerifyCallback's isChainValidArg. +struct CheckChainRevocationArgs { + // The CRLSet to evaluate against. + CRLSet* crl_set = nullptr; + + // The next callback to invoke, if the CRLSet does not report any errors. + CERTChainVerifyCallback* next_callback = nullptr; + + // Indicates that the application callback failure was due to a CRLSet + // revocation, rather than due to |next_callback| rejecting it. This is + // used to map the error back to the proper caller-visible error code. + bool was_revoked = false; +}; + +SECStatus CheckChainRevocationWithCRLSet(void* is_chain_valid_arg, + const CERTCertList* current_chain, + PRBool* chain_ok) { + CHECK(is_chain_valid_arg); + + CheckChainRevocationArgs* args = + static_cast(is_chain_valid_arg); + + CRLSetResult crlset_result = kCRLSetUnknown; + if (args->crl_set) { + crlset_result = + CheckRevocationWithCRLSet(current_chain, nullptr, args->crl_set); + } + + if (crlset_result == kCRLSetRevoked) { + args->was_revoked = true; + *chain_ok = PR_FALSE; + return SECSuccess; + } + args->was_revoked = false; + + *chain_ok = PR_TRUE; + if (!args->next_callback || !args->next_callback->isChainValid) + return SECSuccess; + + return (*args->next_callback->isChainValid)( + args->next_callback->isChainValidArg, current_chain, chain_ok); +} + // Forward declarations. SECStatus RetryPKIXVerifyCertWithWorkarounds( CERTCertificate* cert_handle, int num_policy_oids, @@ -825,6 +869,22 @@ int CertVerifyProcNSS::VerifyInternalImpl( verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; } + // Setup a callback to call into CheckChainRevocationWithCRLSet with the + // current CRLSet. If the CRLSet revokes a given chain, |was_revoked| + // will be set to true. + // The same callback and args are used for every invocation of + // PKIXVerifyCert, as CheckChainRevocationWithCRLSet handles resetting + // |was_revoked| as necessary. + CheckChainRevocationArgs check_chain_revocation_args; + check_chain_revocation_args.crl_set = crl_set; + check_chain_revocation_args.next_callback = chain_verify_callback; + + CERTChainVerifyCallback crlset_callback; + memset(&crlset_callback, 0, sizeof(crlset_callback)); + crlset_callback.isChainValid = &CheckChainRevocationWithCRLSet; + crlset_callback.isChainValidArg = + static_cast(&check_chain_revocation_args); + // Make sure that the cert is valid now. SECCertTimeValidity validity = CERT_CheckCertValidTimes( cert_handle, PR_Now(), PR_TRUE); @@ -862,15 +922,9 @@ int CertVerifyProcNSS::VerifyInternalImpl( CertificateListToCERTCertList(additional_trust_anchors)); } - SECStatus status = PKIXVerifyCert(cert_handle, - check_revocation, - false, - cert_io_enabled, - NULL, - 0, - trust_anchors.get(), - chain_verify_callback, - cvout); + SECStatus status = + PKIXVerifyCert(cert_handle, check_revocation, false, cert_io_enabled, + NULL, 0, trust_anchors.get(), &crlset_callback, cvout); if (status == SECSuccess && (flags & CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) && @@ -880,15 +934,8 @@ int CertVerifyProcNSS::VerifyInternalImpl( // NSS tests for that feature. scoped_cvout.Clear(); verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; - status = PKIXVerifyCert(cert_handle, - true, - true, - cert_io_enabled, - NULL, - 0, - trust_anchors.get(), - chain_verify_callback, - cvout); + status = PKIXVerifyCert(cert_handle, true, true, cert_io_enabled, NULL, 0, + trust_anchors.get(), &crlset_callback, cvout); } if (status == SECSuccess) { @@ -910,13 +957,25 @@ int CertVerifyProcNSS::VerifyInternalImpl( CRLSetResult crl_set_result = kCRLSetUnknown; if (crl_set) { - crl_set_result = CheckRevocationWithCRLSet( - cvout[cvout_cert_list_index].value.pointer.chain, - cvout[cvout_trust_anchor_index].value.pointer.cert, - crl_set); - if (crl_set_result == kCRLSetRevoked) { + if (status == SECSuccess) { + // Reverify the returned chain; NSS should have already called + // CheckChainRevocationWithCRLSet prior to returning, but given the + // edge cases (self-signed certs that are trusted; cached chains; + // unreadable code), this is more about defense in depth than + // functional necessity. + crl_set_result = CheckRevocationWithCRLSet( + cvout[cvout_cert_list_index].value.pointer.chain, + cvout[cvout_trust_anchor_index].value.pointer.cert, crl_set); + if (crl_set_result == kCRLSetRevoked) { + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + status = SECFailure; + } + } else if (PORT_GetError() == SEC_ERROR_APPLICATION_CALLBACK_ERROR && + check_chain_revocation_args.was_revoked) { + // If a CRLSet was supplied, and the error was an application callback + // error, then it was directed through the CRLSet code and that + // particular chain was revoked. PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); - status = SECFailure; } } @@ -949,14 +1008,8 @@ int CertVerifyProcNSS::VerifyInternalImpl( if (check_revocation) verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; - if (VerifyEV(cert_handle, - flags, - crl_set, - check_revocation, - metadata, - ev_policy_oid, - trust_anchors.get(), - chain_verify_callback)) { + if (VerifyEV(cert_handle, flags, crl_set, check_revocation, metadata, + ev_policy_oid, trust_anchors.get(), &crlset_callback)) { verify_result->cert_status |= CERT_STATUS_IS_EV; } } diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc index be9868d3b36955..be7f4d1b3b3250 100644 --- a/net/cert/cert_verify_proc_unittest.cc +++ b/net/cert/cert_verify_proc_unittest.cc @@ -143,7 +143,7 @@ class CertVerifyProcTest : public testing::Test { // into all platforms, this is a temporary, test-only flag to centralize the // conditionals in tests. bool SupportsCRLSetsInPathBuilding() { -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(USE_NSS_CERTS) return true; #else return false; @@ -1279,210 +1279,117 @@ TEST_F(CertVerifyProcTest, CRLSetLeafSerial) { EXPECT_EQ(ERR_CERT_REVOKED, error); } -// Tests that revocation by CRLSet functions properly with the certificate -// immediately before the trust anchor is revoked by that trust anchor, but -// another version to a different trust anchor exists. +// Tests that CRLSets participate in path building functions, and that as +// long as a valid path exists within the verification graph, verification +// succeeds. // -// The two possible paths are: +// In this test, there are two roots (D and E), and three possible paths +// to validate a leaf (A): // 1. A(B) -> B(C) -> C(D) -> D(D) // 2. A(B) -> B(C) -> C(E) -> E(E) +// 3. A(B) -> B(F) -> F(E) -> E(E) // -// In this test, C(E) is revoked by CRLSet. It is configured to be the more -// preferable version compared to C(D), once revoked, it should be ignored. -TEST_F(CertVerifyProcTest, CRLSetRevokedIntermediateSameName) { +// Each permutation of revocation is tried: +// 1. Revoking E by SPKI, so that only Path 1 is valid (as E is in Paths 2 & 3) +// 2. Revoking C(D) and F(E) by serial, so that only Path 2 is valid. +// 3. Revoking C by SPKI, so that only Path 3 is valid (as C is in Paths 1 & 2) +TEST_F(CertVerifyProcTest, CRLSetDuringPathBuilding) { if (!SupportsCRLSetsInPathBuilding()) { LOG(INFO) << "Skipping this test on this platform."; return; } - const char* const kCertificatesToLoad[] = { + const char* const kPath1Files[] = { "multi-root-A-by-B.pem", "multi-root-B-by-C.pem", "multi-root-C-by-D.pem", - "multi-root-D-by-D.pem", "multi-root-C-by-E.pem", "multi-root-E-by-E.pem", - }; - CertificateList certs; - ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kCertificatesToLoad, &certs)); - - // Add D and E as trust anchors - ScopedTestRoot test_root_D(certs[3].get()); // D-by-D - ScopedTestRoot test_root_F(certs[5].get()); // E-by-E - - // Create a chain that sends A(B), B(C), C(E), C(D). The reason that - // both C(E) and C(D) are sent are to ensure both certificates are available - // for path building. The test - // CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts ensures this is - // safe to do. + "multi-root-D-by-D.pem"}; + const char* const kPath2Files[] = { + "multi-root-A-by-B.pem", "multi-root-B-by-C.pem", "multi-root-C-by-E.pem", + "multi-root-E-by-E.pem"}; + const char* const kPath3Files[] = { + "multi-root-A-by-B.pem", "multi-root-B-by-F.pem", "multi-root-F-by-E.pem", + "multi-root-E-by-E.pem"}; + + CertificateList path_1_certs; + ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kPath1Files, &path_1_certs)); + + CertificateList path_2_certs; + ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kPath2Files, &path_2_certs)); + + CertificateList path_3_certs; + ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kPath3Files, &path_3_certs)); + + // Add D and E as trust anchors. + ScopedTestRoot test_root_D(path_1_certs[3].get()); // D-by-D + ScopedTestRoot test_root_E(path_2_certs[3].get()); // E-by-E + + // Create a chain that contains all the certificate paths possible. + // CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts already + // ensures that it's safe to send additional certificates as inputs, and + // that they're ignored if not necessary. + // This is to avoid relying on AIA or internal object caches when + // interacting with the underlying library. X509Certificate::OSCertHandles intermediates; - intermediates.push_back(certs[1]->os_cert_handle()); // B-by-C - intermediates.push_back(certs[4]->os_cert_handle()); // C-by-E - intermediates.push_back(certs[2]->os_cert_handle()); // C-by-D + intermediates.push_back(path_1_certs[1]->os_cert_handle()); // B-by-C + intermediates.push_back(path_1_certs[2]->os_cert_handle()); // C-by-D + intermediates.push_back(path_2_certs[2]->os_cert_handle()); // C-by-E + intermediates.push_back(path_3_certs[1]->os_cert_handle()); // B-by-F + intermediates.push_back(path_3_certs[2]->os_cert_handle()); // F-by-E scoped_refptr cert = X509Certificate::CreateFromHandle( - certs[0]->os_cert_handle(), intermediates); + path_1_certs[0]->os_cert_handle(), intermediates); ASSERT_TRUE(cert); - // Sanity check: Ensure that, without any revocation status, the to-be-revoked - // path is preferred. - int flags = 0; - CertVerifyResult verify_result; - int error = Verify(cert.get(), "127.0.0.1", flags, nullptr, empty_cert_list_, - &verify_result); - ASSERT_EQ(OK, error); - ASSERT_EQ(0U, verify_result.cert_status); - ASSERT_TRUE(verify_result.verified_cert.get()); + struct TestPermutations { + const char* crlset; + bool expect_valid; + scoped_refptr expected_intermediate; + } kTests[] = { + {"multi-root-crlset-D-and-E.raw", false, nullptr}, + {"multi-root-crlset-E.raw", true, path_1_certs[2].get()}, + {"multi-root-crlset-CD-and-FE.raw", true, path_2_certs[2].get()}, + {"multi-root-crlset-C.raw", true, path_3_certs[2].get()}, + {"multi-root-crlset-unrelated.raw", true, nullptr}}; + + for (const auto& testcase : kTests) { + SCOPED_TRACE(testcase.crlset); + scoped_refptr crl_set; + std::string crl_set_bytes; + EXPECT_TRUE(base::ReadFileToString( + GetTestCertsDirectory().AppendASCII(testcase.crlset), &crl_set_bytes)); + ASSERT_TRUE(CRLSetStorage::Parse(crl_set_bytes, &crl_set)); + + int flags = 0; + CertVerifyResult verify_result; + int error = Verify(cert.get(), "127.0.0.1", flags, crl_set.get(), + empty_cert_list_, &verify_result); + + if (!testcase.expect_valid) { + EXPECT_NE(OK, error); + EXPECT_NE(0U, verify_result.cert_status); + continue; + } - // The expected path is A(B) -> B(C) -> C(E) -> E(E). - const X509Certificate::OSCertHandles& verified_intermediates = - verify_result.verified_cert->GetIntermediateCertificates(); - ASSERT_EQ(3U, verified_intermediates.size()); - scoped_refptr verified_root = - X509Certificate::CreateFromHandle(verified_intermediates[2], - X509Certificate::OSCertHandles()); - ASSERT_TRUE(verified_root.get()); - EXPECT_EQ("E Root CA", verified_root->subject().common_name); - - // Load a CRLSet that blocks C-by-E. - scoped_refptr crl_set; - std::string crl_set_bytes; - EXPECT_TRUE(base::ReadFileToString( - GetTestCertsDirectory().AppendASCII("multi-root-crlset-C-by-E.raw"), - &crl_set_bytes)); - ASSERT_TRUE(CRLSetStorage::Parse(crl_set_bytes, &crl_set)); + ASSERT_EQ(OK, error); + ASSERT_EQ(0U, verify_result.cert_status); + ASSERT_TRUE(verify_result.verified_cert.get()); - // Verify with the CRLSet. Because C-by-E is revoked, the expected path is - // A(B) -> B(C) -> C(D) -> D(D). - error = Verify(cert.get(), "127.0.0.1", flags, crl_set.get(), - empty_cert_list_, &verify_result); - ASSERT_EQ(OK, error); - ASSERT_EQ(0U, verify_result.cert_status); - ASSERT_TRUE(verify_result.verified_cert.get()); + if (!testcase.expected_intermediate) + continue; - const X509Certificate::OSCertHandles& new_verified_intermediates = - verify_result.verified_cert->GetIntermediateCertificates(); - ASSERT_EQ(3U, new_verified_intermediates.size()); - verified_root = X509Certificate::CreateFromHandle( - new_verified_intermediates[2], X509Certificate::OSCertHandles()); - ASSERT_TRUE(verified_root.get()); - EXPECT_EQ("D Root CA", verified_root->subject().common_name); - - // Reverify without the CRLSet, to ensure that CRLSets do not persist between - // separate calls. As in the first verification, the expected path is - // A(B) -> B(C) -> C(E) -> E(E). - error = Verify(cert.get(), "127.0.0.1", flags, nullptr, empty_cert_list_, - &verify_result); - ASSERT_EQ(OK, error); - ASSERT_EQ(0U, verify_result.cert_status); - ASSERT_TRUE(verify_result.verified_cert.get()); + const X509Certificate::OSCertHandles& verified_intermediates = + verify_result.verified_cert->GetIntermediateCertificates(); + ASSERT_EQ(3U, verified_intermediates.size()); - const X509Certificate::OSCertHandles& final_verified_intermediates = - verify_result.verified_cert->GetIntermediateCertificates(); - ASSERT_EQ(3U, final_verified_intermediates.size()); - verified_root = X509Certificate::CreateFromHandle( - final_verified_intermediates[2], X509Certificate::OSCertHandles()); - ASSERT_TRUE(verified_root.get()); - EXPECT_EQ("E Root CA", verified_root->subject().common_name); -} + scoped_refptr intermediate = + X509Certificate::CreateFromHandle(verified_intermediates[1], + X509Certificate::OSCertHandles()); + ASSERT_TRUE(intermediate); -// Tests that revocation by CRLSet functions properly when an intermediate is -// revoked by SPKI. In this case, path building should ignore all certificates -// with that SPKI, and search for alternatively keyed versions. -// -// The two possible paths are: -// 1. A(B) -> B(C) -> C(D) -> D(D) -// 2. A(B) -> B(F) -> F(E) -> E(E) -// -// The path building algorithm needs to explore B(C) once it discovers that -// F(E) is revoked, and that there are no valid paths with B(F). -TEST_F(CertVerifyProcTest, CRLSetRevokedIntermediateCrossIntermediates) { - if (!SupportsCRLSetsInPathBuilding()) { - LOG(INFO) << "Skipping this test on this platform."; - return; + EXPECT_TRUE(testcase.expected_intermediate->Equals(intermediate.get())) + << "Expected: " << testcase.expected_intermediate->subject().common_name + << " issued by " << testcase.expected_intermediate->issuer().common_name + << "; Got: " << intermediate->subject().common_name << " issued by " + << intermediate->issuer().common_name; } - - const char* const kCertificatesToLoad[] = { - "multi-root-A-by-B.pem", "multi-root-B-by-C.pem", "multi-root-C-by-D.pem", - "multi-root-D-by-D.pem", "multi-root-B-by-F.pem", "multi-root-F-by-E.pem", - "multi-root-E-by-E.pem", - }; - CertificateList certs; - ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kCertificatesToLoad, &certs)); - - // Add D and E as trust anchors - ScopedTestRoot test_root_D(certs[3].get()); // D-by-D - ScopedTestRoot test_root_F(certs[6].get()); // E-by-E - - // Create a chain that sends A(B), B(F), F(E), B(C), C(D). The reason that - // both B(C) and C(D) are sent are to ensure both certificates are available - // for path building. The test - // CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts ensures this is - // safe to do. - X509Certificate::OSCertHandles intermediates; - intermediates.push_back(certs[4]->os_cert_handle()); // B-by-F - intermediates.push_back(certs[5]->os_cert_handle()); // F-by-E - intermediates.push_back(certs[1]->os_cert_handle()); // B-by-C - intermediates.push_back(certs[2]->os_cert_handle()); // C-by-D - scoped_refptr cert = X509Certificate::CreateFromHandle( - certs[0]->os_cert_handle(), intermediates); - ASSERT_TRUE(cert); - - // Sanity check: Ensure that, without any revocation status, the to-be-revoked - // path is preferred. - int flags = 0; - CertVerifyResult verify_result; - int error = Verify(cert.get(), "127.0.0.1", flags, nullptr, empty_cert_list_, - &verify_result); - ASSERT_EQ(OK, error); - ASSERT_EQ(0U, verify_result.cert_status); - ASSERT_TRUE(verify_result.verified_cert.get()); - - // The expected path is A(B) -> B(F) -> F(E) -> E(E). - const X509Certificate::OSCertHandles& verified_intermediates = - verify_result.verified_cert->GetIntermediateCertificates(); - ASSERT_EQ(3U, verified_intermediates.size()); - scoped_refptr verified_root = - X509Certificate::CreateFromHandle(verified_intermediates[2], - X509Certificate::OSCertHandles()); - ASSERT_TRUE(verified_root.get()); - EXPECT_EQ("E Root CA", verified_root->subject().common_name); - - // Load a CRLSet that blocks F. - scoped_refptr crl_set; - std::string crl_set_bytes; - EXPECT_TRUE(base::ReadFileToString( - GetTestCertsDirectory().AppendASCII("multi-root-crlset-F.raw"), - &crl_set_bytes)); - ASSERT_TRUE(CRLSetStorage::Parse(crl_set_bytes, &crl_set)); - - // Verify with the CRLSet. Because F is revoked, the expected path is - // A(B) -> B(C) -> C(D) -> D(D). - error = Verify(cert.get(), "127.0.0.1", flags, crl_set.get(), - empty_cert_list_, &verify_result); - ASSERT_EQ(OK, error); - ASSERT_EQ(0U, verify_result.cert_status); - ASSERT_TRUE(verify_result.verified_cert.get()); - - const X509Certificate::OSCertHandles& new_verified_intermediates = - verify_result.verified_cert->GetIntermediateCertificates(); - ASSERT_EQ(3U, new_verified_intermediates.size()); - verified_root = X509Certificate::CreateFromHandle( - new_verified_intermediates[2], X509Certificate::OSCertHandles()); - ASSERT_TRUE(verified_root.get()); - EXPECT_EQ("D Root CA", verified_root->subject().common_name); - - // Reverify without the CRLSet, to ensure that CRLSets do not persist between - // separate calls. As in the first verification, the expected path is - // A(B) -> B(F) -> F(E) -> E(E). - error = Verify(cert.get(), "127.0.0.1", flags, nullptr, empty_cert_list_, - &verify_result); - ASSERT_EQ(OK, error); - ASSERT_EQ(0U, verify_result.cert_status); - ASSERT_TRUE(verify_result.verified_cert.get()); - - const X509Certificate::OSCertHandles& final_verified_intermediates = - verify_result.verified_cert->GetIntermediateCertificates(); - ASSERT_EQ(3U, final_verified_intermediates.size()); - verified_root = X509Certificate::CreateFromHandle( - final_verified_intermediates[2], X509Certificate::OSCertHandles()); - ASSERT_TRUE(verified_root.get()); - EXPECT_EQ("E Root CA", verified_root->subject().common_name); } #endif diff --git a/net/data/ssl/certificates/multi-root-crlset-F.raw b/net/data/ssl/certificates/multi-root-crlset-C.raw similarity index 58% rename from net/data/ssl/certificates/multi-root-crlset-F.raw rename to net/data/ssl/certificates/multi-root-crlset-C.raw index bad62ec3902a23..fbc60c022ec3ac 100644 Binary files a/net/data/ssl/certificates/multi-root-crlset-F.raw and b/net/data/ssl/certificates/multi-root-crlset-C.raw differ diff --git a/net/data/ssl/certificates/multi-root-crlset-CD-and-FE.raw b/net/data/ssl/certificates/multi-root-crlset-CD-and-FE.raw new file mode 100644 index 00000000000000..1debeea047255c Binary files /dev/null and b/net/data/ssl/certificates/multi-root-crlset-CD-and-FE.raw differ diff --git a/net/data/ssl/certificates/multi-root-crlset-D-and-E.raw b/net/data/ssl/certificates/multi-root-crlset-D-and-E.raw new file mode 100644 index 00000000000000..6a76f266666407 Binary files /dev/null and b/net/data/ssl/certificates/multi-root-crlset-D-and-E.raw differ diff --git a/net/data/ssl/certificates/multi-root-crlset-E.raw b/net/data/ssl/certificates/multi-root-crlset-E.raw new file mode 100644 index 00000000000000..2a3d27dac16535 Binary files /dev/null and b/net/data/ssl/certificates/multi-root-crlset-E.raw differ diff --git a/net/data/ssl/certificates/multi-root-crlset-C-by-E.raw b/net/data/ssl/certificates/multi-root-crlset-unrelated.raw similarity index 88% rename from net/data/ssl/certificates/multi-root-crlset-C-by-E.raw rename to net/data/ssl/certificates/multi-root-crlset-unrelated.raw index b13bdd875cf546..82293338460fa0 100644 Binary files a/net/data/ssl/certificates/multi-root-crlset-C-by-E.raw and b/net/data/ssl/certificates/multi-root-crlset-unrelated.raw differ diff --git a/net/data/ssl/scripts/generate-multi-root-test-chains.sh b/net/data/ssl/scripts/generate-multi-root-test-chains.sh index 5de08df84cdb4b..8f1b4e470b6df6 100755 --- a/net/data/ssl/scripts/generate-multi-root-test-chains.sh +++ b/net/data/ssl/scripts/generate-multi-root-test-chains.sh @@ -208,20 +208,53 @@ cp out/D.pem ../certificates/multi-root-D-by-D.pem cp out/E.pem ../certificates/multi-root-E-by-E.pem echo "Generating CRLSets" -# Block C-by-E (serial number 0x1001) by way of serial number. -python crlsetutil.py -o ../certificates/multi-root-crlset-C-by-E.raw \ -<