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