summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-08 04:00:39 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-08 04:00:39 +0000
commitf46f6d55efc7b8411220d920ceb25bb524780dc0 (patch)
tree140a5c2d8611f64219b4c9da61d368c81929cd34
parent4809dd982b91421df06da2ce2a20409e7c521366 (diff)
downloadchromium_src-f46f6d55efc7b8411220d920ceb25bb524780dc0.zip
chromium_src-f46f6d55efc7b8411220d920ceb25bb524780dc0.tar.gz
chromium_src-f46f6d55efc7b8411220d920ceb25bb524780dc0.tar.bz2
NSS Cros multiprofile: trust roots added by a profile shouldn't apply to other profiles.
BUG=218627 Review URL: https://codereview.chromium.org/137553004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249928 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/net/cert_verify_proc_chromeos.cc103
-rw-r--r--chrome/browser/chromeos/net/cert_verify_proc_chromeos.h59
-rw-r--r--chrome/browser/chromeos/net/cert_verify_proc_chromeos_unittest.cc373
-rw-r--r--chrome/browser/chromeos/policy/policy_cert_verifier.cc7
-rw-r--r--chrome/browser/chromeos/policy/policy_cert_verifier.h4
-rw-r--r--chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc6
-rw-r--r--chrome/browser/io_thread.cc15
-rw-r--r--chrome/browser/profiles/profile_io_data.cc14
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--net/cert/cert_verify_proc_nss.cc63
-rw-r--r--net/cert/cert_verify_proc_nss.h13
-rw-r--r--net/cert/cert_verify_proc_openssl.cc10
-rw-r--r--net/cert/nss_profile_filter_chromeos.cc20
-rw-r--r--net/cert/nss_profile_filter_chromeos.h16
-rw-r--r--net/cert/nss_profile_filter_chromeos_unittest.cc40
-rw-r--r--net/cert/test_root_certs.h13
-rw-r--r--net/cert/test_root_certs_nss.cc11
-rw-r--r--net/cert/test_root_certs_openssl.cc10
-rw-r--r--net/cert/test_root_certs_unittest.cc33
-rw-r--r--net/data/ssl/certificates/README7
-rw-r--r--net/data/ssl/certificates/multi-root-chain1.pem328
-rw-r--r--net/data/ssl/certificates/multi-root-chain2.pem328
-rwxr-xr-xnet/data/ssl/scripts/generate-multi-root-test-chains.sh161
-rw-r--r--net/ssl/client_cert_store_chromeos_unittest.cc4
25 files changed, 1578 insertions, 63 deletions
diff --git a/chrome/browser/chromeos/net/cert_verify_proc_chromeos.cc b/chrome/browser/chromeos/net/cert_verify_proc_chromeos.cc
new file mode 100644
index 0000000..b99d98c
--- /dev/null
+++ b/chrome/browser/chromeos/net/cert_verify_proc_chromeos.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/net/cert_verify_proc_chromeos.h"
+
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+
+// NSS doesn't currently define CERT_LIST_TAIL.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=962413
+// Can be removed once chrome requires NSS version 3.16 to build.
+#ifndef CERT_LIST_TAIL
+#define CERT_LIST_TAIL(l) ((CERTCertListNode *)PR_LIST_TAIL(&l->list))
+#endif
+
+namespace chromeos {
+
+namespace {
+
+struct ChainVerifyArgs {
+ CertVerifyProcChromeOS* cert_verify_proc;
+ const net::CertificateList& additional_trust_anchors;
+};
+
+} // namespace
+
+CertVerifyProcChromeOS::CertVerifyProcChromeOS() {}
+
+CertVerifyProcChromeOS::CertVerifyProcChromeOS(
+ crypto::ScopedPK11Slot public_slot) {
+ profile_filter_.Init(public_slot.Pass(), crypto::ScopedPK11Slot());
+}
+
+CertVerifyProcChromeOS::~CertVerifyProcChromeOS() {}
+
+int CertVerifyProcChromeOS::VerifyInternal(
+ net::X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ net::CRLSet* crl_set,
+ const net::CertificateList& additional_trust_anchors,
+ net::CertVerifyResult* verify_result) {
+ ChainVerifyArgs chain_verify_args = {this, additional_trust_anchors};
+
+ CERTChainVerifyCallback chain_verify_callback;
+ chain_verify_callback.isChainValid =
+ &CertVerifyProcChromeOS::IsChainValidFunc;
+ chain_verify_callback.isChainValidArg =
+ static_cast<void*>(&chain_verify_args);
+
+ return VerifyInternalImpl(cert,
+ hostname,
+ flags,
+ crl_set,
+ additional_trust_anchors,
+ &chain_verify_callback,
+ verify_result);
+}
+
+// static
+SECStatus CertVerifyProcChromeOS::IsChainValidFunc(
+ void* is_chain_valid_arg,
+ const CERTCertList* current_chain,
+ PRBool* chain_ok) {
+ ChainVerifyArgs* args = static_cast<ChainVerifyArgs*>(is_chain_valid_arg);
+ CERTCertificate* cert = CERT_LIST_TAIL(current_chain)->cert;
+
+ if (net::TestRootCerts::HasInstance()) {
+ if (net::TestRootCerts::GetInstance()->Contains(cert)) {
+ // Certs in the TestRootCerts are not stored in any slot, and thus would
+ // not be allowed by the profile_filter. This should only be hit in tests.
+ DVLOG(3) << cert->subjectName << " is a TestRootCert";
+ *chain_ok = PR_TRUE;
+ return SECSuccess;
+ }
+ }
+
+ for (net::CertificateList::const_iterator i =
+ args->additional_trust_anchors.begin();
+ i != args->additional_trust_anchors.end();
+ ++i) {
+ if (net::X509Certificate::IsSameOSCert(cert, (*i)->os_cert_handle())) {
+ // Certs in the additional_trust_anchors should always be allowed, even if
+ // they aren't stored in a slot that would be allowed by the
+ // profile_filter.
+ DVLOG(3) << cert->subjectName << " is an additional_trust_anchor";
+ *chain_ok = PR_TRUE;
+ return SECSuccess;
+ }
+ }
+
+ // TODO(mattm): If crbug.com/334384 is fixed to allow setting trust
+ // properly when the same cert is in multiple slots, this would also need
+ // updating to check the per-slot trust values.
+ *chain_ok = args->cert_verify_proc->profile_filter_.IsCertAllowed(cert)
+ ? PR_TRUE
+ : PR_FALSE;
+ DVLOG(3) << cert->subjectName << " is " << (*chain_ok ? "ok" : "not ok");
+ return SECSuccess;
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/net/cert_verify_proc_chromeos.h b/chrome/browser/chromeos/net/cert_verify_proc_chromeos.h
new file mode 100644
index 0000000..a715e45
--- /dev/null
+++ b/chrome/browser/chromeos/net/cert_verify_proc_chromeos.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_NET_CERT_VERIFY_PROC_CHROMEOS_H_
+#define CHROME_BROWSER_CHROMEOS_NET_CERT_VERIFY_PROC_CHROMEOS_H_
+
+#include "crypto/scoped_nss_types.h"
+#include "net/cert/cert_verify_proc_nss.h"
+#include "net/cert/nss_profile_filter_chromeos.h"
+
+namespace chromeos {
+
+// Wrapper around CertVerifyProcNSS which allows filtering trust decisions on a
+// per-slot basis.
+//
+// Note that only the simple case is currently handled (if a slot contains a new
+// trust root, that root should not be trusted by CertVerifyProcChromeOS
+// instances using other slots). More complicated cases are not handled (like
+// two slots adding the same root cert but with different trust values).
+class CertVerifyProcChromeOS : public net::CertVerifyProcNSS {
+ public:
+ // Creates a CertVerifyProc that doesn't allow any user-provided trust roots.
+ CertVerifyProcChromeOS();
+
+ // Creates a CertVerifyProc that doesn't allow trust roots provided by
+ // users other than the specified slot.
+ explicit CertVerifyProcChromeOS(crypto::ScopedPK11Slot public_slot);
+
+ protected:
+ virtual ~CertVerifyProcChromeOS();
+
+ private:
+ // net::CertVerifyProcNSS implementation:
+ virtual int VerifyInternal(
+ net::X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ net::CRLSet* crl_set,
+ const net::CertificateList& additional_trust_anchors,
+ net::CertVerifyResult* verify_result) OVERRIDE;
+
+ // Check if the trust root of |current_chain| is allowed.
+ // |is_chain_valid_arg| is actually a ChainVerifyArgs*, which is used to pass
+ // state through the NSS CERTChainVerifyCallback.isChainValidArg parameter.
+ // If the chain is allowed, |*chain_ok| will be set to PR_TRUE.
+ // If the chain is not allowed, |*chain_ok| is set to PR_FALSE, and this
+ // function may be called again during a single certificate verification if
+ // there are multiple possible valid chains.
+ static SECStatus IsChainValidFunc(void* is_chain_valid_arg,
+ const CERTCertList* current_chain,
+ PRBool* chain_ok);
+
+ net::NSSProfileFilterChromeOS profile_filter_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_NET_CERT_VERIFY_PROC_CHROMEOS_H_
diff --git a/chrome/browser/chromeos/net/cert_verify_proc_chromeos_unittest.cc b/chrome/browser/chromeos/net/cert_verify_proc_chromeos_unittest.cc
new file mode 100644
index 0000000..0c485a5
--- /dev/null
+++ b/chrome/browser/chromeos/net/cert_verify_proc_chromeos_unittest.cc
@@ -0,0 +1,373 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/net/cert_verify_proc_chromeos.h"
+
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/nss_cert_database_chromeos.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+class CertVerifyProcChromeOSTest : public testing::Test {
+ public:
+ CertVerifyProcChromeOSTest() : user_1_("user1"), user_2_("user2") {}
+
+ virtual void SetUp() OVERRIDE {
+ // Initialize nss_util slots.
+ ASSERT_TRUE(user_1_.constructed_successfully());
+ ASSERT_TRUE(user_2_.constructed_successfully());
+ user_1_.FinishInit();
+ user_2_.FinishInit();
+
+ // Create NSSCertDatabaseChromeOS for each user.
+ db_1_.reset(new net::NSSCertDatabaseChromeOS(
+ crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()),
+ crypto::GetPrivateSlotForChromeOSUser(
+ user_1_.username_hash(),
+ base::Callback<void(crypto::ScopedPK11Slot)>())));
+ db_2_.reset(new net::NSSCertDatabaseChromeOS(
+ crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()),
+ crypto::GetPrivateSlotForChromeOSUser(
+ user_2_.username_hash(),
+ base::Callback<void(crypto::ScopedPK11Slot)>())));
+
+ // Create default verifier and for each user.
+ verify_proc_default_ = new CertVerifyProcChromeOS();
+ verify_proc_1_ = new CertVerifyProcChromeOS(db_1_->GetPublicSlot());
+ verify_proc_2_ = new CertVerifyProcChromeOS(db_2_->GetPublicSlot());
+
+ // Load test cert chains from disk.
+ certs_1_ =
+ net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
+ "multi-root-chain1.pem",
+ net::X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(4U, certs_1_.size());
+
+ certs_2_ =
+ net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
+ "multi-root-chain2.pem",
+ net::X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(4U, certs_2_.size());
+
+ // The chains:
+ // 1. A (end-entity) -> B -> C -> D (self-signed root)
+ // 2. A (end-entity) -> B -> C2 -> E (self-signed root)
+ ASSERT_TRUE(certs_1_[0]->Equals(certs_2_[0]));
+ ASSERT_TRUE(certs_1_[1]->Equals(certs_2_[1]));
+ ASSERT_FALSE(certs_1_[2]->Equals(certs_2_[2]));
+ ASSERT_EQ("C CA", certs_1_[2]->subject().common_name);
+ ASSERT_EQ("C CA", certs_2_[2]->subject().common_name);
+
+ root_1_.push_back(certs_1_.back());
+ root_2_.push_back(certs_2_.back());
+
+ ASSERT_EQ("D Root CA", root_1_[0]->subject().common_name);
+ ASSERT_EQ("E Root CA", root_2_[0]->subject().common_name);
+ }
+
+ int VerifyWithAdditionalTrustAnchors(
+ net::CertVerifyProc* verify_proc,
+ const net::CertificateList& additional_trust_anchors,
+ net::X509Certificate* cert,
+ std::string* root_subject_name) {
+ int flags = 0;
+ net::CertVerifyResult verify_result;
+ int error = verify_proc->Verify(cert,
+ "127.0.0.1",
+ flags,
+ NULL,
+ additional_trust_anchors,
+ &verify_result);
+ if (verify_result.verified_cert.get() &&
+ !verify_result.verified_cert->GetIntermediateCertificates().empty()) {
+ net::X509Certificate::OSCertHandle root =
+ verify_result.verified_cert->GetIntermediateCertificates().back();
+ root_subject_name->assign(root->subjectName);
+ } else {
+ root_subject_name->clear();
+ }
+ return error;
+ }
+
+ int Verify(net::CertVerifyProc* verify_proc,
+ net::X509Certificate* cert,
+ std::string* root_subject_name) {
+ net::CertificateList additional_trust_anchors;
+ return VerifyWithAdditionalTrustAnchors(
+ verify_proc, additional_trust_anchors, cert, root_subject_name);
+ }
+
+ protected:
+ crypto::ScopedTestNSSChromeOSUser user_1_;
+ crypto::ScopedTestNSSChromeOSUser user_2_;
+ scoped_ptr<net::NSSCertDatabaseChromeOS> db_1_;
+ scoped_ptr<net::NSSCertDatabaseChromeOS> db_2_;
+ scoped_refptr<net::CertVerifyProc> verify_proc_default_;
+ scoped_refptr<net::CertVerifyProc> verify_proc_1_;
+ scoped_refptr<net::CertVerifyProc> verify_proc_2_;
+ net::CertificateList certs_1_;
+ net::CertificateList certs_2_;
+ net::CertificateList root_1_;
+ net::CertificateList root_2_;
+};
+
+// Test that the CertVerifyProcChromeOS doesn't trusts roots that are in other
+// user's slots or that have been deleted, and that verifying done by one user
+// doesn't affect verifications done by others.
+TEST_F(CertVerifyProcChromeOSTest, TestChainVerify) {
+ scoped_refptr<net::X509Certificate> server = certs_1_[0];
+ std::string verify_root;
+ // Before either of the root certs have been trusted, all verifications should
+ // fail with CERT_AUTHORITY_INVALID.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_2_.get(), server.get(), &verify_root));
+
+ // Import and trust the D root for user 1.
+ net::NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(db_1_->ImportCACerts(
+ root_1_, net::NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ // Imported CA certs are not trusted by default verifier.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ // User 1 should now verify successfully through the D root.
+ EXPECT_EQ(net::OK, Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ // User 2 should still fail.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_2_.get(), server.get(), &verify_root));
+
+ // Import and trust the E root for user 2.
+ failed.clear();
+ EXPECT_TRUE(db_2_->ImportCACerts(
+ root_2_, net::NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ // Imported CA certs are not trusted by default verifier.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ // User 1 should still verify successfully through the D root.
+ EXPECT_EQ(net::OK, Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ // User 2 should now verify successfully through the E root.
+ EXPECT_EQ(net::OK, Verify(verify_proc_2_.get(), server.get(), &verify_root));
+ EXPECT_EQ("CN=E Root CA", verify_root);
+
+ // Delete D root.
+ EXPECT_TRUE(db_1_->DeleteCertAndKey(root_1_[0]));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ // User 1 should now fail to verify.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ // User 2 should still verify successfully through the E root.
+ EXPECT_EQ(net::OK, Verify(verify_proc_2_.get(), server.get(), &verify_root));
+ EXPECT_EQ("CN=E Root CA", verify_root);
+
+ // Delete E root.
+ EXPECT_TRUE(db_2_->DeleteCertAndKey(root_2_[0]));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ // User 1 should still fail to verify.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ // User 2 should now fail to verify.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_2_.get(), server.get(), &verify_root));
+}
+
+// Test that roots specified through additional_trust_anchors are trusted for
+// that verification, and that there is not any caching that affects later
+// verifications.
+TEST_F(CertVerifyProcChromeOSTest, TestAdditionalTrustAnchors) {
+ EXPECT_TRUE(verify_proc_default_->SupportsAdditionalTrustAnchors());
+ EXPECT_TRUE(verify_proc_1_->SupportsAdditionalTrustAnchors());
+
+ scoped_refptr<net::X509Certificate> server = certs_1_[0];
+ std::string verify_root;
+ net::CertificateList additional_trust_anchors;
+
+ // Before either of the root certs have been trusted, all verifications should
+ // fail with CERT_AUTHORITY_INVALID.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ VerifyWithAdditionalTrustAnchors(verify_proc_default_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ VerifyWithAdditionalTrustAnchors(verify_proc_1_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+
+ // Use D Root CA as additional trust anchor. Verifications should succeed now.
+ additional_trust_anchors.push_back(root_1_[0]);
+ EXPECT_EQ(net::OK,
+ VerifyWithAdditionalTrustAnchors(verify_proc_default_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ EXPECT_EQ(net::OK,
+ VerifyWithAdditionalTrustAnchors(verify_proc_1_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ // User 2 should still fail.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ VerifyWithAdditionalTrustAnchors(verify_proc_2_.get(),
+ net::CertificateList(),
+ server.get(),
+ &verify_root));
+
+ // Without additional trust anchors, verification should fail again.
+ additional_trust_anchors.clear();
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ VerifyWithAdditionalTrustAnchors(verify_proc_default_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ VerifyWithAdditionalTrustAnchors(verify_proc_1_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+
+ // Import and trust the D Root CA for user 2.
+ net::CertificateList roots;
+ roots.push_back(root_1_[0]);
+ net::NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(
+ db_2_->ImportCACerts(roots, net::NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ // Use D Root CA as additional trust anchor. Verifications should still
+ // succeed even if the cert is trusted by a different profile.
+ additional_trust_anchors.push_back(root_1_[0]);
+ EXPECT_EQ(net::OK,
+ VerifyWithAdditionalTrustAnchors(verify_proc_default_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ EXPECT_EQ(net::OK,
+ VerifyWithAdditionalTrustAnchors(verify_proc_1_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ EXPECT_EQ(net::OK,
+ VerifyWithAdditionalTrustAnchors(verify_proc_2_.get(),
+ additional_trust_anchors,
+ server.get(),
+ &verify_root));
+ EXPECT_EQ("CN=D Root CA", verify_root);
+}
+
+class CertVerifyProcChromeOSOrderingTest
+ : public CertVerifyProcChromeOSTest,
+ public ::testing::WithParamInterface<
+ std::tr1::tuple<bool, int, std::string> > {};
+
+// Test a variety of different combinations of (maybe) verifying / (maybe)
+// importing / verifying again, to try to find any cases where caching might
+// affect the results.
+TEST_P(CertVerifyProcChromeOSOrderingTest, TrustThenVerify) {
+ const ParamType& param = GetParam();
+ const bool verify_first = std::tr1::get<0>(param);
+ const int trust_bitmask = std::tr1::get<1>(param);
+ const std::string test_order = std::tr1::get<2>(param);
+ DVLOG(1) << "verify_first: " << verify_first
+ << " trust_bitmask: " << trust_bitmask
+ << " test_order: " << test_order;
+
+ scoped_refptr<net::X509Certificate> server = certs_1_[0];
+ std::string verify_root;
+
+ if (verify_first) {
+ // Before either of the root certs have been trusted, all verifications
+ // should fail with CERT_AUTHORITY_INVALID.
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_2_.get(), server.get(), &verify_root));
+ }
+
+ int expected_user1_result = net::ERR_CERT_AUTHORITY_INVALID;
+ int expected_user2_result = net::ERR_CERT_AUTHORITY_INVALID;
+
+ if (trust_bitmask & 1) {
+ expected_user1_result = net::OK;
+ // Import and trust the D root for user 1.
+ net::NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(db_1_->ImportCACerts(
+ root_1_, net::NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+ }
+
+ if (trust_bitmask & 2) {
+ expected_user2_result = net::OK;
+ // Import and trust the E root for user 2.
+ net::NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(db_2_->ImportCACerts(
+ root_2_, net::NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+ }
+
+ // Repeat the tests twice, they should return the same each time.
+ for (int i = 0; i < 2; ++i) {
+ SCOPED_TRACE(i);
+ for (std::string::const_iterator j = test_order.begin();
+ j != test_order.end();
+ ++j) {
+ switch (*j) {
+ case 'd':
+ // Default verifier should always fail.
+ EXPECT_EQ(
+ net::ERR_CERT_AUTHORITY_INVALID,
+ Verify(verify_proc_default_.get(), server.get(), &verify_root));
+ break;
+ case '1':
+ EXPECT_EQ(expected_user1_result,
+ Verify(verify_proc_1_.get(), server.get(), &verify_root));
+ if (expected_user1_result == net::OK)
+ EXPECT_EQ("CN=D Root CA", verify_root);
+ break;
+ case '2':
+ EXPECT_EQ(expected_user2_result,
+ Verify(verify_proc_2_.get(), server.get(), &verify_root));
+ if (expected_user2_result == net::OK)
+ EXPECT_EQ("CN=E Root CA", verify_root);
+ break;
+ default:
+ FAIL();
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ Variations,
+ CertVerifyProcChromeOSOrderingTest,
+ ::testing::Combine(
+ ::testing::Bool(),
+ ::testing::Range(0, 1 << 2),
+ ::testing::Values("d12", "d21", "1d2", "12d", "2d1", "21d")));
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.cc b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
index 947f4dcc..1c8d5f3 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
@@ -49,16 +49,15 @@ PolicyCertVerifier::~PolicyCertVerifier() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
}
-void PolicyCertVerifier::InitializeOnIOThread() {
+void PolicyCertVerifier::InitializeOnIOThread(
+ const scoped_refptr<net::CertVerifyProc>& verify_proc) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- scoped_refptr<net::CertVerifyProc> verify_proc =
- net::CertVerifyProc::CreateDefault();
if (!verify_proc->SupportsAdditionalTrustAnchors()) {
LOG(WARNING)
<< "Additional trust anchors not supported on the current platform!";
}
net::MultiThreadedCertVerifier* verifier =
- new net::MultiThreadedCertVerifier(verify_proc.get());
+ new net::MultiThreadedCertVerifier(verify_proc);
verifier->SetCertTrustAnchorProvider(this);
delegate_.reset(verifier);
}
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.h b/chrome/browser/chromeos/policy/policy_cert_verifier.h
index 07b1679..6a2f0ff 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier.h
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier.h
@@ -17,6 +17,7 @@
#include "net/cert/cert_verifier.h"
namespace net {
+class CertVerifyProc;
class CertVerifyResult;
class X509Certificate;
typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
@@ -38,7 +39,8 @@ class PolicyCertVerifier : public net::CertVerifier,
explicit PolicyCertVerifier(const base::Closure& anchor_used_callback);
virtual ~PolicyCertVerifier();
- void InitializeOnIOThread();
+ void InitializeOnIOThread(
+ const scoped_refptr<net::CertVerifyProc>& verify_proc);
// Sets the additional trust anchors.
void SetTrustAnchors(const net::CertificateList& trust_anchors);
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc b/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc
index 11ae9e5..29695fe 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc
@@ -10,14 +10,15 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
+#include "chrome/browser/chromeos/net/cert_verify_proc_chromeos.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
#include "net/base/net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/base/test_data_directory.h"
#include "net/cert/cert_trust_anchor_provider.h"
-#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/x509_certificate.h"
@@ -44,7 +45,8 @@ class PolicyCertVerifierTest : public testing::Test {
cert_verifier_.reset(new PolicyCertVerifier(base::Bind(
&PolicyCertVerifierTest::OnTrustAnchorUsed, base::Unretained(this))));
- cert_verifier_->InitializeOnIOThread();
+ cert_verifier_->InitializeOnIOThread(new chromeos::CertVerifyProcChromeOS(
+ crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot())));
test_ca_cert_ = LoadCertificate("root_ca_cert.pem", net::CA_CERT);
ASSERT_TRUE(test_ca_cert_);
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index ee97bb9..f8ea00c 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -52,8 +52,10 @@
#include "net/base/network_time_notifier.h"
#include "net/base/sdch_manager.h"
#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_proc.h"
#include "net/cert/ct_known_logs.h"
#include "net/cert/ct_verifier.h"
+#include "net/cert/multi_threaded_cert_verifier.h"
#include "net/cookies/cookie_store.h"
#include "net/dns/host_cache.h"
#include "net/dns/host_resolver.h"
@@ -104,6 +106,10 @@
#include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h"
#endif
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/net/cert_verify_proc_chromeos.h"
+#endif
+
using content::BrowserThread;
class SafeBrowsingURLRequestContext;
@@ -545,7 +551,14 @@ void IOThread::InitAsync() {
globals_->system_network_delegate.reset(network_delegate);
globals_->host_resolver = CreateGlobalHostResolver(net_log_);
UpdateDnsClientEnabled();
- globals_->cert_verifier.reset(net::CertVerifier::CreateDefault());
+#if defined(OS_CHROMEOS)
+ // Creates a CertVerifyProc that doesn't allow any profile-provided certs.
+ globals_->cert_verifier.reset(new net::MultiThreadedCertVerifier(
+ new chromeos::CertVerifyProcChromeOS()));
+#else
+ globals_->cert_verifier.reset(
+ new net::MultiThreadedCertVerifier(net::CertVerifyProc::CreateDefault()));
+#endif
globals_->transport_security_state.reset(new net::TransportSecurityState());
#if !defined(USE_OPENSSL)
// For now, Certificate Transparency is only implemented for platforms
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 0b164cd..f8a5dc8 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -94,6 +94,7 @@
#include "chrome/browser/chromeos/drive/drive_protocol_handler.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/net/cert_verify_proc_chromeos.h"
#include "chrome/browser/chromeos/policy/policy_cert_service.h"
#include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
#include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
@@ -103,6 +104,7 @@
#include "chromeos/settings/cros_settings_names.h"
#include "crypto/nss_util.h"
#include "crypto/nss_util_internal.h"
+#include "net/cert/multi_threaded_cert_verifier.h"
#include "net/ssl/client_cert_store_chromeos.h"
#endif // defined(OS_CHROMEOS)
@@ -967,14 +969,20 @@ void ProfileIOData::Init(content::ProtocolHandlerMap* protocol_handlers) const {
#endif
#if defined(OS_CHROMEOS)
+ username_hash_ = profile_params_->username_hash;
+ crypto::ScopedPK11Slot public_slot =
+ crypto::GetPublicSlotForChromeOSUser(username_hash_);
+ // The private slot won't be ready by this point. It shouldn't be necessary
+ // for cert trust purposes anyway.
+ scoped_refptr<net::CertVerifyProc> verify_proc =
+ new chromeos::CertVerifyProcChromeOS(public_slot.Pass());
if (cert_verifier_) {
- cert_verifier_->InitializeOnIOThread();
+ cert_verifier_->InitializeOnIOThread(verify_proc);
main_request_context_->set_cert_verifier(cert_verifier_.get());
} else {
main_request_context_->set_cert_verifier(
- io_thread_globals->cert_verifier.get());
+ new net::MultiThreadedCertVerifier(verify_proc.get()));
}
- username_hash_ = profile_params_->username_hash;
#else
main_request_context_->set_cert_verifier(
io_thread_globals->cert_verifier.get());
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index c34b736..7e3d8c92 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -643,6 +643,8 @@
'browser/chromeos/mobile/mobile_activator.h',
'browser/chromeos/mobile_config.cc',
'browser/chromeos/mobile_config.h',
+ 'browser/chromeos/net/cert_verify_proc_chromeos.cc',
+ 'browser/chromeos/net/cert_verify_proc_chromeos.h',
'browser/chromeos/net/network_portal_detector.cc',
'browser/chromeos/net/network_portal_detector.h',
'browser/chromeos/net/network_portal_detector_impl.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index cf1825e..24289f6 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -731,6 +731,7 @@
'browser/chromeos/memory/oom_priority_manager_unittest.cc',
'browser/chromeos/mobile/mobile_activator_unittest.cc',
'browser/chromeos/mobile_config_unittest.cc',
+ 'browser/chromeos/net/cert_verify_proc_chromeos_unittest.cc',
'browser/chromeos/net/network_portal_detector_impl_unittest.cc',
'browser/chromeos/net/onc_utils_unittest.cc',
'browser/chromeos/offline/offline_load_page_unittest.cc',
diff --git a/net/cert/cert_verify_proc_nss.cc b/net/cert/cert_verify_proc_nss.cc
index e488882..748d1d3 100644
--- a/net/cert/cert_verify_proc_nss.cc
+++ b/net/cert/cert_verify_proc_nss.cc
@@ -108,6 +108,8 @@ int MapSecurityError(int err) {
case SEC_ERROR_UNKNOWN_ISSUER:
case SEC_ERROR_UNTRUSTED_ISSUER:
case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_APPLICATION_CALLBACK_ERROR: // Rejected by
+ // chain_verify_callback.
return ERR_CERT_AUTHORITY_INVALID;
// TODO(port): map ERR_CERT_NO_REVOCATION_MECHANISM.
case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE:
@@ -349,6 +351,7 @@ SECStatus PKIXVerifyCert(CERTCertificate* cert_handle,
const SECOidTag* policy_oids,
int num_policy_oids,
CERTCertList* additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback,
CERTValOutParam* cvout) {
bool use_crl = check_revocation;
bool use_ocsp = check_revocation;
@@ -438,6 +441,11 @@ SECStatus PKIXVerifyCert(CERTCertificate* cert_handle,
in_param.value.scalar.b = PR_FALSE;
cvin.push_back(in_param);
}
+ if (chain_verify_callback) {
+ in_param.type = cert_pi_chainVerifyCallback;
+ in_param.value.pointer.chainVerifyCallback = chain_verify_callback;
+ cvin.push_back(in_param);
+ }
in_param.type = cert_pi_end;
cvin.push_back(in_param);
@@ -658,7 +666,8 @@ bool VerifyEV(CERTCertificate* cert_handle,
bool rev_checking_enabled,
EVRootCAMetadata* metadata,
SECOidTag ev_policy_oid,
- CERTCertList* additional_trust_anchors) {
+ CERTCertList* additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback) {
CERTValOutParam cvout[3];
int cvout_index = 0;
cvout[cvout_index].type = cert_po_certList;
@@ -680,6 +689,7 @@ bool VerifyEV(CERTCertificate* cert_handle,
&ev_policy_oid,
1,
additional_trust_anchors,
+ chain_verify_callback,
cvout);
if (status != SECSuccess)
return false;
@@ -736,12 +746,13 @@ bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
return true;
}
-int CertVerifyProcNSS::VerifyInternal(
+int CertVerifyProcNSS::VerifyInternalImpl(
X509Certificate* cert,
const std::string& hostname,
int flags,
CRLSet* crl_set,
const CertificateList& additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback,
CertVerifyResult* verify_result) {
#if defined(OS_IOS)
// For iOS, the entire chain must be loaded into NSS's in-memory certificate
@@ -794,9 +805,15 @@ int CertVerifyProcNSS::VerifyInternal(
CertificateListToCERTCertList(additional_trust_anchors));
}
- SECStatus status = PKIXVerifyCert(cert_handle, check_revocation, false,
- cert_io_enabled, NULL, 0,
- trust_anchors.get(), cvout);
+ SECStatus status = PKIXVerifyCert(cert_handle,
+ check_revocation,
+ false,
+ cert_io_enabled,
+ NULL,
+ 0,
+ trust_anchors.get(),
+ chain_verify_callback,
+ cvout);
if (status == SECSuccess &&
(flags & CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) &&
@@ -806,8 +823,14 @@ int CertVerifyProcNSS::VerifyInternal(
// 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(),
+ status = PKIXVerifyCert(cert_handle,
+ true,
+ true,
+ cert_io_enabled,
+ NULL,
+ 0,
+ trust_anchors.get(),
+ chain_verify_callback,
cvout);
}
@@ -869,8 +892,14 @@ int CertVerifyProcNSS::VerifyInternal(
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())) {
+ if (VerifyEV(cert_handle,
+ flags,
+ crl_set,
+ check_revocation,
+ metadata,
+ ev_policy_oid,
+ trust_anchors.get(),
+ chain_verify_callback)) {
verify_result->cert_status |= CERT_STATUS_IS_EV;
}
}
@@ -878,4 +907,20 @@ int CertVerifyProcNSS::VerifyInternal(
return OK;
}
+int CertVerifyProcNSS::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ return VerifyInternalImpl(cert,
+ hostname,
+ flags,
+ crl_set,
+ additional_trust_anchors,
+ NULL, // chain_verify_callback
+ verify_result);
+}
+
} // namespace net
diff --git a/net/cert/cert_verify_proc_nss.h b/net/cert/cert_verify_proc_nss.h
index f8bb853..d9971cf 100644
--- a/net/cert/cert_verify_proc_nss.h
+++ b/net/cert/cert_verify_proc_nss.h
@@ -5,6 +5,8 @@
#ifndef NET_CERT_CERT_VERIFY_PROC_NSS_H_
#define NET_CERT_CERT_VERIFY_PROC_NSS_H_
+#include <certt.h>
+
#include "net/base/net_export.h"
#include "net/cert/cert_verify_proc.h"
@@ -20,6 +22,17 @@ class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc {
protected:
virtual ~CertVerifyProcNSS();
+ // Like VerifyInternal, but adds a |chain_verify_callback| to override trust
+ // decisions. See the documentation for CERTChainVerifyCallback and
+ // CERTChainVerifyCallbackFunc in NSS's lib/certdb/certt.h.
+ int VerifyInternalImpl(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CERTChainVerifyCallback* chain_verify_callback,
+ CertVerifyResult* verify_result);
+
private:
virtual int VerifyInternal(X509Certificate* cert,
const std::string& hostname,
diff --git a/net/cert/cert_verify_proc_openssl.cc b/net/cert/cert_verify_proc_openssl.cc
index 3bff531..0f21daa 100644
--- a/net/cert/cert_verify_proc_openssl.cc
+++ b/net/cert/cert_verify_proc_openssl.cc
@@ -140,15 +140,9 @@ void GetCertChainInfo(X509_STORE_CTX* store_ctx,
} else {
root = verified_chain.back();
}
- const CertificateList& temporary_roots =
- TestRootCerts::GetInstance()->temporary_roots();
- for (size_t i = 0; i < temporary_roots.size(); ++i) {
- if (X509Certificate::IsSameOSCert(
- root, temporary_roots[i]->os_cert_handle())) {
+ TestRootCerts* root_certs = TestRootCerts::GetInstance();
+ if (root_certs->Contains(root))
verify_result->is_issued_by_known_root = false;
- break;
- }
- }
}
}
}
diff --git a/net/cert/nss_profile_filter_chromeos.cc b/net/cert/nss_profile_filter_chromeos.cc
index 906780f..5678426 100644
--- a/net/cert/nss_profile_filter_chromeos.cc
+++ b/net/cert/nss_profile_filter_chromeos.cc
@@ -4,6 +4,7 @@
#include "net/cert/nss_profile_filter_chromeos.h"
+#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "net/cert/x509_certificate.h"
@@ -11,10 +12,10 @@ namespace net {
namespace {
-std::string CertSlotsString(const scoped_refptr<X509Certificate>& cert) {
+std::string CertSlotsString(CERTCertificate* cert) {
std::string result;
crypto::ScopedPK11SlotList slots_for_cert(
- PK11_GetAllSlotsForCert(cert->os_cert_handle(), NULL));
+ PK11_GetAllSlotsForCert(cert, NULL));
for (PK11SlotListElement* slot_element =
PK11_GetFirstSafe(slots_for_cert.get());
slot_element;
@@ -86,13 +87,12 @@ bool NSSProfileFilterChromeOS::IsModuleAllowed(PK11SlotInfo* slot) const {
return false;
}
-bool NSSProfileFilterChromeOS::IsCertAllowed(
- const scoped_refptr<X509Certificate>& cert) const {
+bool NSSProfileFilterChromeOS::IsCertAllowed(CERTCertificate* cert) const {
crypto::ScopedPK11SlotList slots_for_cert(
- PK11_GetAllSlotsForCert(cert->os_cert_handle(), NULL));
+ PK11_GetAllSlotsForCert(cert, NULL));
if (!slots_for_cert) {
- DVLOG(2) << "cert no slots: " << cert->subject().GetDisplayName();
- return true;
+ DVLOG(2) << "cert no slots: " << base::StringPiece(cert->nickname);
+ return false;
}
for (PK11SlotListElement* slot_element =
@@ -102,13 +102,13 @@ bool NSSProfileFilterChromeOS::IsCertAllowed(
PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) {
if (IsModuleAllowed(slot_element->slot)) {
DVLOG(3) << "cert from " << CertSlotsString(cert)
- << " allowed: " << cert->subject().GetDisplayName();
+ << " allowed: " << base::StringPiece(cert->nickname);
PK11_FreeSlotListElement(slots_for_cert.get(), slot_element);
return true;
}
}
DVLOG(2) << "cert from " << CertSlotsString(cert)
- << " filtered: " << cert->subject().GetDisplayName();
+ << " filtered: " << base::StringPiece(cert->nickname);
return false;
}
@@ -118,7 +118,7 @@ NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::
bool NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::operator()(
const scoped_refptr<X509Certificate>& cert) const {
- return !filter_.IsCertAllowed(cert);
+ return !filter_.IsCertAllowed(cert->os_cert_handle());
}
NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::
diff --git a/net/cert/nss_profile_filter_chromeos.h b/net/cert/nss_profile_filter_chromeos.h
index ae310c6..4698619 100644
--- a/net/cert/nss_profile_filter_chromeos.h
+++ b/net/cert/nss_profile_filter_chromeos.h
@@ -14,20 +14,32 @@ namespace net {
class X509Certificate;
+// On ChromeOS each user has separate NSS databases, which are loaded
+// simultaneously when multiple users are logged in at the same time. NSS
+// doesn't have built-in support to partition databases into separate groups, so
+// NSSProfileFilterChromeOS can be used to check if a given slot or certificate
+// should be used for a given user.
+//
+// Objects of this class are thread-safe except for the Init function, which if
+// called must not be called while other threads could access the object.
class NET_EXPORT NSSProfileFilterChromeOS {
public:
+ // Create a filter. Until Init is called (or if Init is called with NULL
+ // slot handles), the filter will allow only certs/slots from the read-only
+ // slots and the root CA module.
NSSProfileFilterChromeOS();
NSSProfileFilterChromeOS(const NSSProfileFilterChromeOS& other);
~NSSProfileFilterChromeOS();
NSSProfileFilterChromeOS& operator=(const NSSProfileFilterChromeOS& other);
- // Initializes the filter with slot handles.
+ // Initialize the filter with the slot handles to allow. This method is not
+ // thread-safe.
void Init(crypto::ScopedPK11Slot public_slot,
crypto::ScopedPK11Slot private_slot);
bool IsModuleAllowed(PK11SlotInfo* slot) const;
- bool IsCertAllowed(const scoped_refptr<X509Certificate>& cert) const;
+ bool IsCertAllowed(CERTCertificate* cert) const;
class CertNotAllowedForProfilePredicate {
public:
diff --git a/net/cert/nss_profile_filter_chromeos_unittest.cc b/net/cert/nss_profile_filter_chromeos_unittest.cc
index 1f6260d..60ceeb1 100644
--- a/net/cert/nss_profile_filter_chromeos_unittest.cc
+++ b/net/cert/nss_profile_filter_chromeos_unittest.cc
@@ -102,12 +102,14 @@ class NSSProfileFilterChromeOSTest : public testing::Test {
NSSProfileFilterChromeOS profile_filter_1_copy_;
};
-TEST_F(NSSProfileFilterChromeOSTest, TempCertAllowed) {
+TEST_F(NSSProfileFilterChromeOSTest, TempCertNotAllowed) {
EXPECT_EQ(NULL, certs_[0]->os_cert_handle()->slot);
- EXPECT_TRUE(no_slots_profile_filter_.IsCertAllowed(certs_[0]));
- EXPECT_TRUE(profile_filter_1_.IsCertAllowed(certs_[0]));
- EXPECT_TRUE(profile_filter_1_copy_.IsCertAllowed(certs_[0]));
- EXPECT_TRUE(profile_filter_2_.IsCertAllowed(certs_[0]));
+ EXPECT_FALSE(
+ no_slots_profile_filter_.IsCertAllowed(certs_[0]->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_1_.IsCertAllowed(certs_[0]->os_cert_handle()));
+ EXPECT_FALSE(
+ profile_filter_1_copy_.IsCertAllowed(certs_[0]->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_2_.IsCertAllowed(certs_[0]->os_cert_handle()));
}
TEST_F(NSSProfileFilterChromeOSTest, InternalSlotAllowed) {
@@ -137,10 +139,12 @@ TEST_F(NSSProfileFilterChromeOSTest, RootCertsAllowed) {
CertificateList root_certs(ListCertsInSlot(root_certs_slot.get()));
ASSERT_FALSE(root_certs.empty());
- EXPECT_TRUE(no_slots_profile_filter_.IsCertAllowed(root_certs[0]));
- EXPECT_TRUE(profile_filter_1_.IsCertAllowed(root_certs[0]));
- EXPECT_TRUE(profile_filter_1_copy_.IsCertAllowed(root_certs[0]));
- EXPECT_TRUE(profile_filter_2_.IsCertAllowed(root_certs[0]));
+ EXPECT_TRUE(
+ no_slots_profile_filter_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_1_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+ EXPECT_TRUE(
+ profile_filter_1_copy_.IsCertAllowed(root_certs[0]->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_2_.IsCertAllowed(root_certs[0]->os_cert_handle()));
}
TEST_F(NSSProfileFilterChromeOSTest, SoftwareSlots) {
@@ -171,16 +175,18 @@ TEST_F(NSSProfileFilterChromeOSTest, SoftwareSlots) {
"cert2",
PR_FALSE /* includeTrust (unused) */));
- EXPECT_FALSE(no_slots_profile_filter_.IsCertAllowed(cert_1));
- EXPECT_FALSE(no_slots_profile_filter_.IsCertAllowed(cert_2));
+ EXPECT_FALSE(
+ no_slots_profile_filter_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_FALSE(
+ no_slots_profile_filter_.IsCertAllowed(cert_2->os_cert_handle()));
- EXPECT_TRUE(profile_filter_1_.IsCertAllowed(cert_1));
- EXPECT_TRUE(profile_filter_1_copy_.IsCertAllowed(cert_1));
- EXPECT_FALSE(profile_filter_1_.IsCertAllowed(cert_2));
- EXPECT_FALSE(profile_filter_1_copy_.IsCertAllowed(cert_2));
+ EXPECT_TRUE(profile_filter_1_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_1_copy_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_1_.IsCertAllowed(cert_2->os_cert_handle()));
+ EXPECT_FALSE(profile_filter_1_copy_.IsCertAllowed(cert_2->os_cert_handle()));
- EXPECT_FALSE(profile_filter_2_.IsCertAllowed(cert_1));
- EXPECT_TRUE(profile_filter_2_.IsCertAllowed(cert_2));
+ EXPECT_FALSE(profile_filter_2_.IsCertAllowed(cert_1->os_cert_handle()));
+ EXPECT_TRUE(profile_filter_2_.IsCertAllowed(cert_2->os_cert_handle()));
}
} // namespace net
diff --git a/net/cert/test_root_certs.h b/net/cert/test_root_certs.h
index 03cedcc..cf35a2a 100644
--- a/net/cert/test_root_certs.h
+++ b/net/cert/test_root_certs.h
@@ -23,6 +23,12 @@
#include "base/mac/scoped_cftyperef.h"
#endif
+#if defined(USE_NSS)
+typedef struct CERTCertificateStr CERTCertificate;
+#elif defined(USE_OPENSSL) && !defined(OS_ANDROID)
+typedef struct x509_st X509;
+#endif
+
namespace base {
class FilePath;
}
@@ -34,7 +40,7 @@ class X509Certificate;
// TestRootCerts is a helper class for unit tests that is used to
// artificially mark a certificate as trusted, independent of the local
// machine configuration.
-class NET_EXPORT_PRIVATE TestRootCerts {
+class NET_EXPORT TestRootCerts {
public:
// Obtains the Singleton instance to the trusted certificates.
static TestRootCerts* GetInstance();
@@ -58,7 +64,9 @@ class NET_EXPORT_PRIVATE TestRootCerts {
// Returns true if there are no certificates that have been marked trusted.
bool IsEmpty() const;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(USE_NSS)
+ bool Contains(CERTCertificate* cert) const;
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
CFArrayRef temporary_roots() const { return temporary_roots_; }
// Modifies the root certificates of |trust_ref| to include the
@@ -73,6 +81,7 @@ class NET_EXPORT_PRIVATE TestRootCerts {
#elif defined(USE_OPENSSL) && !defined(OS_ANDROID)
const std::vector<scoped_refptr<X509Certificate> >&
temporary_roots() const { return temporary_roots_; }
+ bool Contains(X509* cert) const;
#elif defined(OS_WIN)
HCERTSTORE temporary_roots() const { return temporary_roots_; }
diff --git a/net/cert/test_root_certs_nss.cc b/net/cert/test_root_certs_nss.cc
index 3a2f88a..3c27145 100644
--- a/net/cert/test_root_certs_nss.cc
+++ b/net/cert/test_root_certs_nss.cc
@@ -114,6 +114,17 @@ bool TestRootCerts::IsEmpty() const {
return trust_cache_.empty();
}
+#if defined(USE_NSS)
+bool TestRootCerts::Contains(CERTCertificate* cert) const {
+ for (std::list<TrustEntry*>::const_iterator it = trust_cache_.begin();
+ it != trust_cache_.end(); ++it) {
+ if (X509Certificate::IsSameOSCert(cert, (*it)->certificate()))
+ return true;
+ }
+ return false;
+}
+#endif
+
TestRootCerts::~TestRootCerts() {
Clear();
}
diff --git a/net/cert/test_root_certs_openssl.cc b/net/cert/test_root_certs_openssl.cc
index 99e1976..1b194bc 100644
--- a/net/cert/test_root_certs_openssl.cc
+++ b/net/cert/test_root_certs_openssl.cc
@@ -42,6 +42,16 @@ bool TestRootCerts::IsEmpty() const {
return temporary_roots_.empty();
}
+bool TestRootCerts::Contains(X509* cert) const {
+ for (std::vector<scoped_refptr<X509Certificate> >::const_iterator it =
+ temporary_roots_.begin();
+ it != temporary_roots_.end(); ++it) {
+ if (X509Certificate::IsSameOSCert(cert, (*it)->os_cert_handle()))
+ return true;
+ }
+ return false;
+}
+
TestRootCerts::~TestRootCerts() {}
void TestRootCerts::Init() {}
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc
index 74c3551..81926be 100644
--- a/net/cert/test_root_certs_unittest.cc
+++ b/net/cert/test_root_certs_unittest.cc
@@ -135,6 +135,39 @@ TEST(TestRootCertsTest, OverrideTrust) {
EXPECT_EQ(bad_verify_result.cert_status, restored_verify_result.cert_status);
}
+#if defined(USE_NSS) || (defined(USE_OPENSSL) && !defined(OS_ANDROID))
+TEST(TestRootCertsTest, Contains) {
+ // Another test root certificate.
+ const char kRootCertificateFile2[] = "2048-rsa-root.pem";
+
+ TestRootCerts* test_roots = TestRootCerts::GetInstance();
+ ASSERT_NE(static_cast<TestRootCerts*>(NULL), test_roots);
+
+ scoped_refptr<X509Certificate> root_cert_1 =
+ ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert_1.get());
+
+ scoped_refptr<X509Certificate> root_cert_2 =
+ ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile2);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert_2.get());
+
+ EXPECT_FALSE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+
+ EXPECT_TRUE(test_roots->Add(root_cert_1.get()));
+ EXPECT_TRUE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+
+ EXPECT_TRUE(test_roots->Add(root_cert_2.get()));
+ EXPECT_TRUE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_TRUE(test_roots->Contains(root_cert_2->os_cert_handle()));
+
+ test_roots->Clear();
+ EXPECT_FALSE(test_roots->Contains(root_cert_1->os_cert_handle()));
+ EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+}
+#endif
+
// TODO(rsleevi): Add tests for revocation checking via CRLs, ensuring that
// TestRootCerts properly injects itself into the validation process. See
// http://crbug.com/63958
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 0ef59fc..9634a28 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -99,6 +99,13 @@ unit tests.
26 Feb 2022 and are generated by
net/data/ssl/scripts/generate-redundant-test-chains.sh.
+- multi-root-chain1.pem
+- multi-root-chain2.pem
+ Two chains, A -> B -> C -> D and A -> B -> C2 -> E (C and C2 share the
+ same public key) to test that certificate validation caching does not
+ interfere with the chain_verify_callback used by CertVerifyProcChromeOS.
+ See CertVerifyProcChromeOSTest.
+
- comodo.chain.pem : A certificate chain for www.comodo.com which should be
recognised as EV. Expires Jun 21 2013.
diff --git a/net/data/ssl/certificates/multi-root-chain1.pem b/net/data/ssl/certificates/multi-root-chain1.pem
new file mode 100644
index 0000000..da8d08a
--- /dev/null
+++ b/net/data/ssl/certificates/multi-root-chain1.pem
@@ -0,0 +1,328 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvYF0kDZ7Nd/z6hjKuWSoWIWQKt4ivpoLl73+YZgq5S3PnrwM
+I14hTxmttT6c1KoFWnFOt5mBp2s1CSJDhL7pccZwO2+IxCu6CrjBfEK+P9gLiIdT
+gnujctRCU/N9y4RQYDQchmU2++ZHT2Fgw5t7SlduLKyQ7kcNspLiu+h84nQ+co8C
+kUHoLmAagIN2uIf9818E3VaX8xEEcJ5I4pblZqOF3bL2kR7ow6Z5lLfkbiU4J6N5
+mZxTuovJz9Ze4MDGBZpgRnMlVqQCPNw21WASD8n5TsAeK3Rg7wMwilN+yArrK5vb
+o2MDhbi6yv2T/fOPh5cRJNUR81Csuh+d1+QwfQIDAQABAoIBAA6ExYZq9iOZhdlw
+js+HW7J0gSgXnrfVm3/DqaKWgurOCLMTmyZ2hrzFrd5N7rwITqKwPaSpWRqXhxet
+DVk1OzNhTaXwFJ1a8ET0BLbdci/4AGI0Y/yCNnKMuowuAnw+Jd5I/8p4JK9F5D67
+qisyVU7LxgAcNHpc7Tq6MC7PUAoVVd7uPy0NtSD3JikwYWHUv+BtOm8P5jugy8Kr
+qMN0c0/qsZ0SedfT1k/IIfu79DZZ5hBl5rUhgMYjuycd0Hi5qOa7o2fdVXJh2eLo
+HY3cQTymWlibutABv//P+NsteyrXXFkcmPjNXHy5WpvmT2RR5Hxx57hJck/7xVjv
+u8B7AgECgYEA9vLjrUeO2Wei5BsL9I5xB+epwaXAUsaugnRXePG5i2HhCRfOeU9f
+PKuv0F4yuHNre9j1w5X/5WzxnFhDSb1QLbFXlgFCltL5a3RypygC9B0r7VFQCaLI
+ODjX5h06PZq6oLLiLSyVlOSrcPXNJLuhMqNnHuOUacUtOIx4B2vpwP0CgYEAxHOU
+YlMwUhgmPKZ/R7lNYMAKKUAiuKEkFM0K1K0/GnizhYDnc2atxORk54ZD6aGYuf9y
+xBWmOZobNTmGw9bnQhePai/xHUoJgs8KWN5e8pBJXAouL12pqRC4sQCIVc9R3J+7
+djTPTco0oexkUsbe1rr4hCqoR9iUePxl7wY5BYECgYEA8nA6fV+HKoDINlEnR4yg
+Aza4PdjQG3Pa11AIoEAP/Hq3RwoMNqRpx1J2ZIZWHSeTGh9CCCY297Ig8XDlfntR
+P8qfRjEugovVOl00Qk7Rt378JRxzC0K4dhm4O73t85T4K9PyoI7ouyhT964ZHDro
+YqJxFq4ugjiF0MJ3BDI5ZrECgYAItf9MZNftq/h2FAPs0ECoG5vXvGpNuYd6DKWA
+TLZRnCyJrO+WZGUsJ9x5j7CPOYUmKjeSjksynqy6LXTWVj8m5RiM4tdULyZA0KFq
+02FubAt0s1bc7tBJGN63qohhFbJRkBul4C2ZC3BOBcdlDEBxURUX9zRDC016F+cF
+NEdvAQKBgQCtJhaEt1dzbB0qYEZz7kYr3QP1hz/6XfnAd8wmJNQQQ8yctNlJc+dH
+Foo3qwU2uJWFbByBxIvkmCDTAOCJwOONP/MhigvDQth/sa+TbY6y39XywL0TyG/p
+X19RWD9TYNG7C9y9Z8Jcz+u9iCd5enqLYw7Qle9gq3jwK1GXsV5zEQ==
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4096 (0x1000)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=B CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:81:74:90:36:7b:35:df:f3:ea:18:ca:b9:64:
+ a8:58:85:90:2a:de:22:be:9a:0b:97:bd:fe:61:98:
+ 2a:e5:2d:cf:9e:bc:0c:23:5e:21:4f:19:ad:b5:3e:
+ 9c:d4:aa:05:5a:71:4e:b7:99:81:a7:6b:35:09:22:
+ 43:84:be:e9:71:c6:70:3b:6f:88:c4:2b:ba:0a:b8:
+ c1:7c:42:be:3f:d8:0b:88:87:53:82:7b:a3:72:d4:
+ 42:53:f3:7d:cb:84:50:60:34:1c:86:65:36:fb:e6:
+ 47:4f:61:60:c3:9b:7b:4a:57:6e:2c:ac:90:ee:47:
+ 0d:b2:92:e2:bb:e8:7c:e2:74:3e:72:8f:02:91:41:
+ e8:2e:60:1a:80:83:76:b8:87:fd:f3:5f:04:dd:56:
+ 97:f3:11:04:70:9e:48:e2:96:e5:66:a3:85:dd:b2:
+ f6:91:1e:e8:c3:a6:79:94:b7:e4:6e:25:38:27:a3:
+ 79:99:9c:53:ba:8b:c9:cf:d6:5e:e0:c0:c6:05:9a:
+ 60:46:73:25:56:a4:02:3c:dc:36:d5:60:12:0f:c9:
+ f9:4e:c0:1e:2b:74:60:ef:03:30:8a:53:7e:c8:0a:
+ eb:2b:9b:db:a3:63:03:85:b8:ba:ca:fd:93:fd:f3:
+ 8f:87:97:11:24:d5:11:f3:50:ac:ba:1f:9d:d7:e4:
+ 30:7d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 2C:58:4E:D7:58:62:F8:D6:BF:BE:80:5C:04:3A:39:B1:41:8C:D3:D7
+ X509v3 Authority Key Identifier:
+ keyid:CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Signature Algorithm: sha1WithRSAEncryption
+ 0d:09:9b:f0:11:81:5a:5d:f1:85:73:fb:f2:c8:bc:82:99:27:
+ fd:58:c7:6a:d6:28:f1:09:63:4e:61:fd:68:3a:23:0a:10:fe:
+ 58:7b:20:a3:7a:69:e6:74:55:5c:14:4f:ec:d7:e2:10:aa:8b:
+ a6:3f:99:c1:ef:bb:48:a9:42:b8:9a:8e:65:75:7b:cc:5c:bd:
+ e4:c0:63:e2:d1:6b:38:32:5f:33:b3:fc:0c:33:b0:8b:dd:10:
+ 54:15:99:2d:c4:62:68:3f:af:3f:e2:d9:52:33:d4:31:4c:ed:
+ c7:28:76:bb:21:6b:b9:41:fb:88:40:52:e1:52:76:31:af:d8:
+ a6:63:d1:d9:a5:e4:95:39:74:a8:73:f5:b0:c4:4e:bf:ef:a1:
+ 87:2e:94:a0:bd:cc:e3:90:45:5c:5a:18:15:0d:09:47:45:0f:
+ 1e:d7:cf:d3:8d:c6:b0:54:c4:5f:21:50:5c:b5:0d:eb:c3:15:
+ 3d:ee:88:fa:b4:80:93:4d:2e:00:9e:46:b6:fb:e8:64:32:51:
+ 8a:35:81:7a:d3:71:73:8d:2a:27:d4:57:2f:66:4c:fc:99:81:
+ 06:66:1f:5b:e6:a4:e5:35:eb:f3:e5:e3:f3:31:78:fe:b0:03:
+ f9:5d:77:97:70:d7:53:52:50:df:15:5b:3e:fc:7e:9a:1f:6a:
+ ce:c2:37:2f
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMGAxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAw
+DgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9gXSQNns13/PqGMq5ZKhYhZAq3iK+mguXvf5h
+mCrlLc+evAwjXiFPGa21PpzUqgVacU63mYGnazUJIkOEvulxxnA7b4jEK7oKuMF8
+Qr4/2AuIh1OCe6Ny1EJT833LhFBgNByGZTb75kdPYWDDm3tKV24srJDuRw2ykuK7
+6HzidD5yjwKRQeguYBqAg3a4h/3zXwTdVpfzEQRwnkjiluVmo4XdsvaRHujDpnmU
+t+RuJTgno3mZnFO6i8nP1l7gwMYFmmBGcyVWpAI83DbVYBIPyflOwB4rdGDvAzCK
+U37ICusrm9ujYwOFuLrK/ZP984+HlxEk1RHzUKy6H53X5DB9AgMBAAGjbzBtMAwG
+A1UdEwEB/wQCMAAwHQYDVR0OBBYEFCxYTtdYYvjWv76AXAQ6ObFBjNPXMB8GA1Ud
+IwQYMBaAFM0YJ0+IsSaFuY+cAKT4Gv0Bhl7sMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+BggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEADQmb8BGBWl3xhXP78si8gpkn
+/VjHatYo8QljTmH9aDojChD+WHsgo3pp5nRVXBRP7NfiEKqLpj+Zwe+7SKlCuJqO
+ZXV7zFy95MBj4tFrODJfM7P8DDOwi90QVBWZLcRiaD+vP+LZUjPUMUztxyh2uyFr
+uUH7iEBS4VJ2Ma/YpmPR2aXklTl0qHP1sMROv++hhy6UoL3M45BFXFoYFQ0JR0UP
+HtfP043GsFTEXyFQXLUN68MVPe6I+rSAk00uAJ5GtvvoZDJRijWBetNxc40qJ9RX
+L2ZM/JmBBmYfW+ak5TXr8+Xj8zF4/rAD+V13l3DXU1JQ3xVbPvx+mh9qzsI3Lw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4097 (0x1001)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=C CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=B CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ab:18:b1:0e:c2:f9:61:1a:13:c4:95:b8:65:d8:
+ cb:36:2f:72:e8:e1:97:44:1d:0d:55:a0:cc:02:12:
+ 04:a4:78:f6:df:c3:6f:c9:b8:50:15:d8:5c:21:ed:
+ 8f:c4:e7:db:81:da:fe:01:60:39:63:99:e4:04:dc:
+ 15:ea:59:6b:35:da:1a:e3:3e:dd:f0:71:8f:64:79:
+ 1a:30:b1:be:5a:43:9c:88:1a:a0:26:2f:5d:6c:b6:
+ 8b:b8:6d:10:b0:97:f4:a1:d3:7b:56:3d:dd:12:19:
+ a3:5e:02:24:9e:19:8e:d2:0b:e2:ea:8e:a2:cc:a1:
+ 8e:f0:49:e1:81:ae:3a:a6:71:d5:e8:e4:80:27:01:
+ 58:66:8c:bd:a0:4f:08:01:59:5c:c3:e3:d5:6f:49:
+ 73:24:66:f5:25:b3:9e:a1:29:21:de:e9:d5:ad:b0:
+ 1d:fc:b7:4c:f7:5a:9a:2b:5a:2c:af:07:aa:c2:82:
+ 5a:36:06:1d:27:2d:90:c7:45:1e:7b:f4:7a:8a:fe:
+ 90:c1:79:c9:8f:4e:67:52:48:ea:0b:dd:d7:fe:84:
+ 54:47:2f:d9:d0:ca:11:07:59:b0:90:08:0b:76:a2:
+ ec:30:a5:45:aa:d7:61:39:84:43:33:97:22:b6:45:
+ c8:e8:ab:73:5f:79:a8:13:55:2f:71:a2:c9:21:aa:
+ 9b:df
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 00:80:9b:99:4b:81:b1:76:49:5c:f2:99:ef:88:93:18:75:68:
+ eb:26:9e:87:95:b6:91:23:c0:b5:78:58:96:97:9c:27:38:fa:
+ f3:e5:c3:a2:34:40:91:6d:45:2c:7f:63:51:98:5b:53:4b:91:
+ ce:0f:e2:32:63:e7:e5:f9:21:6b:b7:f9:94:ac:33:13:5c:27:
+ 9b:98:e7:7f:44:50:29:98:68:81:53:6b:a1:43:d8:04:40:cb:
+ f2:cf:18:39:a5:24:c9:88:b6:b7:76:cb:dd:18:2e:1d:24:3e:
+ d3:59:e9:04:48:9d:59:0c:0f:d0:79:6a:93:7d:2b:b9:8e:50:
+ 34:00:0d:48:ae:10:bd:80:04:74:da:06:27:15:ad:88:ec:36:
+ 61:51:80:fe:6b:1d:46:37:0e:ea:23:60:c1:79:bf:03:2a:d2:
+ 80:9e:2c:10:3a:bc:2d:50:6e:f7:f9:d5:ec:11:d9:b4:62:bc:
+ 09:2c:05:31:06:bf:b9:e1:d5:1e:02:a9:1a:c5:c4:13:bf:b7:
+ 8e:6a:08:51:57:af:db:7b:09:74:bd:c7:bd:3c:de:0a:51:8a:
+ fe:82:0b:4b:34:74:10:4b:4b:34:fd:42:28:48:10:db:5d:6d:
+ 64:80:b1:3c:5c:04:86:32:6c:25:87:db:23:dc:e4:42:e4:71:
+ f9:b1:88:74
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcSgAwIBAgICEAEwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQyBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMA8xDTALBgNVBAMMBEIg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrGLEOwvlhGhPElbhl
+2Ms2L3Lo4ZdEHQ1VoMwCEgSkePbfw2/JuFAV2Fwh7Y/E59uB2v4BYDljmeQE3BXq
+WWs12hrjPt3wcY9keRowsb5aQ5yIGqAmL11stou4bRCwl/Sh03tWPd0SGaNeAiSe
+GY7SC+LqjqLMoY7wSeGBrjqmcdXo5IAnAVhmjL2gTwgBWVzD49VvSXMkZvUls56h
+KSHe6dWtsB38t0z3WporWiyvB6rCglo2Bh0nLZDHRR579HqK/pDBecmPTmdSSOoL
+3df+hFRHL9nQyhEHWbCQCAt2ouwwpUWq12E5hEMzlyK2Rcjoq3NfeagTVS9xoskh
+qpvfAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM0YJ0+IsSaF
+uY+cAKT4Gv0Bhl7sMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEA
+AICbmUuBsXZJXPKZ74iTGHVo6yaeh5W2kSPAtXhYlpecJzj68+XDojRAkW1FLH9j
+UZhbU0uRzg/iMmPn5fkha7f5lKwzE1wnm5jnf0RQKZhogVNroUPYBEDL8s8YOaUk
+yYi2t3bL3RguHSQ+01npBEidWQwP0Hlqk30ruY5QNAANSK4QvYAEdNoGJxWtiOw2
+YVGA/msdRjcO6iNgwXm/AyrSgJ4sEDq8LVBu9/nV7BHZtGK8CSwFMQa/ueHVHgKp
+GsXEE7+3jmoIUVev23sJdL3HvTzeClGK/oILSzR0EEtLNP1CKEgQ211tZICxPFwE
+hjJsJYfbI9zkQuRx+bGIdA==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4099 (0x1003)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=D Root CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=C CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ca:21:76:fd:8b:0f:24:ba:dc:ab:cd:0e:31:94:
+ 01:90:57:79:18:3c:58:61:f5:7e:b7:2f:e6:46:d3:
+ 41:e8:4e:29:29:a8:9d:eb:d6:df:69:d5:b1:10:de:
+ 6b:17:2d:8d:8a:1c:d7:dc:80:85:74:c2:7a:6e:a6:
+ 75:7a:2a:76:42:fb:65:6b:8c:a9:2f:c0:5e:76:1e:
+ bc:35:85:b2:4b:35:a4:97:33:15:76:7d:4f:6e:d0:
+ 3b:45:04:fe:dc:a0:11:67:15:2d:3a:c3:07:c6:db:
+ f2:89:25:92:5e:db:70:bd:88:e9:f0:c7:54:6f:8e:
+ ab:cf:ce:ca:ad:bb:44:72:bf:e9:b5:ab:ba:68:15:
+ 6d:1e:e1:66:d6:60:d8:bd:dc:ab:d3:e8:2f:4c:ee:
+ 29:46:36:c7:b1:61:af:20:19:cc:98:1c:78:5f:4d:
+ 97:7a:de:2f:d9:fd:f0:b8:47:34:ff:ed:73:07:eb:
+ 90:54:11:e2:1b:8e:68:5a:c1:72:a9:af:df:e9:f1:
+ f5:ca:0e:72:03:90:1b:af:64:d6:ee:ce:67:57:1b:
+ fb:c7:f1:c2:5c:97:81:cd:d6:22:7c:26:bf:cd:6a:
+ b9:99:5f:58:63:5f:ce:05:1c:7d:f1:a9:d3:f8:4c:
+ fe:10:82:a4:14:2c:67:97:6c:82:2f:98:38:83:50:
+ bf:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ F5:87:AF:4C:B3:4A:88:7F:EB:92:D3:C7:28:78:91:D9:02:4A:71:DF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 7a:34:4b:a9:c4:e0:82:72:8f:2d:a6:3e:c7:5a:60:61:18:d4:
+ 29:34:96:05:1e:9c:16:23:30:84:2b:72:10:3f:27:cd:0a:e1:
+ 23:0a:fe:6d:86:08:fb:d2:3f:fc:8d:d1:1f:c5:44:bb:89:dc:
+ ae:27:c3:20:99:a3:af:d8:ec:d6:cf:07:e6:59:ae:f4:26:18:
+ 39:78:44:24:4e:aa:93:c2:27:d3:05:ac:06:94:94:8a:d9:fc:
+ 5d:d8:cb:b9:71:14:c8:95:be:29:ee:65:cb:ff:3e:70:05:ef:
+ f9:12:6b:11:cb:37:6d:2d:8a:0e:9d:0d:3b:d8:eb:94:a2:f7:
+ d9:c6:0e:56:18:64:69:e6:3f:3c:64:da:37:c8:f8:f9:dd:1d:
+ 20:e1:af:61:54:09:5f:64:ce:b0:3e:38:fe:fc:20:23:96:d8:
+ 9b:d3:08:23:34:c9:0e:f9:ef:04:96:4a:c8:4c:6e:22:ff:95:
+ 52:db:a8:7f:58:50:eb:10:ca:6a:7b:44:fc:7b:e0:d0:9c:f1:
+ 3d:5a:c6:26:b4:8b:8a:cb:c9:40:94:f6:c8:14:5a:c8:7f:53:
+ 79:a9:d1:83:21:36:a2:ee:3a:50:45:d6:2d:a4:47:ea:67:94:
+ ec:5f:e5:c2:1f:0a:a5:1f:7a:62:ce:d7:b0:b2:bb:e6:af:ab:
+ d5:ed:f6:39
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgICEAMwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJRCBS
+b290IENBMB4XDTE0MDIwNDA0MjU1OFoXDTI0MDIwMjA0MjU1OFowDzENMAsGA1UE
+AwwEQyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMohdv2LDyS6
+3KvNDjGUAZBXeRg8WGH1frcv5kbTQehOKSmonevW32nVsRDeaxctjYoc19yAhXTC
+em6mdXoqdkL7ZWuMqS/AXnYevDWFsks1pJczFXZ9T27QO0UE/tygEWcVLTrDB8bb
+8oklkl7bcL2I6fDHVG+Oq8/Oyq27RHK/6bWrumgVbR7hZtZg2L3cq9PoL0zuKUY2
+x7FhryAZzJgceF9Nl3reL9n98LhHNP/tcwfrkFQR4huOaFrBcqmv3+nx9coOcgOQ
+G69k1u7OZ1cb+8fxwlyXgc3WInwmv81quZlfWGNfzgUcffGp0/hM/hCCpBQsZ5ds
+gi+YOINQv3ECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU9Yev
+TLNKiH/rktPHKHiR2QJKcd8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQB6NEupxOCCco8tpj7HWmBhGNQpNJYFHpwWIzCEK3IQPyfNCuEjCv5thgj7
+0j/8jdEfxUS7idyuJ8MgmaOv2OzWzwfmWa70Jhg5eEQkTqqTwifTBawGlJSK2fxd
+2Mu5cRTIlb4p7mXL/z5wBe/5EmsRyzdtLYoOnQ072OuUovfZxg5WGGRp5j88ZNo3
+yPj53R0g4a9hVAlfZM6wPjj+/CAjltib0wgjNMkO+e8ElkrITG4i/5VS26h/WFDr
+EMpqe0T8e+DQnPE9WsYmtIuKy8lAlPbIFFrIf1N5qdGDITai7jpQRdYtpEfqZ5Ts
+X+XCHwqlH3piztewsrvmr6vV7fY5
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 13476701051823430697 (0xbb06dc343a119c29)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=D Root CA
+ Validity
+ Not Before: Feb 4 04:25:57 2014 GMT
+ Not After : Feb 2 04:25:57 2024 GMT
+ Subject: CN=D Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a8:4f:68:60:f8:f7:db:96:c7:0a:33:3a:bb:24:
+ 01:ec:06:15:68:9e:33:b1:2c:4e:b6:e0:23:bc:ef:
+ 4e:b6:03:09:86:21:72:85:bf:5a:c4:88:54:f8:44:
+ 45:a9:aa:7c:74:50:3c:43:5f:c1:e5:0f:59:8c:e2:
+ 57:48:8f:d9:d9:89:53:01:4a:24:8b:70:b6:38:5d:
+ d4:e3:14:d3:ba:49:83:a5:b7:31:0f:10:2d:06:40:
+ bc:11:39:53:67:04:21:b2:52:a9:61:66:34:2d:3e:
+ ce:0a:9e:96:e7:60:3d:e4:7a:10:9c:1d:e6:8d:a4:
+ 11:20:e9:c5:60:7d:00:d4:03:ff:1b:92:ba:d1:43:
+ 44:12:2e:b9:3e:77:0f:98:33:60:e8:df:67:d8:08:
+ 2d:1a:b4:62:3e:75:6b:76:b8:a1:d1:8f:09:4d:aa:
+ 88:f8:61:90:6c:ce:84:15:85:f8:bd:ba:40:e9:33:
+ 22:ed:63:fa:1b:cf:6a:0d:96:91:11:e1:c7:3a:b9:
+ c3:7a:42:d3:78:5c:c7:c7:b1:72:05:63:92:22:28:
+ b7:ea:3c:0a:d0:6d:9c:aa:6d:60:66:29:bb:43:6a:
+ c0:2b:ce:ef:05:5c:7c:d9:8b:c4:9e:65:80:f0:32:
+ 67:b9:4b:9c:65:7a:df:62:a2:2e:b4:14:50:15:87:
+ b5:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ A6:3C:3F:34:0B:14:89:E3:BF:92:BA:F8:18:0B:E3:C6:31:4C:77:D0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 1e:e6:98:ac:39:4a:f0:c2:c4:70:76:dd:c7:26:2c:61:00:be:
+ fb:73:1e:fd:72:d1:25:a0:84:51:ee:e7:76:f6:72:8e:89:8b:
+ 69:ef:1c:10:ae:ce:4a:29:2b:d6:43:52:3f:32:30:c4:3f:3f:
+ 2a:3e:c8:af:cc:d8:0d:54:b9:74:ac:0c:9e:43:71:c1:56:88:
+ e8:92:a3:40:f4:c0:73:e3:28:bd:93:02:af:c2:1b:66:5e:8c:
+ 5b:0e:48:55:c6:48:aa:f5:c6:c2:c1:cc:83:ac:59:d2:40:25:
+ f9:84:52:8f:57:97:1d:ea:3d:24:26:19:a8:ba:60:1a:dc:bc:
+ 7d:cc:8d:9a:fc:0f:7f:3d:b8:b4:ef:b9:47:28:0f:07:0b:57:
+ ea:3a:12:c8:01:69:8e:49:46:f8:19:cf:ad:69:5a:94:48:f2:
+ 54:3d:b0:99:02:c8:d6:ff:4e:a7:cf:d1:b4:e5:94:92:ce:3f:
+ d1:86:1b:01:6b:51:cd:94:cc:c1:2c:dd:4d:43:c2:e5:cd:21:
+ 9f:3f:ec:88:b7:e4:9d:4c:f5:55:61:2c:75:f9:4b:f7:2f:ba:
+ 2f:6f:d9:f7:1e:70:b5:a5:2f:ea:e9:b6:b0:61:34:0a:20:55:
+ c0:73:af:4a:d3:32:64:7d:cc:c4:3f:b2:45:ad:1e:4c:f6:ad:
+ d9:bf:a7:e0
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIJALsG3DQ6EZwpMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
+BAMMCUQgUm9vdCBDQTAeFw0xNDAyMDQwNDI1NTdaFw0yNDAyMDIwNDI1NTdaMBQx
+EjAQBgNVBAMMCUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKhPaGD499uWxwozOrskAewGFWieM7EsTrbgI7zvTrYDCYYhcoW/WsSIVPhE
+RamqfHRQPENfweUPWYziV0iP2dmJUwFKJItwtjhd1OMU07pJg6W3MQ8QLQZAvBE5
+U2cEIbJSqWFmNC0+zgqeludgPeR6EJwd5o2kESDpxWB9ANQD/xuSutFDRBIuuT53
+D5gzYOjfZ9gILRq0Yj51a3a4odGPCU2qiPhhkGzOhBWF+L26QOkzIu1j+hvPag2W
+kRHhxzq5w3pC03hcx8excgVjkiIot+o8CtBtnKptYGYpu0NqwCvO7wVcfNmLxJ5l
+gPAyZ7lLnGV632KiLrQUUBWHtacCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUpjw/NAsUieO/krr4GAvjxjFMd9AwDgYDVR0PAQH/BAQDAgEGMA0G
+CSqGSIb3DQEBBQUAA4IBAQAe5pisOUrwwsRwdt3HJixhAL77cx79ctEloIRR7ud2
+9nKOiYtp7xwQrs5KKSvWQ1I/MjDEPz8qPsivzNgNVLl0rAyeQ3HBVojokqNA9MBz
+4yi9kwKvwhtmXoxbDkhVxkiq9cbCwcyDrFnSQCX5hFKPV5cd6j0kJhmoumAa3Lx9
+zI2a/A9/Pbi077lHKA8HC1fqOhLIAWmOSUb4Gc+taVqUSPJUPbCZAsjW/06nz9G0
+5ZSSzj/RhhsBa1HNlMzBLN1NQ8LlzSGfP+yIt+SdTPVVYSx1+Uv3L7ovb9n3HnC1
+pS/q6bawYTQKIFXAc69K0zJkfczEP7JFrR5M9q3Zv6fg
+-----END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/multi-root-chain2.pem b/net/data/ssl/certificates/multi-root-chain2.pem
new file mode 100644
index 0000000..b853bb3
--- /dev/null
+++ b/net/data/ssl/certificates/multi-root-chain2.pem
@@ -0,0 +1,328 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvYF0kDZ7Nd/z6hjKuWSoWIWQKt4ivpoLl73+YZgq5S3PnrwM
+I14hTxmttT6c1KoFWnFOt5mBp2s1CSJDhL7pccZwO2+IxCu6CrjBfEK+P9gLiIdT
+gnujctRCU/N9y4RQYDQchmU2++ZHT2Fgw5t7SlduLKyQ7kcNspLiu+h84nQ+co8C
+kUHoLmAagIN2uIf9818E3VaX8xEEcJ5I4pblZqOF3bL2kR7ow6Z5lLfkbiU4J6N5
+mZxTuovJz9Ze4MDGBZpgRnMlVqQCPNw21WASD8n5TsAeK3Rg7wMwilN+yArrK5vb
+o2MDhbi6yv2T/fOPh5cRJNUR81Csuh+d1+QwfQIDAQABAoIBAA6ExYZq9iOZhdlw
+js+HW7J0gSgXnrfVm3/DqaKWgurOCLMTmyZ2hrzFrd5N7rwITqKwPaSpWRqXhxet
+DVk1OzNhTaXwFJ1a8ET0BLbdci/4AGI0Y/yCNnKMuowuAnw+Jd5I/8p4JK9F5D67
+qisyVU7LxgAcNHpc7Tq6MC7PUAoVVd7uPy0NtSD3JikwYWHUv+BtOm8P5jugy8Kr
+qMN0c0/qsZ0SedfT1k/IIfu79DZZ5hBl5rUhgMYjuycd0Hi5qOa7o2fdVXJh2eLo
+HY3cQTymWlibutABv//P+NsteyrXXFkcmPjNXHy5WpvmT2RR5Hxx57hJck/7xVjv
+u8B7AgECgYEA9vLjrUeO2Wei5BsL9I5xB+epwaXAUsaugnRXePG5i2HhCRfOeU9f
+PKuv0F4yuHNre9j1w5X/5WzxnFhDSb1QLbFXlgFCltL5a3RypygC9B0r7VFQCaLI
+ODjX5h06PZq6oLLiLSyVlOSrcPXNJLuhMqNnHuOUacUtOIx4B2vpwP0CgYEAxHOU
+YlMwUhgmPKZ/R7lNYMAKKUAiuKEkFM0K1K0/GnizhYDnc2atxORk54ZD6aGYuf9y
+xBWmOZobNTmGw9bnQhePai/xHUoJgs8KWN5e8pBJXAouL12pqRC4sQCIVc9R3J+7
+djTPTco0oexkUsbe1rr4hCqoR9iUePxl7wY5BYECgYEA8nA6fV+HKoDINlEnR4yg
+Aza4PdjQG3Pa11AIoEAP/Hq3RwoMNqRpx1J2ZIZWHSeTGh9CCCY297Ig8XDlfntR
+P8qfRjEugovVOl00Qk7Rt378JRxzC0K4dhm4O73t85T4K9PyoI7ouyhT964ZHDro
+YqJxFq4ugjiF0MJ3BDI5ZrECgYAItf9MZNftq/h2FAPs0ECoG5vXvGpNuYd6DKWA
+TLZRnCyJrO+WZGUsJ9x5j7CPOYUmKjeSjksynqy6LXTWVj8m5RiM4tdULyZA0KFq
+02FubAt0s1bc7tBJGN63qohhFbJRkBul4C2ZC3BOBcdlDEBxURUX9zRDC016F+cF
+NEdvAQKBgQCtJhaEt1dzbB0qYEZz7kYr3QP1hz/6XfnAd8wmJNQQQ8yctNlJc+dH
+Foo3qwU2uJWFbByBxIvkmCDTAOCJwOONP/MhigvDQth/sa+TbY6y39XywL0TyG/p
+X19RWD9TYNG7C9y9Z8Jcz+u9iCd5enqLYw7Qle9gq3jwK1GXsV5zEQ==
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4096 (0x1000)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=B CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:81:74:90:36:7b:35:df:f3:ea:18:ca:b9:64:
+ a8:58:85:90:2a:de:22:be:9a:0b:97:bd:fe:61:98:
+ 2a:e5:2d:cf:9e:bc:0c:23:5e:21:4f:19:ad:b5:3e:
+ 9c:d4:aa:05:5a:71:4e:b7:99:81:a7:6b:35:09:22:
+ 43:84:be:e9:71:c6:70:3b:6f:88:c4:2b:ba:0a:b8:
+ c1:7c:42:be:3f:d8:0b:88:87:53:82:7b:a3:72:d4:
+ 42:53:f3:7d:cb:84:50:60:34:1c:86:65:36:fb:e6:
+ 47:4f:61:60:c3:9b:7b:4a:57:6e:2c:ac:90:ee:47:
+ 0d:b2:92:e2:bb:e8:7c:e2:74:3e:72:8f:02:91:41:
+ e8:2e:60:1a:80:83:76:b8:87:fd:f3:5f:04:dd:56:
+ 97:f3:11:04:70:9e:48:e2:96:e5:66:a3:85:dd:b2:
+ f6:91:1e:e8:c3:a6:79:94:b7:e4:6e:25:38:27:a3:
+ 79:99:9c:53:ba:8b:c9:cf:d6:5e:e0:c0:c6:05:9a:
+ 60:46:73:25:56:a4:02:3c:dc:36:d5:60:12:0f:c9:
+ f9:4e:c0:1e:2b:74:60:ef:03:30:8a:53:7e:c8:0a:
+ eb:2b:9b:db:a3:63:03:85:b8:ba:ca:fd:93:fd:f3:
+ 8f:87:97:11:24:d5:11:f3:50:ac:ba:1f:9d:d7:e4:
+ 30:7d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 2C:58:4E:D7:58:62:F8:D6:BF:BE:80:5C:04:3A:39:B1:41:8C:D3:D7
+ X509v3 Authority Key Identifier:
+ keyid:CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Signature Algorithm: sha1WithRSAEncryption
+ 0d:09:9b:f0:11:81:5a:5d:f1:85:73:fb:f2:c8:bc:82:99:27:
+ fd:58:c7:6a:d6:28:f1:09:63:4e:61:fd:68:3a:23:0a:10:fe:
+ 58:7b:20:a3:7a:69:e6:74:55:5c:14:4f:ec:d7:e2:10:aa:8b:
+ a6:3f:99:c1:ef:bb:48:a9:42:b8:9a:8e:65:75:7b:cc:5c:bd:
+ e4:c0:63:e2:d1:6b:38:32:5f:33:b3:fc:0c:33:b0:8b:dd:10:
+ 54:15:99:2d:c4:62:68:3f:af:3f:e2:d9:52:33:d4:31:4c:ed:
+ c7:28:76:bb:21:6b:b9:41:fb:88:40:52:e1:52:76:31:af:d8:
+ a6:63:d1:d9:a5:e4:95:39:74:a8:73:f5:b0:c4:4e:bf:ef:a1:
+ 87:2e:94:a0:bd:cc:e3:90:45:5c:5a:18:15:0d:09:47:45:0f:
+ 1e:d7:cf:d3:8d:c6:b0:54:c4:5f:21:50:5c:b5:0d:eb:c3:15:
+ 3d:ee:88:fa:b4:80:93:4d:2e:00:9e:46:b6:fb:e8:64:32:51:
+ 8a:35:81:7a:d3:71:73:8d:2a:27:d4:57:2f:66:4c:fc:99:81:
+ 06:66:1f:5b:e6:a4:e5:35:eb:f3:e5:e3:f3:31:78:fe:b0:03:
+ f9:5d:77:97:70:d7:53:52:50:df:15:5b:3e:fc:7e:9a:1f:6a:
+ ce:c2:37:2f
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMGAxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAw
+DgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9gXSQNns13/PqGMq5ZKhYhZAq3iK+mguXvf5h
+mCrlLc+evAwjXiFPGa21PpzUqgVacU63mYGnazUJIkOEvulxxnA7b4jEK7oKuMF8
+Qr4/2AuIh1OCe6Ny1EJT833LhFBgNByGZTb75kdPYWDDm3tKV24srJDuRw2ykuK7
+6HzidD5yjwKRQeguYBqAg3a4h/3zXwTdVpfzEQRwnkjiluVmo4XdsvaRHujDpnmU
+t+RuJTgno3mZnFO6i8nP1l7gwMYFmmBGcyVWpAI83DbVYBIPyflOwB4rdGDvAzCK
+U37ICusrm9ujYwOFuLrK/ZP984+HlxEk1RHzUKy6H53X5DB9AgMBAAGjbzBtMAwG
+A1UdEwEB/wQCMAAwHQYDVR0OBBYEFCxYTtdYYvjWv76AXAQ6ObFBjNPXMB8GA1Ud
+IwQYMBaAFM0YJ0+IsSaFuY+cAKT4Gv0Bhl7sMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+BggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEADQmb8BGBWl3xhXP78si8gpkn
+/VjHatYo8QljTmH9aDojChD+WHsgo3pp5nRVXBRP7NfiEKqLpj+Zwe+7SKlCuJqO
+ZXV7zFy95MBj4tFrODJfM7P8DDOwi90QVBWZLcRiaD+vP+LZUjPUMUztxyh2uyFr
+uUH7iEBS4VJ2Ma/YpmPR2aXklTl0qHP1sMROv++hhy6UoL3M45BFXFoYFQ0JR0UP
+HtfP043GsFTEXyFQXLUN68MVPe6I+rSAk00uAJ5GtvvoZDJRijWBetNxc40qJ9RX
+L2ZM/JmBBmYfW+ak5TXr8+Xj8zF4/rAD+V13l3DXU1JQ3xVbPvx+mh9qzsI3Lw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4097 (0x1001)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=C CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=B CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ab:18:b1:0e:c2:f9:61:1a:13:c4:95:b8:65:d8:
+ cb:36:2f:72:e8:e1:97:44:1d:0d:55:a0:cc:02:12:
+ 04:a4:78:f6:df:c3:6f:c9:b8:50:15:d8:5c:21:ed:
+ 8f:c4:e7:db:81:da:fe:01:60:39:63:99:e4:04:dc:
+ 15:ea:59:6b:35:da:1a:e3:3e:dd:f0:71:8f:64:79:
+ 1a:30:b1:be:5a:43:9c:88:1a:a0:26:2f:5d:6c:b6:
+ 8b:b8:6d:10:b0:97:f4:a1:d3:7b:56:3d:dd:12:19:
+ a3:5e:02:24:9e:19:8e:d2:0b:e2:ea:8e:a2:cc:a1:
+ 8e:f0:49:e1:81:ae:3a:a6:71:d5:e8:e4:80:27:01:
+ 58:66:8c:bd:a0:4f:08:01:59:5c:c3:e3:d5:6f:49:
+ 73:24:66:f5:25:b3:9e:a1:29:21:de:e9:d5:ad:b0:
+ 1d:fc:b7:4c:f7:5a:9a:2b:5a:2c:af:07:aa:c2:82:
+ 5a:36:06:1d:27:2d:90:c7:45:1e:7b:f4:7a:8a:fe:
+ 90:c1:79:c9:8f:4e:67:52:48:ea:0b:dd:d7:fe:84:
+ 54:47:2f:d9:d0:ca:11:07:59:b0:90:08:0b:76:a2:
+ ec:30:a5:45:aa:d7:61:39:84:43:33:97:22:b6:45:
+ c8:e8:ab:73:5f:79:a8:13:55:2f:71:a2:c9:21:aa:
+ 9b:df
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ CD:18:27:4F:88:B1:26:85:B9:8F:9C:00:A4:F8:1A:FD:01:86:5E:EC
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 00:80:9b:99:4b:81:b1:76:49:5c:f2:99:ef:88:93:18:75:68:
+ eb:26:9e:87:95:b6:91:23:c0:b5:78:58:96:97:9c:27:38:fa:
+ f3:e5:c3:a2:34:40:91:6d:45:2c:7f:63:51:98:5b:53:4b:91:
+ ce:0f:e2:32:63:e7:e5:f9:21:6b:b7:f9:94:ac:33:13:5c:27:
+ 9b:98:e7:7f:44:50:29:98:68:81:53:6b:a1:43:d8:04:40:cb:
+ f2:cf:18:39:a5:24:c9:88:b6:b7:76:cb:dd:18:2e:1d:24:3e:
+ d3:59:e9:04:48:9d:59:0c:0f:d0:79:6a:93:7d:2b:b9:8e:50:
+ 34:00:0d:48:ae:10:bd:80:04:74:da:06:27:15:ad:88:ec:36:
+ 61:51:80:fe:6b:1d:46:37:0e:ea:23:60:c1:79:bf:03:2a:d2:
+ 80:9e:2c:10:3a:bc:2d:50:6e:f7:f9:d5:ec:11:d9:b4:62:bc:
+ 09:2c:05:31:06:bf:b9:e1:d5:1e:02:a9:1a:c5:c4:13:bf:b7:
+ 8e:6a:08:51:57:af:db:7b:09:74:bd:c7:bd:3c:de:0a:51:8a:
+ fe:82:0b:4b:34:74:10:4b:4b:34:fd:42:28:48:10:db:5d:6d:
+ 64:80:b1:3c:5c:04:86:32:6c:25:87:db:23:dc:e4:42:e4:71:
+ f9:b1:88:74
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcSgAwIBAgICEAEwDQYJKoZIhvcNAQEFBQAwDzENMAsGA1UEAwwEQyBD
+QTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMA8xDTALBgNVBAMMBEIg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrGLEOwvlhGhPElbhl
+2Ms2L3Lo4ZdEHQ1VoMwCEgSkePbfw2/JuFAV2Fwh7Y/E59uB2v4BYDljmeQE3BXq
+WWs12hrjPt3wcY9keRowsb5aQ5yIGqAmL11stou4bRCwl/Sh03tWPd0SGaNeAiSe
+GY7SC+LqjqLMoY7wSeGBrjqmcdXo5IAnAVhmjL2gTwgBWVzD49VvSXMkZvUls56h
+KSHe6dWtsB38t0z3WporWiyvB6rCglo2Bh0nLZDHRR579HqK/pDBecmPTmdSSOoL
+3df+hFRHL9nQyhEHWbCQCAt2ouwwpUWq12E5hEMzlyK2Rcjoq3NfeagTVS9xoskh
+qpvfAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM0YJ0+IsSaF
+uY+cAKT4Gv0Bhl7sMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEA
+AICbmUuBsXZJXPKZ74iTGHVo6yaeh5W2kSPAtXhYlpecJzj68+XDojRAkW1FLH9j
+UZhbU0uRzg/iMmPn5fkha7f5lKwzE1wnm5jnf0RQKZhogVNroUPYBEDL8s8YOaUk
+yYi2t3bL3RguHSQ+01npBEidWQwP0Hlqk30ruY5QNAANSK4QvYAEdNoGJxWtiOw2
+YVGA/msdRjcO6iNgwXm/AyrSgJ4sEDq8LVBu9/nV7BHZtGK8CSwFMQa/ueHVHgKp
+GsXEE7+3jmoIUVev23sJdL3HvTzeClGK/oILSzR0EEtLNP1CKEgQ211tZICxPFwE
+hjJsJYfbI9zkQuRx+bGIdA==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4100 (0x1004)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=E Root CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=C CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ca:21:76:fd:8b:0f:24:ba:dc:ab:cd:0e:31:94:
+ 01:90:57:79:18:3c:58:61:f5:7e:b7:2f:e6:46:d3:
+ 41:e8:4e:29:29:a8:9d:eb:d6:df:69:d5:b1:10:de:
+ 6b:17:2d:8d:8a:1c:d7:dc:80:85:74:c2:7a:6e:a6:
+ 75:7a:2a:76:42:fb:65:6b:8c:a9:2f:c0:5e:76:1e:
+ bc:35:85:b2:4b:35:a4:97:33:15:76:7d:4f:6e:d0:
+ 3b:45:04:fe:dc:a0:11:67:15:2d:3a:c3:07:c6:db:
+ f2:89:25:92:5e:db:70:bd:88:e9:f0:c7:54:6f:8e:
+ ab:cf:ce:ca:ad:bb:44:72:bf:e9:b5:ab:ba:68:15:
+ 6d:1e:e1:66:d6:60:d8:bd:dc:ab:d3:e8:2f:4c:ee:
+ 29:46:36:c7:b1:61:af:20:19:cc:98:1c:78:5f:4d:
+ 97:7a:de:2f:d9:fd:f0:b8:47:34:ff:ed:73:07:eb:
+ 90:54:11:e2:1b:8e:68:5a:c1:72:a9:af:df:e9:f1:
+ f5:ca:0e:72:03:90:1b:af:64:d6:ee:ce:67:57:1b:
+ fb:c7:f1:c2:5c:97:81:cd:d6:22:7c:26:bf:cd:6a:
+ b9:99:5f:58:63:5f:ce:05:1c:7d:f1:a9:d3:f8:4c:
+ fe:10:82:a4:14:2c:67:97:6c:82:2f:98:38:83:50:
+ bf:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ F5:87:AF:4C:B3:4A:88:7F:EB:92:D3:C7:28:78:91:D9:02:4A:71:DF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 8c:05:c2:e8:03:57:4c:17:bd:69:1c:2f:3a:3e:9a:c5:11:2d:
+ 94:c1:38:4d:f1:a8:aa:29:9c:40:d6:99:63:79:8a:96:60:be:
+ 5a:ba:54:8b:db:a6:f0:66:ae:f1:48:54:2d:a4:9f:ec:ba:e3:
+ 75:77:c0:54:1d:ce:b1:87:74:61:91:19:14:28:74:b0:ca:7a:
+ a0:fe:da:eb:26:be:86:cc:db:65:c4:c8:17:fb:9f:78:c3:df:
+ 39:f5:4f:77:5c:05:49:b5:fa:c5:9e:04:35:4f:ff:72:73:6d:
+ 8a:31:70:83:3b:19:c2:14:09:9a:2f:f2:ea:cb:13:b4:fa:56:
+ d2:a9:34:88:56:9c:f2:3e:22:46:56:ad:62:05:20:3b:ed:2e:
+ 6d:c5:02:de:da:53:8f:79:6c:42:29:c4:b6:5b:9a:31:d2:9b:
+ 07:3d:62:22:fa:c9:7c:f6:96:e4:a3:f2:5f:46:65:35:ba:af:
+ 34:9b:2d:30:eb:de:ac:6f:b3:af:e6:7f:10:80:8d:f6:eb:0a:
+ 03:74:75:8f:ae:e1:13:70:4a:4a:55:e1:f0:f3:69:ff:7e:fb:
+ 76:7b:5e:ae:ad:5e:59:6c:af:35:e9:08:f6:16:fe:3c:85:e3:
+ 91:97:0c:23:63:d5:07:40:23:23:eb:20:a8:e1:05:e3:ae:44:
+ 9f:38:f1:61
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgICEAQwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJRSBS
+b290IENBMB4XDTE0MDIwNDA0MjU1OFoXDTI0MDIwMjA0MjU1OFowDzENMAsGA1UE
+AwwEQyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMohdv2LDyS6
+3KvNDjGUAZBXeRg8WGH1frcv5kbTQehOKSmonevW32nVsRDeaxctjYoc19yAhXTC
+em6mdXoqdkL7ZWuMqS/AXnYevDWFsks1pJczFXZ9T27QO0UE/tygEWcVLTrDB8bb
+8oklkl7bcL2I6fDHVG+Oq8/Oyq27RHK/6bWrumgVbR7hZtZg2L3cq9PoL0zuKUY2
+x7FhryAZzJgceF9Nl3reL9n98LhHNP/tcwfrkFQR4huOaFrBcqmv3+nx9coOcgOQ
+G69k1u7OZ1cb+8fxwlyXgc3WInwmv81quZlfWGNfzgUcffGp0/hM/hCCpBQsZ5ds
+gi+YOINQv3ECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU9Yev
+TLNKiH/rktPHKHiR2QJKcd8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQCMBcLoA1dMF71pHC86PprFES2UwThN8aiqKZxA1pljeYqWYL5aulSL26bw
+Zq7xSFQtpJ/suuN1d8BUHc6xh3RhkRkUKHSwynqg/trrJr6GzNtlxMgX+594w985
+9U93XAVJtfrFngQ1T/9yc22KMXCDOxnCFAmaL/LqyxO0+lbSqTSIVpzyPiJGVq1i
+BSA77S5txQLe2lOPeWxCKcS2W5ox0psHPWIi+sl89pbko/JfRmU1uq80my0w696s
+b7Ov5n8QgI326woDdHWPruETcEpKVeHw82n/fvt2e16urV5ZbK816Qj2Fv48heOR
+lwwjY9UHQCMj6yCo4QXjrkSfOPFh
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 15337932515437853935 (0xd4db485be3963cef)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=E Root CA
+ Validity
+ Not Before: Feb 4 04:25:58 2014 GMT
+ Not After : Feb 2 04:25:58 2024 GMT
+ Subject: CN=E Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a4:0f:61:93:2e:cc:e7:2f:fa:30:d2:6a:ca:eb:
+ 21:bb:f8:26:55:f3:70:42:c0:48:ca:51:2b:11:20:
+ 32:16:86:38:8f:d3:fe:88:5a:a4:81:3b:77:1f:82:
+ e0:26:08:d1:8f:d6:03:98:2d:21:eb:d0:bb:f2:fc:
+ 25:83:98:95:e9:d2:cc:a6:4e:0d:fb:70:6a:0c:33:
+ ca:bb:a1:d4:27:30:69:f7:0c:86:e7:82:26:56:c4:
+ 95:aa:f5:1f:50:8c:95:bf:9a:2d:40:a8:d2:9d:2b:
+ 78:b0:87:a9:20:b1:73:e1:99:64:cd:22:81:72:76:
+ d0:fa:46:80:0a:45:50:c5:c1:92:8b:f4:1d:e4:73:
+ 74:90:53:7f:6c:43:6a:9b:e0:8d:87:96:85:8c:86:
+ 72:21:8c:17:78:ab:b7:fd:1a:ad:9f:37:03:53:4f:
+ 94:66:5d:99:fa:cd:d8:c9:0b:0b:dc:83:5f:c8:40:
+ 9d:50:e8:aa:de:a1:35:8a:ea:e0:26:10:cc:c0:9d:
+ 0c:aa:23:df:68:b3:7e:e3:55:e4:91:b2:f5:97:15:
+ 22:6a:71:5e:83:19:41:cb:99:8e:84:33:cd:15:7f:
+ a1:90:42:5b:ab:f6:40:b8:67:97:d7:65:0f:d3:a5:
+ 0a:96:cd:9c:aa:fc:57:87:6b:54:ed:4f:b1:4c:01:
+ 81:cd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ B1:D9:42:46:8A:51:56:85:13:A1:BF:84:84:20:40:7A:ED:54:DC:9B
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 44:46:db:03:28:bd:b3:32:9d:48:cb:5f:dc:2d:3f:5e:1a:52:
+ a8:61:ef:fb:97:7a:0e:a9:a9:83:5c:1c:7e:b9:e4:7a:12:57:
+ d0:5b:8f:c1:34:b8:5a:7f:ee:d0:c5:c9:b5:a3:68:0d:ce:68:
+ 78:08:44:f7:0f:50:bc:c8:87:15:f6:79:5a:1e:21:cc:6d:c9:
+ 31:70:ad:58:fc:9d:71:d1:2f:25:eb:e1:b6:b8:50:d0:90:28:
+ f2:48:c7:9f:f2:a4:63:ba:13:03:05:60:41:93:4e:ab:02:0f:
+ 27:ae:e9:51:f2:45:ba:47:4c:f6:17:82:24:39:a7:5d:c3:eb:
+ 2d:47:fb:14:06:f8:10:67:e4:98:aa:57:68:9e:f3:5a:b1:82:
+ 24:e6:dc:0f:cc:53:83:a5:a3:53:9f:d0:31:76:21:50:d0:6c:
+ af:d5:19:4e:41:b9:d1:b1:eb:bf:02:34:c5:ab:b8:d6:5e:33:
+ 13:26:e6:82:42:01:94:77:3e:94:35:5e:d5:73:16:7d:fb:a6:
+ 2d:a7:b8:8a:b4:49:56:d5:7b:5f:ec:d0:fe:ca:7a:78:56:ea:
+ 1c:a7:61:94:30:a8:92:32:50:40:47:8f:0d:be:f9:96:e4:0a:
+ 6f:29:7b:41:7d:87:2a:be:b8:42:17:cc:4a:42:ec:c3:1b:2e:
+ b6:7f:17:54
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIJANTbSFvjljzvMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
+BAMMCUUgUm9vdCBDQTAeFw0xNDAyMDQwNDI1NThaFw0yNDAyMDIwNDI1NThaMBQx
+EjAQBgNVBAMMCUUgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKQPYZMuzOcv+jDSasrrIbv4JlXzcELASMpRKxEgMhaGOI/T/ohapIE7dx+C
+4CYI0Y/WA5gtIevQu/L8JYOYlenSzKZODftwagwzyruh1CcwafcMhueCJlbElar1
+H1CMlb+aLUCo0p0reLCHqSCxc+GZZM0igXJ20PpGgApFUMXBkov0HeRzdJBTf2xD
+apvgjYeWhYyGciGMF3irt/0arZ83A1NPlGZdmfrN2MkLC9yDX8hAnVDoqt6hNYrq
+4CYQzMCdDKoj32izfuNV5JGy9ZcVImpxXoMZQcuZjoQzzRV/oZBCW6v2QLhnl9dl
+D9OlCpbNnKr8V4drVO1PsUwBgc0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUsdlCRopRVoUTob+EhCBAeu1U3JswDgYDVR0PAQH/BAQDAgEGMA0G
+CSqGSIb3DQEBBQUAA4IBAQBERtsDKL2zMp1Iy1/cLT9eGlKoYe/7l3oOqamDXBx+
+ueR6ElfQW4/BNLhaf+7Qxcm1o2gNzmh4CET3D1C8yIcV9nlaHiHMbckxcK1Y/J1x
+0S8l6+G2uFDQkCjySMef8qRjuhMDBWBBk06rAg8nrulR8kW6R0z2F4IkOaddw+st
+R/sUBvgQZ+SYqldonvNasYIk5twPzFODpaNTn9AxdiFQ0Gyv1RlOQbnRseu/AjTF
+q7jWXjMTJuaCQgGUdz6UNV7VcxZ9+6Ytp7iKtElW1Xtf7ND+ynp4Vuocp2GUMKiS
+MlBAR48NvvmW5ApvKXtBfYcqvrhCF8xKQuzDGy62fxdU
+-----END CERTIFICATE-----
diff --git a/net/data/ssl/scripts/generate-multi-root-test-chains.sh b/net/data/ssl/scripts/generate-multi-root-test-chains.sh
new file mode 100755
index 0000000..6f88325
--- /dev/null
+++ b/net/data/ssl/scripts/generate-multi-root-test-chains.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script generates two chains of test certificates:
+#
+# 1. A (end-entity) -> B -> C -> D (self-signed root)
+# 2. A (end-entity) -> B -> C2 -> E (self-signed root)
+#
+# C and C2 have the same subject and keypair.
+#
+# We use these cert chains in CertVerifyProcChromeOSTest
+# to ensure that multiple verification paths are properly handled.
+
+try () {
+ echo "$@"
+ "$@" || exit 1
+}
+
+try rm -rf out
+try mkdir out
+
+echo Create the serial number files.
+serial=1000
+for i in B C C2 D E
+do
+ try /bin/sh -c "echo $serial > out/$i-serial"
+ serial=$(expr $serial + 1)
+done
+
+echo Generate the keys.
+try openssl genrsa -out out/A.key 2048
+try openssl genrsa -out out/B.key 2048
+try openssl genrsa -out out/C.key 2048
+try openssl genrsa -out out/D.key 2048
+try openssl genrsa -out out/E.key 2048
+
+echo Generate the D CSR.
+CA_COMMON_NAME="D Root CA" \
+ CERTIFICATE=D \
+ try openssl req \
+ -new \
+ -key out/D.key \
+ -out out/D.csr \
+ -config redundant-ca.cnf
+
+echo D signs itself.
+CA_COMMON_NAME="D Root CA" \
+ try openssl x509 \
+ -req -days 3650 \
+ -in out/D.csr \
+ -extensions ca_cert \
+ -extfile redundant-ca.cnf \
+ -signkey out/D.key \
+ -out out/D.pem \
+ -text
+
+echo Generate the E CSR.
+CA_COMMON_NAME="E Root CA" \
+ CERTIFICATE=E \
+ try openssl req \
+ -new \
+ -key out/E.key \
+ -out out/E.csr \
+ -config redundant-ca.cnf
+
+echo E signs itself.
+CA_COMMON_NAME="E Root CA" \
+ try openssl x509 \
+ -req -days 3650 \
+ -in out/E.csr \
+ -extensions ca_cert \
+ -extfile redundant-ca.cnf \
+ -signkey out/E.key \
+ -out out/E.pem \
+ -text
+
+echo Generate the C2 intermediary CSR.
+CA_COMMON_NAME="C CA" \
+ CERTIFICATE=C2 \
+ try openssl req \
+ -new \
+ -key out/C.key \
+ -out out/C2.csr \
+ -config redundant-ca.cnf
+
+echo Generate the B and C intermediaries\' CSRs.
+for i in B C
+do
+ CA_COMMON_NAME="$i CA" \
+ CERTIFICATE="$i" \
+ try openssl req \
+ -new \
+ -key "out/$i.key" \
+ -out "out/$i.csr" \
+ -config redundant-ca.cnf
+done
+
+echo D signs the C intermediate.
+# Make sure the signer's DB file exists.
+touch out/D-index.txt
+CA_COMMON_NAME="D Root CA" \
+ CERTIFICATE=D \
+ try openssl ca \
+ -batch \
+ -extensions ca_cert \
+ -in out/C.csr \
+ -out out/C.pem \
+ -config redundant-ca.cnf
+
+echo E signs the C2 intermediate.
+# Make sure the signer's DB file exists.
+touch out/E-index.txt
+CA_COMMON_NAME="E Root CA" \
+ CERTIFICATE=E \
+ try openssl ca \
+ -batch \
+ -extensions ca_cert \
+ -in out/C2.csr \
+ -out out/C2.pem \
+ -config redundant-ca.cnf
+
+echo C signs the B intermediate.
+touch out/C-index.txt
+CA_COMMON_NAME="C CA" \
+ CERTIFICATE=C \
+ try openssl ca \
+ -batch \
+ -extensions ca_cert \
+ -in out/B.csr \
+ -out out/B.pem \
+ -config redundant-ca.cnf
+
+echo Generate the A end-entity CSR.
+try openssl req \
+ -new \
+ -key out/A.key \
+ -out out/A.csr \
+ -config ee.cnf
+
+echo B signs A.
+touch out/B-index.txt
+CA_COMMON_NAME="B CA" \
+ CERTIFICATE=B \
+ try openssl ca \
+ -batch \
+ -extensions user_cert \
+ -in out/A.csr \
+ -out out/A.pem \
+ -config redundant-ca.cnf
+
+echo Create multi-root-chain1.pem
+try /bin/sh -c "cat out/A.key out/A.pem out/B.pem out/C.pem out/D.pem \
+ > ../certificates/multi-root-chain1.pem"
+
+echo Create multi-root-chain2.pem
+try /bin/sh -c "cat out/A.key out/A.pem out/B.pem out/C2.pem out/E.pem \
+ > ../certificates/multi-root-chain2.pem"
+
diff --git a/net/ssl/client_cert_store_chromeos_unittest.cc b/net/ssl/client_cert_store_chromeos_unittest.cc
index 73e0326..de0358d 100644
--- a/net/ssl/client_cert_store_chromeos_unittest.cc
+++ b/net/ssl/client_cert_store_chromeos_unittest.cc
@@ -37,10 +37,6 @@ class ClientCertStoreChromeOSTestDelegate {
ClientCertStoreChromeOS store_;
};
-INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS,
- ClientCertStoreTest,
- ClientCertStoreChromeOSTestDelegate);
-
class ClientCertStoreChromeOSTest : public ::testing::Test {
public:
scoped_refptr<X509Certificate> ImportCertForUser(