summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
Diffstat (limited to 'net/base')
-rw-r--r--net/base/cert_verifier.cc4
-rw-r--r--net/base/cert_verifier_unittest.cc67
-rw-r--r--net/base/x509_certificate.cc3
-rw-r--r--net/base/x509_certificate.h12
-rw-r--r--net/base/x509_certificate_mac.cc25
-rw-r--r--net/base/x509_certificate_nss.cc21
-rw-r--r--net/base/x509_certificate_openssl.cc22
-rw-r--r--net/base/x509_certificate_unittest.cc41
-rw-r--r--net/base/x509_certificate_win.cc28
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,