// Copyright (c) 2006-2008 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 "net/base/x509_certificate.h" #include #include #include "base/histogram.h" #include "base/logging.h" #include "base/pickle.h" #include "net/base/cert_status_flags.h" #include "net/base/ev_root_ca_metadata.h" using base::Time; namespace net { namespace { // Calculates the SHA-1 fingerprint of the certificate. Returns an empty // (all zero) fingerprint on failure. X509Certificate::Fingerprint CalculateFingerprint( X509Certificate::OSCertHandle cert) { X509Certificate::Fingerprint sha1; memset(sha1.data, 0, sizeof(sha1.data)); CSSM_DATA cert_data; OSStatus status = SecCertificateGetData(cert, &cert_data); if (status) return sha1; DCHECK(NULL != cert_data.Data); DCHECK(0 != cert_data.Length); CC_SHA1(cert_data.Data, cert_data.Length, sha1.data); return sha1; } inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) { return oid1->Length == oid2->Length && (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0); } void ParsePrincipal(const CSSM_X509_NAME* name, X509Certificate::Principal* principal) { std::vector common_names, locality_names, state_names, country_names; // TODO(jcampan): add business_category and serial_number. const CSSM_OID* kOIDs[] = { &CSSMOID_CommonName, &CSSMOID_LocalityName, &CSSMOID_StateProvinceName, &CSSMOID_CountryName, &CSSMOID_StreetAddress, &CSSMOID_OrganizationName, &CSSMOID_OrganizationalUnitName, &CSSMOID_DNQualifier }; // This should be "DC" // but is undoubtedly // wrong. TODO(avi): // Find the right OID. std::vector* values[] = { &common_names, &locality_names, &state_names, &country_names, &(principal->street_addresses), &(principal->organization_names), &(principal->organization_unit_names), &(principal->domain_components) }; DCHECK(arraysize(kOIDs) == arraysize(values)); for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) { CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn]; for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) { CSSM_X509_TYPE_VALUE_PAIR pair_struct = rdn_struct.AttributeTypeAndValue[pair]; for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { if (CSSMOIDEqual(&pair_struct.type, kOIDs[oid])) { std::string value = std::string(reinterpret_cast (pair_struct.value.Data), pair_struct.value.Length); values[oid]->push_back(value); break; } } } } // We don't expect to have more than one CN, L, S, and C. std::vector* single_value_lists[4] = { &common_names, &locality_names, &state_names, &country_names }; std::string* single_values[4] = { &principal->common_name, &principal->locality_name, &principal->state_or_province_name, &principal->country_name }; for (size_t i = 0; i < arraysize(single_value_lists); ++i) { DCHECK(single_value_lists[i]->size() <= 1); if (single_value_lists[i]->size() > 0) *(single_values[i]) = (*(single_value_lists[i]))[0]; } } OSStatus GetCertFieldsForOID(X509Certificate::OSCertHandle cert_handle, CSSM_OID oid, uint32* num_of_fields, CSSM_FIELD_PTR* fields) { *num_of_fields = 0; *fields = NULL; CSSM_DATA cert_data; OSStatus status = SecCertificateGetData(cert_handle, &cert_data); if (status) return status; CSSM_CL_HANDLE cl_handle; status = SecCertificateGetCLHandle(cert_handle, &cl_handle); if (status) return status; status = CSSM_CL_CertGetAllFields(cl_handle, &cert_data, num_of_fields, fields); return status; } void GetCertGeneralNamesForOID(X509Certificate::OSCertHandle cert_handle, CSSM_OID oid, CE_GeneralNameType name_type, std::vector* result) { uint32 num_of_fields; CSSM_FIELD_PTR fields; OSStatus status = GetCertFieldsForOID(cert_handle, oid, &num_of_fields, &fields); if (status) return; for (size_t field = 0; field < num_of_fields; ++field) { if (CSSMOIDEqual(&fields[field].FieldOid, &oid)) { CSSM_X509_EXTENSION_PTR cssm_ext = (CSSM_X509_EXTENSION_PTR)fields[field].FieldValue.Data; CE_GeneralNames* alt_name = (CE_GeneralNames*) cssm_ext->value.parsedValue; for (size_t name = 0; name < alt_name->numNames; ++name) { const CE_GeneralName& name_struct = alt_name->generalName[name]; // For future extension: We're assuming that these values are of types // GNT_RFC822Name, GNT_DNSName or GNT_URI, all of which are encoded as // IA5String. In general, we should be switching off // |name_struct.nameType| and doing type-appropriate conversions. See // certextensions.h and the comment immediately preceding // CE_GeneralNameType for more information. DCHECK(name_struct.nameType == GNT_RFC822Name || name_struct.nameType == GNT_DNSName || name_struct.nameType == GNT_URI); if (name_struct.nameType == name_type) { const CSSM_DATA& name_data = name_struct.name; std::string value = std::string(reinterpret_cast (name_data.Data), name_data.Length); result->push_back(value); } } } } } void GetCertDateForOID(X509Certificate::OSCertHandle cert_handle, CSSM_OID oid, Time* result) { *result = Time::Time(); uint32 num_of_fields; CSSM_FIELD_PTR fields; OSStatus status = GetCertFieldsForOID(cert_handle, oid, &num_of_fields, &fields); if (status) return; for (size_t field = 0; field < num_of_fields; ++field) { if (CSSMOIDEqual(&fields[field].FieldOid, &oid)) { CSSM_X509_TIME* x509_time = reinterpret_cast(fields[field].FieldValue.Data); std::string time_string = std::string(reinterpret_cast (x509_time->time.Data), x509_time->time.Length); DCHECK(x509_time->timeType == BER_TAG_UTC_TIME || x509_time->timeType == BER_TAG_GENERALIZED_TIME); struct tm time; const char* parse_string; if (x509_time->timeType == BER_TAG_UTC_TIME) parse_string = "%y%m%d%H%M%SZ"; else if (x509_time->timeType == BER_TAG_GENERALIZED_TIME) parse_string = "%y%m%d%H%M%SZ"; else { // Those are the only two BER tags for time; if neither are used then // this is a rather broken cert. return; } strptime(time_string.c_str(), parse_string, &time); Time::Exploded exploded; exploded.year = time.tm_year + 1900; exploded.month = time.tm_mon + 1; exploded.day_of_week = time.tm_wday; exploded.day_of_month = time.tm_mday; exploded.hour = time.tm_hour; exploded.minute = time.tm_min; exploded.second = time.tm_sec; exploded.millisecond = 0; *result = Time::FromUTCExploded(exploded); break; } } } } // namespace void X509Certificate::Initialize() { const CSSM_X509_NAME* name; OSStatus status = SecCertificateGetSubject(cert_handle_, &name); if (!status) { ParsePrincipal(name, &subject_); } status = SecCertificateGetIssuer(cert_handle_, &name); if (!status) { ParsePrincipal(name, &issuer_); } GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore, &valid_start_); GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter, &valid_expiry_); fingerprint_ = CalculateFingerprint(cert_handle_); // Store the certificate in the cache in case we need it later. X509Certificate::Cache::GetInstance()->Insert(this); } // static X509Certificate* X509Certificate::CreateFromHandle(OSCertHandle cert_handle) { DCHECK(cert_handle); // Check if we already have this certificate in memory. X509Certificate::Cache* cache = X509Certificate::Cache::GetInstance(); X509Certificate* cert = cache->Find(CalculateFingerprint(cert_handle)); if (cert) { // We've found a certificate with the same fingerprint in our cache. We own // the |cert_handle|, which makes it our job to free it. CFRelease(cert_handle); DHISTOGRAM_COUNTS(L"X509CertificateReuseCount", 1); return cert; } // Otherwise, allocate a new object. return new X509Certificate(cert_handle); } // static X509Certificate* X509Certificate::CreateFromBytes(const char* data, int length) { CSSM_DATA cert_data; cert_data.Data = const_cast(reinterpret_cast(data)); cert_data.Length = length; OSCertHandle cert_handle = NULL; OSStatus status = SecCertificateCreateFromData(&cert_data, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &cert_handle); if (status) return NULL; return CreateFromHandle(cert_handle); } // static X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle, void** pickle_iter) { const char* data; int length; if (!pickle.ReadData(pickle_iter, &data, &length)) return NULL; return CreateFromBytes(data, length); } X509Certificate::X509Certificate(OSCertHandle cert_handle) : cert_handle_(cert_handle) { Initialize(); } X509Certificate::X509Certificate(std::string subject, std::string issuer, Time start_date, Time expiration_date) : subject_(subject), issuer_(issuer), valid_start_(start_date), valid_expiry_(expiration_date), cert_handle_(NULL) { memset(fingerprint_.data, 0, sizeof(fingerprint_.data)); } void X509Certificate::Persist(Pickle* pickle) { CSSM_DATA cert_data; OSStatus status = SecCertificateGetData(cert_handle_, &cert_data); if (status) { NOTREACHED(); return; } pickle->WriteData(reinterpret_cast(cert_data.Data), cert_data.Length); } X509Certificate::~X509Certificate() { // We might not be in the cache, but it is safe to remove ourselves anyway. X509Certificate::Cache::GetInstance()->Remove(this); if (cert_handle_) CFRelease(cert_handle_); } void X509Certificate::GetDNSNames(std::vector* dns_names) const { dns_names->clear(); GetCertGeneralNamesForOID(cert_handle_, CSSMOID_SubjectAltName, GNT_DNSName, dns_names); if (dns_names->empty()) dns_names->push_back(subject_.common_name); } // Returns true if the certificate is an extended-validation certificate. // // The certificate has already been verified by the HTTP library. cert_status // represents the result of that verification. This function performs // additional checks of the certificatePolicies extensions of the certificates // in the certificate chain according to Section 7 (pp. 11-12) of the EV // Certificate Guidelines Version 1.0 at // http://cabforum.org/EV_Certificate_Guidelines.pdf. bool X509Certificate::IsEV(int cert_status) const { // TODO(avi): implement this NOTIMPLEMENTED(); return false; } } // namespace net