diff options
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/cert_verifier.cc | 4 | ||||
-rw-r--r-- | net/base/cert_verifier_unittest.cc | 67 | ||||
-rw-r--r-- | net/base/x509_certificate.cc | 3 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 12 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 25 | ||||
-rw-r--r-- | net/base/x509_certificate_nss.cc | 21 | ||||
-rw-r--r-- | net/base/x509_certificate_openssl.cc | 22 | ||||
-rw-r--r-- | net/base/x509_certificate_unittest.cc | 41 | ||||
-rw-r--r-- | net/base/x509_certificate_win.cc | 28 |
9 files changed, 220 insertions, 3 deletions
diff --git a/net/base/cert_verifier.cc b/net/base/cert_verifier.cc index 90a728c..1a80b40 100644 --- a/net/base/cert_verifier.cc +++ b/net/base/cert_verifier.cc @@ -364,7 +364,7 @@ int CertVerifier::Verify(X509Certificate* cert, requests_++; - const RequestParams key = {cert->fingerprint(), hostname, flags}; + const RequestParams key = {cert->chain_fingerprint(), hostname, flags}; // First check the cache. std::map<RequestParams, CachedCertVerifyResult>::iterator i; i = cache_.find(key); @@ -449,7 +449,7 @@ void CertVerifier::HandleResult(X509Certificate* cert, uint32 ttl = kTTLSecs; cached_result.expiry = current_time + base::TimeDelta::FromSeconds(ttl); - const RequestParams key = {cert->fingerprint(), hostname, flags}; + const RequestParams key = {cert->chain_fingerprint(), hostname, flags}; DCHECK_GE(max_cache_entries_, 1u); DCHECK_LE(cache_.size(), max_cache_entries_); diff --git a/net/base/cert_verifier_unittest.cc b/net/base/cert_verifier_unittest.cc index 8a026fb..f4c7dcd 100644 --- a/net/base/cert_verifier_unittest.cc +++ b/net/base/cert_verifier_unittest.cc @@ -59,6 +59,7 @@ TEST(CertVerifierTest, CacheHit) { ASSERT_EQ(1u, verifier.requests()); ASSERT_EQ(0u, verifier.cache_hits()); ASSERT_EQ(0u, verifier.inflight_joins()); + ASSERT_EQ(1u, verifier.GetCacheSize()); error = verifier.Verify(test_cert, "www.example.com", 0, NULL, &verify_result, callback.callback(), &request_handle, BoundNetLog()); @@ -69,6 +70,72 @@ TEST(CertVerifierTest, CacheHit) { ASSERT_EQ(2u, verifier.requests()); ASSERT_EQ(1u, verifier.cache_hits()); ASSERT_EQ(0u, verifier.inflight_joins()); + ASSERT_EQ(1u, verifier.GetCacheSize()); +} + +// Tests the same server certificate with different intermediate CA +// certificates. These should be treated as different certificate chains even +// though the two X509Certificate objects contain the same server certificate. +TEST(CertVerifierTest, DifferentCACerts) { + TestTimeService* time_service = new TestTimeService; + base::Time current_time = base::Time::Now(); + time_service->set_current_time(current_time); + CertVerifier verifier(time_service); + + FilePath certs_dir = GetTestCertsDirectory(); + + scoped_refptr<X509Certificate> server_cert = + ImportCertFromFile(certs_dir, "salesforce_com_test.pem"); + ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert); + + scoped_refptr<X509Certificate> intermediate_cert1 = + ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem"); + ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert1); + + scoped_refptr<X509Certificate> intermediate_cert2 = + ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem"); + ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert2); + + X509Certificate::OSCertHandles intermediates; + intermediates.push_back(intermediate_cert1->os_cert_handle()); + scoped_refptr<X509Certificate> cert_chain1 = + X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), + intermediates); + + intermediates.clear(); + intermediates.push_back(intermediate_cert2->os_cert_handle()); + scoped_refptr<X509Certificate> cert_chain2 = + X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), + intermediates); + + int error; + CertVerifyResult verify_result; + TestCompletionCallback callback; + CertVerifier::RequestHandle request_handle; + + error = verifier.Verify(cert_chain1, "www.example.com", 0, NULL, + &verify_result, callback.callback(), + &request_handle, BoundNetLog()); + ASSERT_EQ(ERR_IO_PENDING, error); + ASSERT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + ASSERT_TRUE(IsCertificateError(error)); + ASSERT_EQ(1u, verifier.requests()); + ASSERT_EQ(0u, verifier.cache_hits()); + ASSERT_EQ(0u, verifier.inflight_joins()); + ASSERT_EQ(1u, verifier.GetCacheSize()); + + error = verifier.Verify(cert_chain2, "www.example.com", 0, NULL, + &verify_result, callback.callback(), + &request_handle, BoundNetLog()); + ASSERT_EQ(ERR_IO_PENDING, error); + ASSERT_TRUE(request_handle != NULL); + error = callback.WaitForResult(); + ASSERT_TRUE(IsCertificateError(error)); + ASSERT_EQ(2u, verifier.requests()); + ASSERT_EQ(0u, verifier.cache_hits()); + ASSERT_EQ(0u, verifier.inflight_joins()); + ASSERT_EQ(2u, verifier.GetCacheSize()); } // Tests an inflight join. diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc index 5004bf4..d36bfad 100644 --- a/net/base/x509_certificate.cc +++ b/net/base/x509_certificate.cc @@ -232,7 +232,7 @@ bool X509Certificate::LessThan::operator()(X509Certificate* lhs, return false; SHA1FingerprintLessThan fingerprint_functor; - return fingerprint_functor(lhs->fingerprint_, rhs->fingerprint_); + return fingerprint_functor(lhs->chain_fingerprint_, rhs->chain_fingerprint_); } X509Certificate::X509Certificate(const std::string& subject, @@ -245,6 +245,7 @@ X509Certificate::X509Certificate(const std::string& subject, valid_expiry_(expiration_date), cert_handle_(NULL) { memset(fingerprint_.data, 0, sizeof(fingerprint_.data)); + memset(chain_fingerprint_.data, 0, sizeof(chain_fingerprint_.data)); } // static diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index b4677d2..aee2447 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -210,6 +210,11 @@ class NET_EXPORT X509Certificate // The fingerprint of this certificate. const SHA1Fingerprint& fingerprint() const { return fingerprint_; } + // The fingerprint of this certificate and its intermediate CA certificates. + const SHA1Fingerprint& chain_fingerprint() const { + return chain_fingerprint_; + } + // Gets the DNS names in the certificate. Pursuant to RFC 2818, Section 3.1 // Server Identity, if the certificate has a subjectAltName extension of // type dNSName, this method gets the DNS names in that extension. @@ -365,6 +370,10 @@ class NET_EXPORT X509Certificate // (all zero) fingerprint on failure. static SHA1Fingerprint CalculateFingerprint(OSCertHandle cert_handle); + // Calculates the SHA-1 fingerprint of the certificate and its intermediate + // CA certificates. Returns an empty (all zero) fingerprint on failure. + SHA1Fingerprint CalculateChainFingerprint() const; + private: friend class base::RefCountedThreadSafe<X509Certificate>; friend class TestRootCerts; // For unit tests @@ -472,6 +481,9 @@ class NET_EXPORT X509Certificate // The fingerprint of this certificate. SHA1Fingerprint fingerprint_; + // The fingerprint of this certificate and its intermediate CA certificates. + SHA1Fingerprint chain_fingerprint_; + // The serial number of this certificate, DER encoded. std::string serial_number_; diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index dc7d4e9..0b65c77 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -540,6 +540,7 @@ void X509Certificate::Initialize() { &valid_expiry_); fingerprint_ = CalculateFingerprint(cert_handle_); + chain_fingerprint_ = CalculateChainFingerprint(); serial_number_ = GetCertSerialNumber(cert_handle_); } @@ -1069,6 +1070,30 @@ SHA1Fingerprint X509Certificate::CalculateFingerprint( return sha1; } +SHA1Fingerprint X509Certificate::CalculateChainFingerprint() const { + SHA1Fingerprint sha1; + memset(sha1.data, 0, sizeof(sha1.data)); + + // The CC_SHA(3cc) man page says all CC_SHA1_xxx routines return 1, so + // we don't check their return values. + CC_SHA1_CTX sha1_ctx; + CC_SHA1_Init(&sha1_ctx); + CSSM_DATA cert_data; + OSStatus status = SecCertificateGetData(cert_handle_, &cert_data); + if (status) + return sha1; + CC_SHA1_Update(&sha1_ctx, cert_data.Data, cert_data.Length); + for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { + status = SecCertificateGetData(intermediate_ca_certs_[i], &cert_data); + if (status) + return sha1; + CC_SHA1_Update(&sha1_ctx, cert_data.Data, cert_data.Length); + } + CC_SHA1_Final(sha1.data, &sha1_ctx); + + return sha1; +} + bool X509Certificate::SupportsSSLClientAuth() const { CSSMFields fields; if (GetCertFields(cert_handle_, &fields) != noErr) diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc index 2ca31ba..4a8a4e0 100644 --- a/net/base/x509_certificate_nss.cc +++ b/net/base/x509_certificate_nss.cc @@ -677,6 +677,7 @@ void X509Certificate::Initialize() { ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_); fingerprint_ = CalculateFingerprint(cert_handle_); + chain_fingerprint_ = CalculateChainFingerprint(); serial_number_ = std::string( reinterpret_cast<char*>(cert_handle_->serialNumber.data), @@ -1005,6 +1006,26 @@ SHA1Fingerprint X509Certificate::CalculateFingerprint( return sha1; } +SHA1Fingerprint X509Certificate::CalculateChainFingerprint() const { + SHA1Fingerprint sha1; + memset(sha1.data, 0, sizeof(sha1.data)); + + HASHContext* sha1_ctx = HASH_Create(HASH_AlgSHA1); + if (!sha1_ctx) + return sha1; + HASH_Begin(sha1_ctx); + HASH_Update(sha1_ctx, cert_handle_->derCert.data, cert_handle_->derCert.len); + for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { + CERTCertificate* ca_cert = intermediate_ca_certs_[i]; + HASH_Update(sha1_ctx, ca_cert->derCert.data, ca_cert->derCert.len); + } + unsigned int result_len; + HASH_End(sha1_ctx, sha1.data, &result_len, HASH_ResultLenContext(sha1_ctx)); + HASH_Destroy(sha1_ctx); + + return sha1; +} + // static X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle(const Pickle& pickle, diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc index a0601a7..0c74bc5 100644 --- a/net/base/x509_certificate_openssl.cc +++ b/net/base/x509_certificate_openssl.cc @@ -325,6 +325,7 @@ void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { void X509Certificate::Initialize() { crypto::EnsureOpenSSLInit(); fingerprint_ = CalculateFingerprint(cert_handle_); + chain_fingerprint_ = CalculateChainFingerprint(); ASN1_INTEGER* num = X509_get_serialNumber(cert_handle_); if (num) { @@ -347,6 +348,7 @@ void X509Certificate::ResetCertStore() { X509InitSingleton::GetInstance()->ResetCertStore(); } +// static SHA1Fingerprint X509Certificate::CalculateFingerprint(OSCertHandle cert) { SHA1Fingerprint sha1; unsigned int sha1_size = static_cast<unsigned int>(sizeof(sha1.data)); @@ -356,6 +358,26 @@ SHA1Fingerprint X509Certificate::CalculateFingerprint(OSCertHandle cert) { return sha1; } +SHA1Fingerprint X509Certificate::CalculateChainFingerprint() const { + SHA1Fingerprint sha1; + memset(sha1.data, 0, sizeof(sha1.data)); + + SHA_CTX sha1_ctx; + SHA1_Init(&sha1_ctx); + DERCache der_cache; + if (!GetDERAndCacheIfNeeded(cert_handle_, &der_cache)) + return sha1; + SHA1_Update(&sha1_ctx, der_cache.data, der_cache.data_length); + for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { + if (!GetDERAndCacheIfNeeded(intermediate_ca_certs_[i], &der_cache)) + return sha1; + SHA1_Update(&sha1_ctx, der_cache.data, der_cache.data_length); + } + SHA1_Final(sha1.data, &sha1_ctx); + + return sha1; +} + // static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc index dba04e9..a42799e 100644 --- a/net/base/x509_certificate_unittest.cc +++ b/net/base/x509_certificate_unittest.cc @@ -436,6 +436,47 @@ TEST(X509CertificateTest, SerialNumbers) { paypal_null_serial, sizeof(paypal_null_serial)) == 0); } +TEST(X509CertificateTest, ChainFingerprints) { + FilePath certs_dir = GetTestCertsDirectory(); + + scoped_refptr<X509Certificate> server_cert = + ImportCertFromFile(certs_dir, "salesforce_com_test.pem"); + ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert); + + scoped_refptr<X509Certificate> intermediate_cert1 = + ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem"); + ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert1); + + scoped_refptr<X509Certificate> intermediate_cert2 = + ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem"); + ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert2); + + X509Certificate::OSCertHandles intermediates; + intermediates.push_back(intermediate_cert1->os_cert_handle()); + scoped_refptr<X509Certificate> cert_chain1 = + X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), + intermediates); + + intermediates.clear(); + intermediates.push_back(intermediate_cert2->os_cert_handle()); + scoped_refptr<X509Certificate> cert_chain2 = + X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), + intermediates); + + static const uint8 cert_chain1_fingerprint[20] = { + 0x67, 0x78, 0x81, 0xd7, 0x78, 0xca, 0xd5, 0x04, 0x73, 0xf8, + 0x95, 0xff, 0xf3, 0x39, 0xe4, 0xcd, 0x5e, 0xf0, 0x79, 0x76 + }; + static const uint8 cert_chain2_fingerprint[20] = { + 0x8c, 0x93, 0x85, 0xb0, 0x15, 0xd3, 0xa3, 0x0e, 0xe7, 0x4f, + 0x42, 0xf4, 0x30, 0xc3, 0xe9, 0x14, 0x12, 0x54, 0xb9, 0x9d + }; + EXPECT_TRUE(memcmp(cert_chain1->chain_fingerprint().data, + cert_chain1_fingerprint, 20) == 0); + EXPECT_TRUE(memcmp(cert_chain2->chain_fingerprint().data, + cert_chain2_fingerprint, 20) == 0); +} + // A regression test for http://crbug.com/31497. // This certificate will expire on 2012-04-08. TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) { diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc index d7b28a2..041fe4d5 100644 --- a/net/base/x509_certificate_win.cc +++ b/net/base/x509_certificate_win.cc @@ -4,6 +4,9 @@ #include "net/base/x509_certificate.h" +#define PRArenaPool PLArenaPool // Required by <blapi.h>. +#include <blapi.h> // Implement CalculateChainFingerprint() with NSS. + #include "base/lazy_instance.h" #include "base/logging.h" #include "base/pickle.h" @@ -541,6 +544,7 @@ void X509Certificate::Initialize() { valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter); fingerprint_ = CalculateFingerprint(cert_handle_); + chain_fingerprint_ = CalculateChainFingerprint(); const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber; scoped_array<uint8> serial_bytes(new uint8[serial->cbData]); @@ -1018,6 +1022,30 @@ SHA1Fingerprint X509Certificate::CalculateFingerprint( return sha1; } +// TODO(wtc): This function is implemented with NSS low-level hash +// functions to ensure it is fast. Reimplement this function with +// CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead. +SHA1Fingerprint X509Certificate::CalculateChainFingerprint() const { + SHA1Fingerprint sha1; + memset(sha1.data, 0, sizeof(sha1.data)); + + SHA1Context* sha1_ctx = SHA1_NewContext(); + if (!sha1_ctx) + return sha1; + SHA1_Begin(sha1_ctx); + SHA1_Update(sha1_ctx, cert_handle_->pbCertEncoded, + cert_handle_->cbCertEncoded); + for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { + PCCERT_CONTEXT ca_cert = intermediate_ca_certs_[i]; + SHA1_Update(sha1_ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded); + } + unsigned int result_len; + SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH); + SHA1_DestroyContext(sha1_ctx, PR_TRUE); + + return sha1; +} + // static X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle(const Pickle& pickle, |