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 <blapi.h>  // Implement CalculateChainFingerprint() with NSS.
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/pickle.h"
     12 #include "base/sha1.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "crypto/capi_util.h"
     16 #include "crypto/scoped_capi_types.h"
     17 #include "net/base/net_errors.h"
     18 
     19 #pragma comment(lib, "crypt32.lib")
     20 
     21 using base::Time;
     22 
     23 namespace net {
     24 
     25 namespace {
     26 
     27 typedef crypto::ScopedCAPIHandle<
     28     HCERTSTORE,
     29     crypto::CAPIDestroyerWithFlags<HCERTSTORE,
     30                                    CertCloseStore, 0> > ScopedHCERTSTORE;
     31 
     32 void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded,
     33                               SYSTEMTIME* system_time) {
     34   system_time->wYear = exploded.year;
     35   system_time->wMonth = exploded.month;
     36   system_time->wDayOfWeek = exploded.day_of_week;
     37   system_time->wDay = exploded.day_of_month;
     38   system_time->wHour = exploded.hour;
     39   system_time->wMinute = exploded.minute;
     40   system_time->wSecond = exploded.second;
     41   system_time->wMilliseconds = exploded.millisecond;
     42 }
     43 
     44 //-----------------------------------------------------------------------------
     45 
     46 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
     47 // structure and stores it in *output.
     48 void GetCertSubjectAltName(
     49     PCCERT_CONTEXT cert,
     50     scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter>* output) {
     51   PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
     52                                                 cert->pCertInfo->cExtension,
     53                                                 cert->pCertInfo->rgExtension);
     54   if (!extension)
     55     return;
     56 
     57   CRYPT_DECODE_PARA decode_para;
     58   decode_para.cbSize = sizeof(decode_para);
     59   decode_para.pfnAlloc = crypto::CryptAlloc;
     60   decode_para.pfnFree = crypto::CryptFree;
     61   CERT_ALT_NAME_INFO* alt_name_info = NULL;
     62   DWORD alt_name_info_size = 0;
     63   BOOL rv;
     64   rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
     65                            szOID_SUBJECT_ALT_NAME2,
     66                            extension->Value.pbData,
     67                            extension->Value.cbData,
     68                            CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
     69                            &decode_para,
     70                            &alt_name_info,
     71                            &alt_name_info_size);
     72   if (rv)
     73     output->reset(alt_name_info);
     74 }
     75 
     76 void AddCertsFromStore(HCERTSTORE store,
     77                        X509Certificate::OSCertHandles* results) {
     78   PCCERT_CONTEXT cert = NULL;
     79 
     80   while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
     81     PCCERT_CONTEXT to_add = NULL;
     82     if (CertAddCertificateContextToStore(
     83         NULL,  // The cert won't be persisted in any cert store. This breaks
     84                // any association the context currently has to |store|, which
     85                // allows us, the caller, to safely close |store| without
     86                // releasing the cert handles.
     87         cert,
     88         CERT_STORE_ADD_USE_EXISTING,
     89         &to_add) && to_add != NULL) {
     90       // When processing stores generated from PKCS#7/PKCS#12 files, it
     91       // appears that the order returned is the inverse of the order that it
     92       // appeared in the file.
     93       // TODO(rsleevi): Ensure this order is consistent across all Win
     94       // versions
     95       results->insert(results->begin(), to_add);
     96     }
     97   }
     98 }
     99 
    100 X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) {
    101   X509Certificate::OSCertHandles results;
    102   CERT_BLOB data_blob;
    103   data_blob.cbData = length;
    104   data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data));
    105 
    106   HCERTSTORE out_store = NULL;
    107 
    108   DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
    109                          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
    110                          CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED;
    111 
    112   if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types,
    113                         CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
    114                         &out_store, NULL, NULL) || out_store == NULL) {
    115     return results;
    116   }
    117 
    118   AddCertsFromStore(out_store, &results);
    119   CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG);
    120 
    121   return results;
    122 }
    123 
    124 // Given a CERT_NAME_BLOB, returns true if it appears in a given list,
    125 // formatted as a vector of strings holding DER-encoded X.509
    126 // DistinguishedName entries.
    127 bool IsCertNameBlobInIssuerList(
    128     CERT_NAME_BLOB* name_blob,
    129     const std::vector<std::string>& issuer_names) {
    130   for (std::vector<std::string>::const_iterator it = issuer_names.begin();
    131        it != issuer_names.end(); ++it) {
    132     CERT_NAME_BLOB issuer_blob;
    133     issuer_blob.pbData =
    134         reinterpret_cast<BYTE*>(const_cast<char*>(it->data()));
    135     issuer_blob.cbData = static_cast<DWORD>(it->length());
    136 
    137     BOOL rb = CertCompareCertificateName(
    138         X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuer_blob, name_blob);
    139     if (rb)
    140       return true;
    141   }
    142   return false;
    143 }
    144 
    145 }  // namespace
    146 
    147 void X509Certificate::Initialize() {
    148   DCHECK(cert_handle_);
    149   subject_.ParseDistinguishedName(cert_handle_->pCertInfo->Subject.pbData,
    150                                   cert_handle_->pCertInfo->Subject.cbData);
    151   issuer_.ParseDistinguishedName(cert_handle_->pCertInfo->Issuer.pbData,
    152                                  cert_handle_->pCertInfo->Issuer.cbData);
    153 
    154   valid_start_ = Time::FromFileTime(cert_handle_->pCertInfo->NotBefore);
    155   valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
    156 
    157   fingerprint_ = CalculateFingerprint(cert_handle_);
    158   ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
    159 
    160   const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
    161   scoped_ptr<uint8[]> serial_bytes(new uint8[serial->cbData]);
    162   for (unsigned i = 0; i < serial->cbData; i++)
    163     serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
    164   serial_number_ = std::string(
    165       reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
    166 }
    167 
    168 void X509Certificate::GetSubjectAltName(
    169     std::vector<std::string>* dns_names,
    170     std::vector<std::string>* ip_addrs) const {
    171   if (dns_names)
    172     dns_names->clear();
    173   if (ip_addrs)
    174     ip_addrs->clear();
    175 
    176   if (!cert_handle_)
    177     return;
    178 
    179   scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter> alt_name_info;
    180   GetCertSubjectAltName(cert_handle_, &alt_name_info);
    181   CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
    182   if (alt_name) {
    183     int num_entries = alt_name->cAltEntry;
    184     for (int i = 0; i < num_entries; i++) {
    185       // dNSName is an ASN.1 IA5String representing a string of ASCII
    186       // characters, so we can use UTF16ToASCII here.
    187       const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
    188 
    189       if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
    190         dns_names->push_back(base::UTF16ToASCII(entry.pwszDNSName));
    191       } else if (ip_addrs &&
    192                  entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
    193         ip_addrs->push_back(std::string(
    194             reinterpret_cast<const char*>(entry.IPAddress.pbData),
    195             entry.IPAddress.cbData));
    196       }
    197     }
    198   }
    199 }
    200 
    201 PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const {
    202   // Create an in-memory certificate store to hold this certificate and
    203   // any intermediate certificates in |intermediate_ca_certs_|. The store
    204   // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
    205   // until the PCCERT_CONTEXT is freed.
    206   ScopedHCERTSTORE store(CertOpenStore(
    207       CERT_STORE_PROV_MEMORY, 0, NULL,
    208       CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL));
    209   if (!store.get())
    210     return NULL;
    211 
    212   // NOTE: This preserves all of the properties of |os_cert_handle()| except
    213   // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
    214   // properties that hold access to already-opened private keys. If a handle
    215   // has already been unlocked (eg: PIN prompt), then the first time that the
    216   // identity is used for client auth, it may prompt the user again.
    217   PCCERT_CONTEXT primary_cert;
    218   BOOL ok = CertAddCertificateContextToStore(store.get(), os_cert_handle(),
    219                                              CERT_STORE_ADD_ALWAYS,
    220                                              &primary_cert);
    221   if (!ok || !primary_cert)
    222     return NULL;
    223 
    224   for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
    225     CertAddCertificateContextToStore(store.get(), intermediate_ca_certs_[i],
    226                                      CERT_STORE_ADD_ALWAYS, NULL);
    227   }
    228 
    229   // Note: |store| is explicitly not released, as the call to CertCloseStore()
    230   // when |store| goes out of scope will not actually free the store. Instead,
    231   // the store will be freed when |primary_cert| is freed.
    232   return primary_cert;
    233 }
    234 
    235 // static
    236 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
    237                                     std::string* encoded) {
    238   if (!cert_handle || !cert_handle->pbCertEncoded ||
    239       !cert_handle->cbCertEncoded) {
    240     return false;
    241   }
    242   encoded->assign(reinterpret_cast<char*>(cert_handle->pbCertEncoded),
    243                   cert_handle->cbCertEncoded);
    244   return true;
    245 }
    246 
    247 // static
    248 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
    249                                    X509Certificate::OSCertHandle b) {
    250   DCHECK(a && b);
    251   if (a == b)
    252     return true;
    253   return a->cbCertEncoded == b->cbCertEncoded &&
    254       memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
    255 }
    256 
    257 // static
    258 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
    259     const char* data, int length) {
    260   OSCertHandle cert_handle = NULL;
    261   if (!CertAddEncodedCertificateToStore(
    262       NULL, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(data),
    263       length, CERT_STORE_ADD_USE_EXISTING, &cert_handle))
    264     return NULL;
    265 
    266   return cert_handle;
    267 }
    268 
    269 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
    270     const char* data, int length, Format format) {
    271   OSCertHandles results;
    272   switch (format) {
    273     case FORMAT_SINGLE_CERTIFICATE: {
    274       OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
    275       if (handle != NULL)
    276         results.push_back(handle);
    277       break;
    278     }
    279     case FORMAT_PKCS7:
    280       results = ParsePKCS7(data, length);
    281       break;
    282     default:
    283       NOTREACHED() << "Certificate format " << format << " unimplemented";
    284       break;
    285   }
    286 
    287   return results;
    288 }
    289 
    290 // static
    291 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
    292     OSCertHandle cert_handle) {
    293   return CertDuplicateCertificateContext(cert_handle);
    294 }
    295 
    296 // static
    297 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
    298   CertFreeCertificateContext(cert_handle);
    299 }
    300 
    301 // static
    302 SHA1HashValue X509Certificate::CalculateFingerprint(
    303     OSCertHandle cert) {
    304   DCHECK(NULL != cert->pbCertEncoded);
    305   DCHECK_NE(static_cast<DWORD>(0), cert->cbCertEncoded);
    306 
    307   BOOL rv;
    308   SHA1HashValue sha1;
    309   DWORD sha1_size = sizeof(sha1.data);
    310   rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
    311                             cert->cbCertEncoded, sha1.data, &sha1_size);
    312   DCHECK(rv && sha1_size == sizeof(sha1.data));
    313   if (!rv)
    314     memset(sha1.data, 0, sizeof(sha1.data));
    315   return sha1;
    316 }
    317 
    318 // TODO(wtc): This function is implemented with NSS low-level hash
    319 // functions to ensure it is fast.  Reimplement this function with
    320 // CryptoAPI.  May need to cache the HCRYPTPROV to reduce the overhead.
    321 // static
    322 SHA1HashValue X509Certificate::CalculateCAFingerprint(
    323     const OSCertHandles& intermediates) {
    324   SHA1HashValue sha1;
    325   memset(sha1.data, 0, sizeof(sha1.data));
    326 
    327   SHA1Context* sha1_ctx = SHA1_NewContext();
    328   if (!sha1_ctx)
    329     return sha1;
    330   SHA1_Begin(sha1_ctx);
    331   for (size_t i = 0; i < intermediates.size(); ++i) {
    332     PCCERT_CONTEXT ca_cert = intermediates[i];
    333     SHA1_Update(sha1_ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded);
    334   }
    335   unsigned int result_len;
    336   SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH);
    337   SHA1_DestroyContext(sha1_ctx, PR_TRUE);
    338 
    339   return sha1;
    340 }
    341 
    342 // static
    343 X509Certificate::OSCertHandle
    344 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
    345   const char* data;
    346   int length;
    347   if (!pickle_iter->ReadData(&data, &length))
    348     return NULL;
    349 
    350   // Legacy serialized certificates were serialized with extended attributes,
    351   // rather than as DER only. As a result, these serialized certificates are
    352   // not portable across platforms and may have side-effects on Windows due
    353   // to extended attributes being serialized/deserialized -
    354   // http://crbug.com/118706. To avoid deserializing these attributes, write
    355   // the deserialized cert into a temporary cert store and then create a new
    356   // cert from the DER - that is, without attributes.
    357   ScopedHCERTSTORE store(
    358       CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL));
    359   if (!store.get())
    360     return NULL;
    361 
    362   OSCertHandle cert_handle = NULL;
    363   if (!CertAddSerializedElementToStore(
    364           store.get(), reinterpret_cast<const BYTE*>(data), length,
    365           CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
    366           NULL, reinterpret_cast<const void **>(&cert_handle))) {
    367     return NULL;
    368   }
    369 
    370   std::string encoded;
    371   bool ok = GetDEREncoded(cert_handle, &encoded);
    372   FreeOSCertHandle(cert_handle);
    373   cert_handle = NULL;
    374 
    375   if (ok)
    376     cert_handle = CreateOSCertHandleFromBytes(encoded.data(), encoded.size());
    377   return cert_handle;
    378 }
    379 
    380 // static
    381 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
    382                                                 Pickle* pickle) {
    383   return pickle->WriteData(
    384       reinterpret_cast<char*>(cert_handle->pbCertEncoded),
    385       cert_handle->cbCertEncoded);
    386 }
    387 
    388 // static
    389 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
    390                                        size_t* size_bits,
    391                                        PublicKeyType* type) {
    392   *type = kPublicKeyTypeUnknown;
    393   *size_bits = 0;
    394 
    395   PCCRYPT_OID_INFO oid_info = CryptFindOIDInfo(
    396       CRYPT_OID_INFO_OID_KEY,
    397       cert_handle->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId,
    398       CRYPT_PUBKEY_ALG_OID_GROUP_ID);
    399   if (!oid_info)
    400     return;
    401 
    402   CHECK_EQ(oid_info->dwGroupId,
    403            static_cast<DWORD>(CRYPT_PUBKEY_ALG_OID_GROUP_ID));
    404 
    405   *size_bits = CertGetPublicKeyLength(
    406       X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    407       &cert_handle->pCertInfo->SubjectPublicKeyInfo);
    408 
    409   if (IS_SPECIAL_OID_INFO_ALGID(oid_info->Algid)) {
    410     // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
    411     // (0xFFFFFFFE). Need to handle it as a special case.
    412     if (strcmp(oid_info->pszOID, szOID_ECC_PUBLIC_KEY) == 0) {
    413       *type = kPublicKeyTypeECDSA;
    414     } else {
    415       NOTREACHED();
    416     }
    417     return;
    418   }
    419   switch (oid_info->Algid) {
    420     case CALG_RSA_SIGN:
    421     case CALG_RSA_KEYX:
    422       *type = kPublicKeyTypeRSA;
    423       break;
    424     case CALG_DSS_SIGN:
    425       *type = kPublicKeyTypeDSA;
    426       break;
    427     case CALG_ECDSA:
    428       *type = kPublicKeyTypeECDSA;
    429       break;
    430     case CALG_ECDH:
    431       *type = kPublicKeyTypeECDH;
    432       break;
    433   }
    434 }
    435 
    436 bool X509Certificate::IsIssuedByEncoded(
    437     const std::vector<std::string>& valid_issuers) {
    438 
    439   // If the certificate's issuer in the list?
    440   if (IsCertNameBlobInIssuerList(&cert_handle_->pCertInfo->Issuer,
    441                                  valid_issuers)) {
    442     return true;
    443   }
    444   // Otherwise, is any of the intermediate CA subjects in the list?
    445   for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
    446        it != intermediate_ca_certs_.end(); ++it) {
    447     if (IsCertNameBlobInIssuerList(&(*it)->pCertInfo->Issuer,
    448                                    valid_issuers)) {
    449       return true;
    450     }
    451   }
    452 
    453   return false;
    454 }
    455 
    456 }  // namespace net
    457