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( 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 76void 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 100X509Certificate::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. 127bool 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 147void 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 168void 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 201PCCERT_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 236bool 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 248bool 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 258X509Certificate::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 269X509Certificate::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 291X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( 292 OSCertHandle cert_handle) { 293 return CertDuplicateCertificateContext(cert_handle); 294} 295 296// static 297void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { 298 CertFreeCertificateContext(cert_handle); 299} 300 301// static 302SHA1HashValue 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 322SHA1HashValue 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 343X509Certificate::OSCertHandle 344X509Certificate::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 381bool 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 389void 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 436bool 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