summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/x509_certificate.cc30
-rw-r--r--net/base/x509_certificate.h9
-rw-r--r--net/base/x509_certificate_mac.cc31
-rw-r--r--net/base/x509_certificate_nss.cc12
-rw-r--r--net/base/x509_certificate_unittest.cc28
-rw-r--r--net/base/x509_certificate_win.cc15
6 files changed, 125 insertions, 0 deletions
diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc
index 0c02c07..ef6dfdd 100644
--- a/net/base/x509_certificate.cc
+++ b/net/base/x509_certificate.cc
@@ -474,4 +474,34 @@ X509Certificate::~X509Certificate() {
FreeOSCertHandle(intermediate_ca_certs_[i]);
}
+bool X509Certificate::IsBlacklisted() const {
+ static const unsigned kNumSerials = 8;
+ static const unsigned kSerialBytes = 16;
+ static const uint8 kSerials[kNumSerials][kSerialBytes] = {
+ // Not a real certificate. For testing only.
+ {0x07,0x7a,0x59,0xbc,0xd5,0x34,0x59,0x60,0x1c,0xa6,0x90,0x72,0x67,0xa6,0xdd,0x1c},
+
+ {0x04,0x7e,0xcb,0xe9,0xfc,0xa5,0x5f,0x7b,0xd0,0x9e,0xae,0x36,0xe1,0x0c,0xae,0x1e},
+ {0xd8,0xf3,0x5f,0x4e,0xb7,0x87,0x2b,0x2d,0xab,0x06,0x92,0xe3,0x15,0x38,0x2f,0xb0},
+ {0xb0,0xb7,0x13,0x3e,0xd0,0x96,0xf9,0xb5,0x6f,0xae,0x91,0xc8,0x74,0xbd,0x3a,0xc0},
+ {0x92,0x39,0xd5,0x34,0x8f,0x40,0xd1,0x69,0x5a,0x74,0x54,0x70,0xe1,0xf2,0x3f,0x43},
+ {0x92,0x39,0xd5,0x34,0x8f,0x40,0xd1,0x69,0x5a,0x74,0x54,0x70,0xe1,0xf2,0x3f,0x43},
+ {0xd7,0x55,0x8f,0xda,0xf5,0xf1,0x10,0x5b,0xb2,0x13,0x28,0x2b,0x70,0x77,0x29,0xa3},
+ {0xf5,0xc8,0x6a,0xf3,0x61,0x62,0xf1,0x3a,0x64,0xf5,0x4f,0x6d,0xc9,0x58,0x7c,0x06},
+ };
+
+ if (serial_number_.size() == kSerialBytes) {
+ for (unsigned i = 0; i < kNumSerials; i++) {
+ if (memcmp(kSerials[i], serial_number_.data(), kSerialBytes) == 0) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.SSLCertBlacklisted", i /* sample */, 0 /* min */,
+ 99 /* max */, 100 /* num buckets */);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
} // namespace net
diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h
index b1a15aa..b25d0a9 100644
--- a/net/base/x509_certificate.h
+++ b/net/base/x509_certificate.h
@@ -202,6 +202,9 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
// The fingerprint of this certificate.
const SHA1Fingerprint& fingerprint() const { return fingerprint_; }
+ // The serial number, DER encoded.
+ const std::string& serial_number() const { return serial_number_; }
+
// 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.
@@ -366,6 +369,9 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
static bool VerifyHostname(const std::string& hostname,
const std::vector<std::string>& cert_names);
+ // IsBlacklisted returns true if this certificate is explicitly blacklisted.
+ bool IsBlacklisted() const;
+
// The subject of the certificate.
CertPrincipal subject_;
@@ -382,6 +388,9 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
// The fingerprint of this certificate.
SHA1Fingerprint fingerprint_;
+ // The serial number of this certificate, DER encoded.
+ std::string serial_number_;
+
// A handle to the certificate object in the underlying crypto library.
OSCertHandle cert_handle_;
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 839e91e..254469e 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -229,6 +229,31 @@ void GetCertDateForOID(X509Certificate::OSCertHandle cert_handle,
}
}
+std::string GetCertSerialNumber(X509Certificate::OSCertHandle cert_handle) {
+ CSSMFields fields;
+ OSStatus status = GetCertFields(cert_handle, &fields);
+ if (status)
+ return "";
+
+ std::string ret;
+ for (size_t field = 0; field < fields.num_of_fields; ++field) {
+ if (!CSSMOIDEqual(&fields.fields[field].FieldOid,
+ &CSSMOID_X509V1SerialNumber)) {
+ continue;
+ }
+ ret.assign(
+ reinterpret_cast<char*>(fields.fields[field].FieldValue.Data),
+ fields.fields[field].FieldValue.Length);
+ break;
+ }
+
+ // Remove leading zeros.
+ while (ret.size() > 1 && ret[0] == 0)
+ ret = ret.substr(1, ret.size() - 1);
+
+ return ret;
+}
+
// Creates a SecPolicyRef for the given OID, with optional value.
OSStatus CreatePolicy(const CSSM_OID* policy_OID,
void* option_data,
@@ -486,6 +511,7 @@ void X509Certificate::Initialize() {
&valid_expiry_);
fingerprint_ = CalculateFingerprint(cert_handle_);
+ serial_number_ = GetCertSerialNumber(cert_handle_);
}
// static
@@ -664,6 +690,11 @@ int X509Certificate::Verify(const std::string& hostname, int flags,
CertVerifyResult* verify_result) const {
verify_result->Reset();
+ if (IsBlacklisted()) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ return ERR_CERT_REVOKED;
+ }
+
// Create an SSL SecPolicyRef, and configure it to perform hostname
// validation. The hostname check does 99% of what we want, with the
// exception of dotted IPv4 addreses, which we handle ourselves below.
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index 0c61512..31199d9 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -609,6 +609,13 @@ void X509Certificate::Initialize() {
ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_);
fingerprint_ = CalculateFingerprint(cert_handle_);
+
+ serial_number_ = std::string(
+ reinterpret_cast<char*>(cert_handle_->serialNumber.data),
+ cert_handle_->serialNumber.len);
+ // Remove leading zeros.
+ while (serial_number_.size() > 1 && serial_number_[0] == 0)
+ serial_number_ = serial_number_.substr(1, serial_number_.size() - 1);
}
// static
@@ -744,6 +751,11 @@ int X509Certificate::Verify(const std::string& hostname,
CertVerifyResult* verify_result) const {
verify_result->Reset();
+ if (IsBlacklisted()) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ return ERR_CERT_REVOKED;
+ }
+
// Make sure that the hostname matches with the common name of the cert.
SECStatus status = CERT_VerifyCertName(cert_handle_, hostname.c_str());
if (status != SECSuccess)
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index 7f000fe..ecfd9fc 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -413,6 +413,34 @@ TEST(X509CertificateTest, UnoSoftCertParsing) {
EXPECT_NE(0, verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
}
+TEST(X509CertificateTest, SerialNumbers) {
+ scoped_refptr<X509Certificate> google_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der)));
+
+ static const uint8 google_serial[16] = {
+ 0x01,0x2a,0x39,0x76,0x0d,0x3f,0x4f,0xc9,
+ 0x0b,0xe7,0xbd,0x2b,0xcf,0x95,0x2e,0x7a,
+ };
+
+ ASSERT_EQ(sizeof(google_serial), google_cert->serial_number().size());
+ EXPECT_TRUE(memcmp(google_cert->serial_number().data(), google_serial,
+ sizeof(google_serial)) == 0);
+
+ // We also want to check a serial number where the first byte is >= 0x80 in
+ // case the underlying library tries to pad it.
+ scoped_refptr<X509Certificate> paypal_null_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(paypal_null_der),
+ sizeof(paypal_null_der)));
+
+ static const uint8 paypal_null_serial[2] = {0xf0, 0x9b};
+ ASSERT_EQ(sizeof(paypal_null_serial),
+ paypal_null_cert->serial_number().size());
+ EXPECT_TRUE(memcmp(paypal_null_cert->serial_number().data(),
+ paypal_null_serial, sizeof(paypal_null_serial)) == 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 2bf4a28..a9d6606 100644
--- a/net/base/x509_certificate_win.cc
+++ b/net/base/x509_certificate_win.cc
@@ -492,6 +492,16 @@ void X509Certificate::Initialize() {
valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
fingerprint_ = CalculateFingerprint(cert_handle_);
+
+ const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
+ scoped_array<uint8> serial_bytes(new uint8[serial->cbData]);
+ for (unsigned i = 0; i < serial->cbData; i++)
+ serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
+ serial_number_ = std::string(
+ reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
+ // Remove leading zeros.
+ while (serial_number_.size() > 1 && serial_number_[0] == 0)
+ serial_number_ = serial_number_.substr(1, serial_number_.size() - 1);
}
// static
@@ -655,6 +665,11 @@ int X509Certificate::Verify(const std::string& hostname,
if (!cert_handle_)
return ERR_UNEXPECTED;
+ if (IsBlacklisted()) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ return ERR_CERT_REVOKED;
+ }
+
// Build and validate certificate chain.
CERT_CHAIN_PARA chain_para;