Home | History | Annotate | Download | only in cert
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/cert/x509_certificate.h"
      6 
      7 #include <CommonCrypto/CommonDigest.h>
      8 #include <CoreServices/CoreServices.h>
      9 #include <Security/Security.h>
     10 
     11 #include <cert.h>
     12 
     13 #include <vector>
     14 
     15 #include "base/lazy_instance.h"
     16 #include "base/logging.h"
     17 #include "base/mac/mac_logging.h"
     18 #include "base/mac/scoped_cftyperef.h"
     19 #include "base/memory/singleton.h"
     20 #include "base/pickle.h"
     21 #include "base/sha1.h"
     22 #include "base/strings/string_piece.h"
     23 #include "base/strings/sys_string_conversions.h"
     24 #include "base/synchronization/lock.h"
     25 #include "crypto/cssm_init.h"
     26 #include "crypto/mac_security_services_lock.h"
     27 #include "crypto/nss_util.h"
     28 #include "net/cert/x509_util_mac.h"
     29 
     30 using base::ScopedCFTypeRef;
     31 using base::Time;
     32 
     33 namespace net {
     34 
     35 namespace {
     36 
     37 void GetCertDistinguishedName(
     38     const x509_util::CSSMCachedCertificate& cached_cert,
     39     const CSSM_OID* oid,
     40     CertPrincipal* result) {
     41   x509_util::CSSMFieldValue distinguished_name;
     42   OSStatus status = cached_cert.GetField(oid, &distinguished_name);
     43   if (status || !distinguished_name.field())
     44     return;
     45   result->ParseDistinguishedName(distinguished_name.field()->Data,
     46                                  distinguished_name.field()->Length);
     47 }
     48 
     49 bool IsCertIssuerInEncodedList(X509Certificate::OSCertHandle cert_handle,
     50                                const std::vector<std::string>& issuers) {
     51   x509_util::CSSMCachedCertificate cached_cert;
     52   if (cached_cert.Init(cert_handle) != CSSM_OK)
     53     return false;
     54 
     55   x509_util::CSSMFieldValue distinguished_name;
     56   OSStatus status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd,
     57                                          &distinguished_name);
     58   if (status || !distinguished_name.field())
     59     return false;
     60 
     61   base::StringPiece name_piece(
     62       reinterpret_cast<const char*>(distinguished_name.field()->Data),
     63       static_cast<size_t>(distinguished_name.field()->Length));
     64 
     65   for (std::vector<std::string>::const_iterator it = issuers.begin();
     66        it != issuers.end(); ++it) {
     67     base::StringPiece issuer_piece(*it);
     68     if (name_piece == issuer_piece)
     69       return true;
     70   }
     71 
     72   return false;
     73 }
     74 
     75 void GetCertDateForOID(const x509_util::CSSMCachedCertificate& cached_cert,
     76                        const CSSM_OID* oid,
     77                        Time* result) {
     78   *result = Time::Time();
     79 
     80   x509_util::CSSMFieldValue field;
     81   OSStatus status = cached_cert.GetField(oid, &field);
     82   if (status)
     83     return;
     84 
     85   const CSSM_X509_TIME* x509_time = field.GetAs<CSSM_X509_TIME>();
     86   if (x509_time->timeType != BER_TAG_UTC_TIME &&
     87       x509_time->timeType != BER_TAG_GENERALIZED_TIME) {
     88     LOG(ERROR) << "Unsupported date/time format "
     89                << x509_time->timeType;
     90     return;
     91   }
     92 
     93   base::StringPiece time_string(
     94       reinterpret_cast<const char*>(x509_time->time.Data),
     95       x509_time->time.Length);
     96   CertDateFormat format = x509_time->timeType == BER_TAG_UTC_TIME ?
     97       CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
     98   if (!ParseCertificateDate(time_string, format, result))
     99     LOG(ERROR) << "Invalid certificate date/time " << time_string;
    100 }
    101 
    102 std::string GetCertSerialNumber(
    103     const x509_util::CSSMCachedCertificate& cached_cert) {
    104   x509_util::CSSMFieldValue serial_number;
    105   OSStatus status = cached_cert.GetField(&CSSMOID_X509V1SerialNumber,
    106                                          &serial_number);
    107   if (status || !serial_number.field())
    108     return std::string();
    109 
    110   return std::string(
    111       reinterpret_cast<const char*>(serial_number.field()->Data),
    112       serial_number.field()->Length);
    113 }
    114 
    115 // Returns true if |purpose| is listed as allowed in |usage|. This
    116 // function also considers the "Any" purpose. If the attribute is
    117 // present and empty, we return false.
    118 bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage,
    119                             const CSSM_OID* purpose) {
    120   for (unsigned p = 0; p < usage->numPurposes; ++p) {
    121     if (CSSMOIDEqual(&usage->purposes[p], purpose))
    122       return true;
    123     if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ExtendedKeyUsageAny))
    124       return true;
    125   }
    126   return false;
    127 }
    128 
    129 // Test that a given |cert_handle| is actually a valid X.509 certificate, and
    130 // return true if it is.
    131 //
    132 // On OS X, SecCertificateCreateFromData() does not return any errors if
    133 // called with invalid data, as long as data is present. The actual decoding
    134 // of the certificate does not happen until an API that requires a CSSM
    135 // handle is called. While SecCertificateGetCLHandle is the most likely
    136 // candidate, as it performs the parsing, it does not check whether the
    137 // parsing was actually successful. Instead, SecCertificateGetSubject is
    138 // used (supported since 10.3), as a means to check that the certificate
    139 // parsed as a valid X.509 certificate.
    140 bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
    141   const CSSM_X509_NAME* sanity_check = NULL;
    142   OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check);
    143   return status == noErr && sanity_check;
    144 }
    145 
    146 // Parses |data| of length |length|, attempting to decode it as the specified
    147 // |format|. If |data| is in the specified format, any certificates contained
    148 // within are stored into |output|.
    149 void AddCertificatesFromBytes(const char* data, size_t length,
    150                               SecExternalFormat format,
    151                               X509Certificate::OSCertHandles* output) {
    152   SecExternalFormat input_format = format;
    153   ScopedCFTypeRef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy(
    154       kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), length,
    155       kCFAllocatorNull));
    156 
    157   CFArrayRef items = NULL;
    158   OSStatus status;
    159   {
    160     base::AutoLock lock(crypto::GetMacSecurityServicesLock());
    161     status = SecKeychainItemImport(local_data, NULL, &input_format,
    162                                    NULL, 0, NULL, NULL, &items);
    163   }
    164 
    165   if (status) {
    166     OSSTATUS_DLOG(WARNING, status)
    167         << "Unable to import items from data of length " << length;
    168     return;
    169   }
    170 
    171   ScopedCFTypeRef<CFArrayRef> scoped_items(items);
    172   CFTypeID cert_type_id = SecCertificateGetTypeID();
    173 
    174   for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) {
    175     SecKeychainItemRef item = reinterpret_cast<SecKeychainItemRef>(
    176         const_cast<void*>(CFArrayGetValueAtIndex(items, i)));
    177 
    178     // While inputFormat implies only certificates will be imported, if/when
    179     // other formats (eg: PKCS#12) are supported, this may also include
    180     // private keys or other items types, so filter appropriately.
    181     if (CFGetTypeID(item) == cert_type_id) {
    182       SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(item);
    183       // OS X ignores |input_format| if it detects that |local_data| is PEM
    184       // encoded, attempting to decode data based on internal rules for PEM
    185       // block headers. If a PKCS#7 blob is encoded with a PEM block of
    186       // CERTIFICATE, OS X 10.5 will return a single, invalid certificate
    187       // based on the decoded data. If this happens, the certificate should
    188       // not be included in |output|. Because |output| is empty,
    189       // CreateCertificateListfromBytes will use PEMTokenizer to decode the
    190       // data. When called again with the decoded data, OS X will honor
    191       // |input_format|, causing decode to succeed. On OS X 10.6, the data
    192       // is properly decoded as a PKCS#7, whether PEM or not, which avoids
    193       // the need to fallback to internal decoding.
    194       if (IsValidOSCertHandle(cert)) {
    195         CFRetain(cert);
    196         output->push_back(cert);
    197       }
    198     }
    199   }
    200 }
    201 
    202 struct CSSMOIDString {
    203   const CSSM_OID* oid_;
    204   std::string string_;
    205 };
    206 
    207 typedef std::vector<CSSMOIDString> CSSMOIDStringVector;
    208 
    209 bool CERTNameToCSSMOIDVector(CERTName* name, CSSMOIDStringVector* out_values) {
    210   struct OIDCSSMMap {
    211     SECOidTag sec_OID_;
    212     const CSSM_OID* cssm_OID_;
    213   };
    214 
    215   const OIDCSSMMap kOIDs[] = {
    216       { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName },
    217       { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName },
    218       { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName },
    219       { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName },
    220       { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress },
    221       { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName },
    222       { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName },
    223       { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier },
    224       { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier },
    225       { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress },
    226   };
    227 
    228   CERTRDN** rdns = name->rdns;
    229   for (size_t rdn = 0; rdns[rdn]; ++rdn) {
    230     CERTAVA** avas = rdns[rdn]->avas;
    231     for (size_t pair = 0; avas[pair] != 0; ++pair) {
    232       SECOidTag tag = CERT_GetAVATag(avas[pair]);
    233       if (tag == SEC_OID_UNKNOWN) {
    234         return false;
    235       }
    236       CSSMOIDString oidString;
    237       bool found_oid = false;
    238       for (size_t oid = 0; oid < ARRAYSIZE_UNSAFE(kOIDs); ++oid) {
    239         if (kOIDs[oid].sec_OID_ == tag) {
    240           SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
    241           if (!decode_item)
    242             return false;
    243 
    244           // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
    245           std::string value(reinterpret_cast<char*>(decode_item->data),
    246                             decode_item->len);
    247           oidString.oid_ = kOIDs[oid].cssm_OID_;
    248           oidString.string_ = value;
    249           out_values->push_back(oidString);
    250           SECITEM_FreeItem(decode_item, PR_TRUE);
    251           found_oid = true;
    252           break;
    253         }
    254       }
    255       if (!found_oid) {
    256         DLOG(ERROR) << "Unrecognized OID: " << tag;
    257       }
    258     }
    259   }
    260   return true;
    261 }
    262 
    263 class ScopedCertName {
    264  public:
    265   explicit ScopedCertName(CERTName* name) : name_(name) { }
    266   ~ScopedCertName() {
    267     if (name_) CERT_DestroyName(name_);
    268   }
    269   operator CERTName*() { return name_; }
    270 
    271  private:
    272   CERTName* name_;
    273 };
    274 
    275 class ScopedEncodedCertResults {
    276  public:
    277   explicit ScopedEncodedCertResults(CSSM_TP_RESULT_SET* results)
    278       : results_(results) { }
    279   ~ScopedEncodedCertResults() {
    280     if (results_) {
    281       CSSM_ENCODED_CERT* encCert =
    282           reinterpret_cast<CSSM_ENCODED_CERT*>(results_->Results);
    283       for (uint32 i = 0; i < results_->NumberOfResults; i++) {
    284         crypto::CSSMFree(encCert[i].CertBlob.Data);
    285       }
    286     }
    287     crypto::CSSMFree(results_->Results);
    288     crypto::CSSMFree(results_);
    289   }
    290 
    291  private:
    292   CSSM_TP_RESULT_SET* results_;
    293 };
    294 
    295 }  // namespace
    296 
    297 void X509Certificate::Initialize() {
    298   x509_util::CSSMCachedCertificate cached_cert;
    299   if (cached_cert.Init(cert_handle_) == CSSM_OK) {
    300     GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1SubjectNameStd,
    301                              &subject_);
    302     GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1IssuerNameStd,
    303                              &issuer_);
    304     GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotBefore,
    305                       &valid_start_);
    306     GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotAfter,
    307                       &valid_expiry_);
    308     serial_number_ = GetCertSerialNumber(cached_cert);
    309   }
    310 
    311   fingerprint_ = CalculateFingerprint(cert_handle_);
    312   ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
    313 }
    314 
    315 bool X509Certificate::IsIssuedByEncoded(
    316     const std::vector<std::string>& valid_issuers) {
    317   if (IsCertIssuerInEncodedList(cert_handle_, valid_issuers))
    318     return true;
    319 
    320   for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
    321        it != intermediate_ca_certs_.end(); ++it) {
    322     if (IsCertIssuerInEncodedList(*it, valid_issuers))
    323       return true;
    324   }
    325   return false;
    326 }
    327 
    328 void X509Certificate::GetSubjectAltName(
    329     std::vector<std::string>* dns_names,
    330     std::vector<std::string>* ip_addrs) const {
    331   if (dns_names)
    332     dns_names->clear();
    333   if (ip_addrs)
    334     ip_addrs->clear();
    335 
    336   x509_util::CSSMCachedCertificate cached_cert;
    337   OSStatus status = cached_cert.Init(cert_handle_);
    338   if (status)
    339     return;
    340   x509_util::CSSMFieldValue subject_alt_name;
    341   status = cached_cert.GetField(&CSSMOID_SubjectAltName, &subject_alt_name);
    342   if (status || !subject_alt_name.field())
    343     return;
    344   const CSSM_X509_EXTENSION* cssm_ext =
    345       subject_alt_name.GetAs<CSSM_X509_EXTENSION>();
    346   if (!cssm_ext || !cssm_ext->value.parsedValue)
    347     return;
    348   const CE_GeneralNames* alt_name =
    349       reinterpret_cast<const CE_GeneralNames*>(cssm_ext->value.parsedValue);
    350 
    351   for (size_t name = 0; name < alt_name->numNames; ++name) {
    352     const CE_GeneralName& name_struct = alt_name->generalName[name];
    353     const CSSM_DATA& name_data = name_struct.name;
    354     // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
    355     // respectively, both of which can be byte copied from
    356     // CSSM_DATA::data into the appropriate output vector.
    357     if (dns_names && name_struct.nameType == GNT_DNSName) {
    358       dns_names->push_back(std::string(
    359           reinterpret_cast<const char*>(name_data.Data),
    360           name_data.Length));
    361     } else if (ip_addrs && name_struct.nameType == GNT_IPAddress) {
    362       ip_addrs->push_back(std::string(
    363           reinterpret_cast<const char*>(name_data.Data),
    364           name_data.Length));
    365     }
    366   }
    367 }
    368 
    369 // static
    370 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
    371                                     std::string* encoded) {
    372   CSSM_DATA der_data;
    373   if (SecCertificateGetData(cert_handle, &der_data) != noErr)
    374     return false;
    375   encoded->assign(reinterpret_cast<char*>(der_data.Data),
    376                   der_data.Length);
    377   return true;
    378 }
    379 
    380 // static
    381 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
    382                                    X509Certificate::OSCertHandle b) {
    383   DCHECK(a && b);
    384   if (a == b)
    385     return true;
    386   if (CFEqual(a, b))
    387     return true;
    388   CSSM_DATA a_data, b_data;
    389   return SecCertificateGetData(a, &a_data) == noErr &&
    390       SecCertificateGetData(b, &b_data) == noErr &&
    391       a_data.Length == b_data.Length &&
    392       memcmp(a_data.Data, b_data.Data, a_data.Length) == 0;
    393 }
    394 
    395 // static
    396 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
    397     const char* data, int length) {
    398   CSSM_DATA cert_data;
    399   cert_data.Data = const_cast<uint8*>(reinterpret_cast<const uint8*>(data));
    400   cert_data.Length = length;
    401 
    402   OSCertHandle cert_handle = NULL;
    403   OSStatus status = SecCertificateCreateFromData(&cert_data,
    404                                                  CSSM_CERT_X_509v3,
    405                                                  CSSM_CERT_ENCODING_DER,
    406                                                  &cert_handle);
    407   if (status != noErr)
    408     return NULL;
    409   if (!IsValidOSCertHandle(cert_handle)) {
    410     CFRelease(cert_handle);
    411     return NULL;
    412   }
    413   return cert_handle;
    414 }
    415 
    416 // static
    417 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
    418     const char* data, int length, Format format) {
    419   OSCertHandles results;
    420 
    421   switch (format) {
    422     case FORMAT_SINGLE_CERTIFICATE: {
    423       OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
    424       if (handle)
    425         results.push_back(handle);
    426       break;
    427     }
    428     case FORMAT_PKCS7:
    429       AddCertificatesFromBytes(data, length, kSecFormatPKCS7, &results);
    430       break;
    431     default:
    432       NOTREACHED() << "Certificate format " << format << " unimplemented";
    433       break;
    434   }
    435 
    436   return results;
    437 }
    438 
    439 // static
    440 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
    441     OSCertHandle handle) {
    442   if (!handle)
    443     return NULL;
    444   return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
    445 }
    446 
    447 // static
    448 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
    449   CFRelease(cert_handle);
    450 }
    451 
    452 // static
    453 SHA1HashValue X509Certificate::CalculateFingerprint(
    454     OSCertHandle cert) {
    455   SHA1HashValue sha1;
    456   memset(sha1.data, 0, sizeof(sha1.data));
    457 
    458   CSSM_DATA cert_data;
    459   OSStatus status = SecCertificateGetData(cert, &cert_data);
    460   if (status)
    461     return sha1;
    462 
    463   DCHECK(cert_data.Data);
    464   DCHECK_NE(cert_data.Length, 0U);
    465 
    466   CC_SHA1(cert_data.Data, cert_data.Length, sha1.data);
    467 
    468   return sha1;
    469 }
    470 
    471 // static
    472 SHA1HashValue X509Certificate::CalculateCAFingerprint(
    473     const OSCertHandles& intermediates) {
    474   SHA1HashValue sha1;
    475   memset(sha1.data, 0, sizeof(sha1.data));
    476 
    477   // The CC_SHA(3cc) man page says all CC_SHA1_xxx routines return 1, so
    478   // we don't check their return values.
    479   CC_SHA1_CTX sha1_ctx;
    480   CC_SHA1_Init(&sha1_ctx);
    481   CSSM_DATA cert_data;
    482   for (size_t i = 0; i < intermediates.size(); ++i) {
    483     OSStatus status = SecCertificateGetData(intermediates[i], &cert_data);
    484     if (status)
    485       return sha1;
    486     CC_SHA1_Update(&sha1_ctx, cert_data.Data, cert_data.Length);
    487   }
    488   CC_SHA1_Final(sha1.data, &sha1_ctx);
    489 
    490   return sha1;
    491 }
    492 
    493 bool X509Certificate::SupportsSSLClientAuth() const {
    494   x509_util::CSSMCachedCertificate cached_cert;
    495   OSStatus status = cached_cert.Init(cert_handle_);
    496   if (status)
    497     return false;
    498 
    499   // RFC5280 says to take the intersection of the two extensions.
    500   //
    501   // Our underlying crypto libraries don't expose
    502   // ClientCertificateType, so for now we will not support fixed
    503   // Diffie-Hellman mechanisms. For rsa_sign, we need the
    504   // digitalSignature bit.
    505   //
    506   // In particular, if a key has the nonRepudiation bit and not the
    507   // digitalSignature one, we will not offer it to the user.
    508   x509_util::CSSMFieldValue key_usage;
    509   status = cached_cert.GetField(&CSSMOID_KeyUsage, &key_usage);
    510   if (status == CSSM_OK && key_usage.field()) {
    511     const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
    512     const CE_KeyUsage* key_usage_value =
    513         reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue);
    514     if (!((*key_usage_value) & CE_KU_DigitalSignature))
    515       return false;
    516   }
    517 
    518   status = cached_cert.GetField(&CSSMOID_ExtendedKeyUsage, &key_usage);
    519   if (status == CSSM_OK && key_usage.field()) {
    520     const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
    521     const CE_ExtendedKeyUsage* ext_key_usage =
    522         reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
    523     if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth))
    524       return false;
    525   }
    526   return true;
    527 }
    528 
    529 CFArrayRef X509Certificate::CreateOSCertChainForCert() const {
    530   CFMutableArrayRef cert_list =
    531       CFArrayCreateMutable(kCFAllocatorDefault, 0,
    532                            &kCFTypeArrayCallBacks);
    533   if (!cert_list)
    534     return NULL;
    535 
    536   CFArrayAppendValue(cert_list, os_cert_handle());
    537   for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i)
    538     CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]);
    539 
    540   return cert_list;
    541 }
    542 
    543 // static
    544 X509Certificate::OSCertHandle
    545 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
    546   const char* data;
    547   int length;
    548   if (!pickle_iter->ReadData(&data, &length))
    549     return NULL;
    550 
    551   return CreateOSCertHandleFromBytes(data, length);
    552 }
    553 
    554 // static
    555 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
    556                                                 Pickle* pickle) {
    557   CSSM_DATA cert_data;
    558   OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
    559   if (status)
    560     return false;
    561 
    562   return pickle->WriteData(reinterpret_cast<char*>(cert_data.Data),
    563                            cert_data.Length);
    564 }
    565 
    566 // static
    567 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
    568                                        size_t* size_bits,
    569                                        PublicKeyType* type) {
    570   // Since we might fail, set the output parameters to default values first.
    571   *type = kPublicKeyTypeUnknown;
    572   *size_bits = 0;
    573 
    574   SecKeyRef key;
    575   OSStatus status = SecCertificateCopyPublicKey(cert_handle, &key);
    576   if (status) {
    577     NOTREACHED() << "SecCertificateCopyPublicKey failed: " << status;
    578     return;
    579   }
    580   ScopedCFTypeRef<SecKeyRef> scoped_key(key);
    581 
    582   const CSSM_KEY* cssm_key;
    583   status = SecKeyGetCSSMKey(key, &cssm_key);
    584   if (status) {
    585     NOTREACHED() << "SecKeyGetCSSMKey failed: " << status;
    586     return;
    587   }
    588 
    589   *size_bits = cssm_key->KeyHeader.LogicalKeySizeInBits;
    590 
    591   switch (cssm_key->KeyHeader.AlgorithmId) {
    592     case CSSM_ALGID_RSA:
    593       *type = kPublicKeyTypeRSA;
    594       break;
    595     case CSSM_ALGID_DSA:
    596       *type = kPublicKeyTypeDSA;
    597       break;
    598     case CSSM_ALGID_ECDSA:
    599       *type = kPublicKeyTypeECDSA;
    600       break;
    601     case CSSM_ALGID_DH:
    602       *type = kPublicKeyTypeDH;
    603       break;
    604     default:
    605       *type = kPublicKeyTypeUnknown;
    606       *size_bits = 0;
    607       break;
    608   }
    609 }
    610 
    611 }  // namespace net
    612