1 // Copyright (c) 2011 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/base/x509_certificate.h"
6
7 #include "base/lazy_instance.h"
8 #include "base/logging.h"
9 #include "base/pickle.h"
10 #include "base/sha1.h"
11 #include "base/string_tokenizer.h"
12 #include "base/string_util.h"
13 #include "base/utf_string_conversions.h"
14 #include "crypto/rsa_private_key.h"
15 #include "crypto/scoped_capi_types.h"
16 #include "net/base/asn1_util.h"
17 #include "net/base/cert_status_flags.h"
18 #include "net/base/cert_verify_result.h"
19 #include "net/base/ev_root_ca_metadata.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/scoped_cert_chain_context.h"
22 #include "net/base/test_root_certs.h"
23 #include "net/base/x509_certificate_known_roots_win.h"
24
25 #pragma comment(lib, "crypt32.lib")
26
27 using base::Time;
28
29 namespace net {
30
31 namespace {
32
33 typedef crypto::ScopedCAPIHandle<
34 HCERTSTORE,
35 crypto::CAPIDestroyerWithFlags<HCERTSTORE,
36 CertCloseStore, 0> > ScopedHCERTSTORE;
37
38 struct FreeChainEngineFunctor {
operator ()net::__anone1c389e00111::FreeChainEngineFunctor39 void operator()(HCERTCHAINENGINE engine) const {
40 if (engine)
41 CertFreeCertificateChainEngine(engine);
42 }
43 };
44
45 typedef crypto::ScopedCAPIHandle<HCERTCHAINENGINE, FreeChainEngineFunctor>
46 ScopedHCERTCHAINENGINE;
47
48 //-----------------------------------------------------------------------------
49
50 // TODO(wtc): This is a copy of the MapSecurityError function in
51 // ssl_client_socket_win.cc. Another function that maps Windows error codes
52 // to our network error codes is WinInetUtil::OSErrorToNetError. We should
53 // eliminate the code duplication.
MapSecurityError(SECURITY_STATUS err)54 int MapSecurityError(SECURITY_STATUS err) {
55 // There are numerous security error codes, but these are the ones we thus
56 // far find interesting.
57 switch (err) {
58 case SEC_E_WRONG_PRINCIPAL: // Schannel
59 case CERT_E_CN_NO_MATCH: // CryptoAPI
60 return ERR_CERT_COMMON_NAME_INVALID;
61 case SEC_E_UNTRUSTED_ROOT: // Schannel
62 case CERT_E_UNTRUSTEDROOT: // CryptoAPI
63 return ERR_CERT_AUTHORITY_INVALID;
64 case SEC_E_CERT_EXPIRED: // Schannel
65 case CERT_E_EXPIRED: // CryptoAPI
66 return ERR_CERT_DATE_INVALID;
67 case CRYPT_E_NO_REVOCATION_CHECK:
68 return ERR_CERT_NO_REVOCATION_MECHANISM;
69 case CRYPT_E_REVOCATION_OFFLINE:
70 return ERR_CERT_UNABLE_TO_CHECK_REVOCATION;
71 case CRYPT_E_REVOKED: // Schannel and CryptoAPI
72 return ERR_CERT_REVOKED;
73 case SEC_E_CERT_UNKNOWN:
74 case CERT_E_ROLE:
75 return ERR_CERT_INVALID;
76 case CERT_E_WRONG_USAGE:
77 // TODO(wtc): Should we add ERR_CERT_WRONG_USAGE?
78 return ERR_CERT_INVALID;
79 // We received an unexpected_message or illegal_parameter alert message
80 // from the server.
81 case SEC_E_ILLEGAL_MESSAGE:
82 return ERR_SSL_PROTOCOL_ERROR;
83 case SEC_E_ALGORITHM_MISMATCH:
84 return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
85 case SEC_E_INVALID_HANDLE:
86 return ERR_UNEXPECTED;
87 case SEC_E_OK:
88 return OK;
89 default:
90 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
91 return ERR_FAILED;
92 }
93 }
94
95 // Map the errors in the chain_context->TrustStatus.dwErrorStatus returned by
96 // CertGetCertificateChain to our certificate status flags.
MapCertChainErrorStatusToCertStatus(DWORD error_status)97 int MapCertChainErrorStatusToCertStatus(DWORD error_status) {
98 int cert_status = 0;
99
100 // We don't include CERT_TRUST_IS_NOT_TIME_NESTED because it's obsolete and
101 // we wouldn't consider it an error anyway
102 const DWORD kDateInvalidErrors = CERT_TRUST_IS_NOT_TIME_VALID |
103 CERT_TRUST_CTL_IS_NOT_TIME_VALID;
104 if (error_status & kDateInvalidErrors)
105 cert_status |= CERT_STATUS_DATE_INVALID;
106
107 const DWORD kAuthorityInvalidErrors = CERT_TRUST_IS_UNTRUSTED_ROOT |
108 CERT_TRUST_IS_EXPLICIT_DISTRUST |
109 CERT_TRUST_IS_PARTIAL_CHAIN;
110 if (error_status & kAuthorityInvalidErrors)
111 cert_status |= CERT_STATUS_AUTHORITY_INVALID;
112
113 if ((error_status & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) &&
114 !(error_status & CERT_TRUST_IS_OFFLINE_REVOCATION))
115 cert_status |= CERT_STATUS_NO_REVOCATION_MECHANISM;
116
117 if (error_status & CERT_TRUST_IS_OFFLINE_REVOCATION)
118 cert_status |= CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
119
120 if (error_status & CERT_TRUST_IS_REVOKED)
121 cert_status |= CERT_STATUS_REVOKED;
122
123 const DWORD kWrongUsageErrors = CERT_TRUST_IS_NOT_VALID_FOR_USAGE |
124 CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
125 if (error_status & kWrongUsageErrors) {
126 // TODO(wtc): Should we add CERT_STATUS_WRONG_USAGE?
127 cert_status |= CERT_STATUS_INVALID;
128 }
129
130 // The rest of the errors.
131 const DWORD kCertInvalidErrors =
132 CERT_TRUST_IS_NOT_SIGNATURE_VALID |
133 CERT_TRUST_IS_CYCLIC |
134 CERT_TRUST_INVALID_EXTENSION |
135 CERT_TRUST_INVALID_POLICY_CONSTRAINTS |
136 CERT_TRUST_INVALID_BASIC_CONSTRAINTS |
137 CERT_TRUST_INVALID_NAME_CONSTRAINTS |
138 CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID |
139 CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT |
140 CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT |
141 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT |
142 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT |
143 CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY |
144 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT;
145 if (error_status & kCertInvalidErrors)
146 cert_status |= CERT_STATUS_INVALID;
147
148 return cert_status;
149 }
150
ExplodedTimeToSystemTime(const base::Time::Exploded & exploded,SYSTEMTIME * system_time)151 void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded,
152 SYSTEMTIME* system_time) {
153 system_time->wYear = exploded.year;
154 system_time->wMonth = exploded.month;
155 system_time->wDayOfWeek = exploded.day_of_week;
156 system_time->wDay = exploded.day_of_month;
157 system_time->wHour = exploded.hour;
158 system_time->wMinute = exploded.minute;
159 system_time->wSecond = exploded.second;
160 system_time->wMilliseconds = exploded.millisecond;
161 }
162
163 //-----------------------------------------------------------------------------
164
165 // Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the
166 // WINAPI calling convention.
MyCryptAlloc(size_t size)167 void* WINAPI MyCryptAlloc(size_t size) {
168 return malloc(size);
169 }
170
MyCryptFree(void * p)171 void WINAPI MyCryptFree(void* p) {
172 free(p);
173 }
174
175 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
176 // structure and stores it in *output.
GetCertSubjectAltName(PCCERT_CONTEXT cert,scoped_ptr_malloc<CERT_ALT_NAME_INFO> * output)177 void GetCertSubjectAltName(PCCERT_CONTEXT cert,
178 scoped_ptr_malloc<CERT_ALT_NAME_INFO>* output) {
179 PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
180 cert->pCertInfo->cExtension,
181 cert->pCertInfo->rgExtension);
182 if (!extension)
183 return;
184
185 CRYPT_DECODE_PARA decode_para;
186 decode_para.cbSize = sizeof(decode_para);
187 decode_para.pfnAlloc = MyCryptAlloc;
188 decode_para.pfnFree = MyCryptFree;
189 CERT_ALT_NAME_INFO* alt_name_info = NULL;
190 DWORD alt_name_info_size = 0;
191 BOOL rv;
192 rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
193 szOID_SUBJECT_ALT_NAME2,
194 extension->Value.pbData,
195 extension->Value.cbData,
196 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
197 &decode_para,
198 &alt_name_info,
199 &alt_name_info_size);
200 if (rv)
201 output->reset(alt_name_info);
202 }
203
204 // Returns true if any common name in the certificate's Subject field contains
205 // a NULL character.
CertSubjectCommonNameHasNull(PCCERT_CONTEXT cert)206 bool CertSubjectCommonNameHasNull(PCCERT_CONTEXT cert) {
207 CRYPT_DECODE_PARA decode_para;
208 decode_para.cbSize = sizeof(decode_para);
209 decode_para.pfnAlloc = MyCryptAlloc;
210 decode_para.pfnFree = MyCryptFree;
211 CERT_NAME_INFO* name_info = NULL;
212 DWORD name_info_size = 0;
213 BOOL rv;
214 rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
215 X509_NAME,
216 cert->pCertInfo->Subject.pbData,
217 cert->pCertInfo->Subject.cbData,
218 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
219 &decode_para,
220 &name_info,
221 &name_info_size);
222 if (rv) {
223 scoped_ptr_malloc<CERT_NAME_INFO> scoped_name_info(name_info);
224
225 // The Subject field may have multiple common names. According to the
226 // "PKI Layer Cake" paper, CryptoAPI uses every common name in the
227 // Subject field, so we inspect every common name.
228 //
229 // From RFC 5280:
230 // X520CommonName ::= CHOICE {
231 // teletexString TeletexString (SIZE (1..ub-common-name)),
232 // printableString PrintableString (SIZE (1..ub-common-name)),
233 // universalString UniversalString (SIZE (1..ub-common-name)),
234 // utf8String UTF8String (SIZE (1..ub-common-name)),
235 // bmpString BMPString (SIZE (1..ub-common-name)) }
236 //
237 // We also check IA5String and VisibleString.
238 for (DWORD i = 0; i < name_info->cRDN; ++i) {
239 PCERT_RDN rdn = &name_info->rgRDN[i];
240 for (DWORD j = 0; j < rdn->cRDNAttr; ++j) {
241 PCERT_RDN_ATTR rdn_attr = &rdn->rgRDNAttr[j];
242 if (strcmp(rdn_attr->pszObjId, szOID_COMMON_NAME) == 0) {
243 switch (rdn_attr->dwValueType) {
244 // After the CryptoAPI ASN.1 security vulnerabilities described in
245 // http://www.microsoft.com/technet/security/Bulletin/MS09-056.mspx
246 // were patched, we get CERT_RDN_ENCODED_BLOB for a common name
247 // that contains a NULL character.
248 case CERT_RDN_ENCODED_BLOB:
249 break;
250 // Array of 8-bit characters.
251 case CERT_RDN_PRINTABLE_STRING:
252 case CERT_RDN_TELETEX_STRING:
253 case CERT_RDN_IA5_STRING:
254 case CERT_RDN_VISIBLE_STRING:
255 for (DWORD k = 0; k < rdn_attr->Value.cbData; ++k) {
256 if (rdn_attr->Value.pbData[k] == '\0')
257 return true;
258 }
259 break;
260 // Array of 16-bit characters.
261 case CERT_RDN_BMP_STRING:
262 case CERT_RDN_UTF8_STRING: {
263 DWORD num_wchars = rdn_attr->Value.cbData / 2;
264 wchar_t* common_name =
265 reinterpret_cast<wchar_t*>(rdn_attr->Value.pbData);
266 for (DWORD k = 0; k < num_wchars; ++k) {
267 if (common_name[k] == L'\0')
268 return true;
269 }
270 break;
271 }
272 // Array of ints (32-bit).
273 case CERT_RDN_UNIVERSAL_STRING: {
274 DWORD num_ints = rdn_attr->Value.cbData / 4;
275 int* common_name =
276 reinterpret_cast<int*>(rdn_attr->Value.pbData);
277 for (DWORD k = 0; k < num_ints; ++k) {
278 if (common_name[k] == 0)
279 return true;
280 }
281 break;
282 }
283 default:
284 NOTREACHED();
285 break;
286 }
287 }
288 }
289 }
290 }
291 return false;
292 }
293
294 // Saves some information about the certificate chain chain_context in
295 // *verify_result. The caller MUST initialize *verify_result before calling
296 // this function.
GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,CertVerifyResult * verify_result)297 void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,
298 CertVerifyResult* verify_result) {
299 PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0];
300 int num_elements = first_chain->cElement;
301 PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
302
303 // Each chain starts with the end entity certificate (i = 0) and ends with
304 // the root CA certificate (i = num_elements - 1). Do not inspect the
305 // signature algorithm of the root CA certificate because the signature on
306 // the trust anchor is not important.
307 for (int i = 0; i < num_elements - 1; ++i) {
308 PCCERT_CONTEXT cert = element[i]->pCertContext;
309 const char* algorithm = cert->pCertInfo->SignatureAlgorithm.pszObjId;
310 if (strcmp(algorithm, szOID_RSA_MD5RSA) == 0) {
311 // md5WithRSAEncryption: 1.2.840.113549.1.1.4
312 verify_result->has_md5 = true;
313 if (i != 0)
314 verify_result->has_md5_ca = true;
315 } else if (strcmp(algorithm, szOID_RSA_MD2RSA) == 0) {
316 // md2WithRSAEncryption: 1.2.840.113549.1.1.2
317 verify_result->has_md2 = true;
318 if (i != 0)
319 verify_result->has_md2_ca = true;
320 } else if (strcmp(algorithm, szOID_RSA_MD4RSA) == 0) {
321 // md4WithRSAEncryption: 1.2.840.113549.1.1.3
322 verify_result->has_md4 = true;
323 }
324 }
325 }
326
327 // Decodes the cert's certificatePolicies extension into a CERT_POLICIES_INFO
328 // structure and stores it in *output.
GetCertPoliciesInfo(PCCERT_CONTEXT cert,scoped_ptr_malloc<CERT_POLICIES_INFO> * output)329 void GetCertPoliciesInfo(PCCERT_CONTEXT cert,
330 scoped_ptr_malloc<CERT_POLICIES_INFO>* output) {
331 PCERT_EXTENSION extension = CertFindExtension(szOID_CERT_POLICIES,
332 cert->pCertInfo->cExtension,
333 cert->pCertInfo->rgExtension);
334 if (!extension)
335 return;
336
337 CRYPT_DECODE_PARA decode_para;
338 decode_para.cbSize = sizeof(decode_para);
339 decode_para.pfnAlloc = MyCryptAlloc;
340 decode_para.pfnFree = MyCryptFree;
341 CERT_POLICIES_INFO* policies_info = NULL;
342 DWORD policies_info_size = 0;
343 BOOL rv;
344 rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
345 szOID_CERT_POLICIES,
346 extension->Value.pbData,
347 extension->Value.cbData,
348 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
349 &decode_para,
350 &policies_info,
351 &policies_info_size);
352 if (rv)
353 output->reset(policies_info);
354 }
355
356 // Helper function to parse a principal from a WinInet description of that
357 // principal.
ParsePrincipal(const std::string & description,CertPrincipal * principal)358 void ParsePrincipal(const std::string& description,
359 CertPrincipal* principal) {
360 // The description of the principal is a string with each LDAP value on
361 // a separate line.
362 const std::string kDelimiters("\r\n");
363
364 std::vector<std::string> common_names, locality_names, state_names,
365 country_names;
366
367 // TODO(jcampan): add business_category and serial_number.
368 const std::string kPrefixes[] = { std::string("CN="),
369 std::string("L="),
370 std::string("S="),
371 std::string("C="),
372 std::string("STREET="),
373 std::string("O="),
374 std::string("OU="),
375 std::string("DC=") };
376
377 std::vector<std::string>* values[] = {
378 &common_names, &locality_names,
379 &state_names, &country_names,
380 &(principal->street_addresses),
381 &(principal->organization_names),
382 &(principal->organization_unit_names),
383 &(principal->domain_components) };
384 DCHECK(arraysize(kPrefixes) == arraysize(values));
385
386 StringTokenizer str_tok(description, kDelimiters);
387 while (str_tok.GetNext()) {
388 std::string entry = str_tok.token();
389 for (int i = 0; i < arraysize(kPrefixes); i++) {
390 if (!entry.compare(0, kPrefixes[i].length(), kPrefixes[i])) {
391 std::string value = entry.substr(kPrefixes[i].length());
392 // Remove enclosing double-quotes if any.
393 if (value.size() >= 2 &&
394 value[0] == '"' && value[value.size() - 1] == '"')
395 value = value.substr(1, value.size() - 2);
396 values[i]->push_back(value);
397 break;
398 }
399 }
400 }
401
402 // We don't expect to have more than one CN, L, S, and C. If there is more
403 // than one entry for CN, L, S, and C, we will use the first entry. Although
404 // RFC 2818 Section 3.1 says the "most specific" CN should be used, that term
405 // has been removed in draft-saintandre-tls-server-id-check, which requires
406 // that the Subject field contains only one CN. So it is fine for us to just
407 // use the first CN.
408 std::vector<std::string>* single_value_lists[4] = {
409 &common_names, &locality_names, &state_names, &country_names };
410 std::string* single_values[4] = {
411 &principal->common_name, &principal->locality_name,
412 &principal->state_or_province_name, &principal->country_name };
413 for (int i = 0; i < arraysize(single_value_lists); ++i) {
414 int length = static_cast<int>(single_value_lists[i]->size());
415 if (!single_value_lists[i]->empty())
416 *(single_values[i]) = (*(single_value_lists[i]))[0];
417 }
418 }
419
AddCertsFromStore(HCERTSTORE store,X509Certificate::OSCertHandles * results)420 void AddCertsFromStore(HCERTSTORE store,
421 X509Certificate::OSCertHandles* results) {
422 PCCERT_CONTEXT cert = NULL;
423
424 while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
425 PCCERT_CONTEXT to_add = NULL;
426 if (CertAddCertificateContextToStore(
427 NULL, // The cert won't be persisted in any cert store. This breaks
428 // any association the context currently has to |store|, which
429 // allows us, the caller, to safely close |store| without
430 // releasing the cert handles.
431 cert,
432 CERT_STORE_ADD_USE_EXISTING,
433 &to_add) && to_add != NULL) {
434 // When processing stores generated from PKCS#7/PKCS#12 files, it
435 // appears that the order returned is the inverse of the order that it
436 // appeared in the file.
437 // TODO(rsleevi): Ensure this order is consistent across all Win
438 // versions
439 results->insert(results->begin(), to_add);
440 }
441 }
442 }
443
ParsePKCS7(const char * data,size_t length)444 X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) {
445 X509Certificate::OSCertHandles results;
446 CERT_BLOB data_blob;
447 data_blob.cbData = length;
448 data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data));
449
450 HCERTSTORE out_store = NULL;
451
452 DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
453 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
454 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED;
455
456 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types,
457 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
458 &out_store, NULL, NULL) || out_store == NULL) {
459 return results;
460 }
461
462 AddCertsFromStore(out_store, &results);
463 CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG);
464
465 return results;
466 }
467
AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain,std::vector<SHA1Fingerprint> * hashes)468 void AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain,
469 std::vector<SHA1Fingerprint>* hashes) {
470 if (chain->cChain == 0)
471 return;
472
473 PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0];
474 PCERT_CHAIN_ELEMENT* const element = first_chain->rgpElement;
475
476 const DWORD num_elements = first_chain->cElement;
477 for (DWORD i = 0; i < num_elements; i++) {
478 PCCERT_CONTEXT cert = element[i]->pCertContext;
479
480 base::StringPiece der_bytes(
481 reinterpret_cast<const char*>(cert->pbCertEncoded),
482 cert->cbCertEncoded);
483 base::StringPiece spki_bytes;
484 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
485 continue;
486
487 SHA1Fingerprint hash;
488 base::SHA1HashBytes(reinterpret_cast<const uint8*>(spki_bytes.data()),
489 spki_bytes.size(), hash.data);
490 hashes->push_back(hash);
491 }
492 }
493
494 } // namespace
495
Initialize()496 void X509Certificate::Initialize() {
497 std::wstring subject_info;
498 std::wstring issuer_info;
499 DWORD name_size;
500 DCHECK(cert_handle_);
501 name_size = CertNameToStr(cert_handle_->dwCertEncodingType,
502 &cert_handle_->pCertInfo->Subject,
503 CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
504 NULL, 0);
505 name_size = CertNameToStr(cert_handle_->dwCertEncodingType,
506 &cert_handle_->pCertInfo->Subject,
507 CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
508 WriteInto(&subject_info, name_size), name_size);
509 name_size = CertNameToStr(cert_handle_->dwCertEncodingType,
510 &cert_handle_->pCertInfo->Issuer,
511 CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
512 NULL, 0);
513 name_size = CertNameToStr(cert_handle_->dwCertEncodingType,
514 &cert_handle_->pCertInfo->Issuer,
515 CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
516 WriteInto(&issuer_info, name_size), name_size);
517 ParsePrincipal(WideToUTF8(subject_info), &subject_);
518 ParsePrincipal(WideToUTF8(issuer_info), &issuer_);
519
520 valid_start_ = Time::FromFileTime(cert_handle_->pCertInfo->NotBefore);
521 valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
522
523 fingerprint_ = CalculateFingerprint(cert_handle_);
524
525 const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
526 scoped_array<uint8> serial_bytes(new uint8[serial->cbData]);
527 for (unsigned i = 0; i < serial->cbData; i++)
528 serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
529 serial_number_ = std::string(
530 reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
531 // Remove leading zeros.
532 while (serial_number_.size() > 1 && serial_number_[0] == 0)
533 serial_number_ = serial_number_.substr(1, serial_number_.size() - 1);
534 }
535
536 // IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
537 // which we recognise as a standard root.
538 // static
IsIssuedByKnownRoot(PCCERT_CHAIN_CONTEXT chain_context)539 bool X509Certificate::IsIssuedByKnownRoot(PCCERT_CHAIN_CONTEXT chain_context) {
540 PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0];
541 int num_elements = first_chain->cElement;
542 if (num_elements < 1)
543 return false;
544 PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
545 PCCERT_CONTEXT cert = element[num_elements - 1]->pCertContext;
546
547 SHA1Fingerprint hash = CalculateFingerprint(cert);
548 return IsSHA1HashInSortedArray(
549 hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
550 }
551
552 // static
CreateSelfSigned(crypto::RSAPrivateKey * key,const std::string & subject,uint32 serial_number,base::TimeDelta valid_duration)553 X509Certificate* X509Certificate::CreateSelfSigned(
554 crypto::RSAPrivateKey* key,
555 const std::string& subject,
556 uint32 serial_number,
557 base::TimeDelta valid_duration) {
558 // Get the ASN.1 encoding of the certificate subject.
559 std::wstring w_subject = ASCIIToWide(subject);
560 DWORD encoded_subject_length = 0;
561 if (!CertStrToName(
562 X509_ASN_ENCODING,
563 w_subject.c_str(),
564 CERT_X500_NAME_STR, NULL, NULL, &encoded_subject_length, NULL)) {
565 return NULL;
566 }
567
568 scoped_array<BYTE> encoded_subject(new BYTE[encoded_subject_length]);
569 if (!CertStrToName(
570 X509_ASN_ENCODING,
571 w_subject.c_str(),
572 CERT_X500_NAME_STR, NULL,
573 encoded_subject.get(),
574 &encoded_subject_length, NULL)) {
575 return NULL;
576 }
577
578 CERT_NAME_BLOB subject_name;
579 memset(&subject_name, 0, sizeof(subject_name));
580 subject_name.cbData = encoded_subject_length;
581 subject_name.pbData = encoded_subject.get();
582
583 CRYPT_ALGORITHM_IDENTIFIER sign_algo;
584 memset(&sign_algo, 0, sizeof(sign_algo));
585 sign_algo.pszObjId = szOID_RSA_SHA1RSA;
586
587 base::Time not_before = base::Time::Now();
588 base::Time not_after = not_before + valid_duration;
589 base::Time::Exploded exploded;
590
591 // Create the system time structs representing our exploded times.
592 not_before.UTCExplode(&exploded);
593 SYSTEMTIME start_time;
594 ExplodedTimeToSystemTime(exploded, &start_time);
595 not_after.UTCExplode(&exploded);
596 SYSTEMTIME end_time;
597 ExplodedTimeToSystemTime(exploded, &end_time);
598
599 PCCERT_CONTEXT cert_handle =
600 CertCreateSelfSignCertificate(key->provider(), &subject_name,
601 CERT_CREATE_SELFSIGN_NO_KEY_INFO, NULL,
602 &sign_algo, &start_time, &end_time, NULL);
603 DCHECK(cert_handle) << "Failed to create self-signed certificate: "
604 << GetLastError();
605 if (!cert_handle)
606 return NULL;
607
608 X509Certificate* cert = CreateFromHandle(cert_handle,
609 SOURCE_LONE_CERT_IMPORT,
610 OSCertHandles());
611 FreeOSCertHandle(cert_handle);
612 return cert;
613 }
614
GetDNSNames(std::vector<std::string> * dns_names) const615 void X509Certificate::GetDNSNames(std::vector<std::string>* dns_names) const {
616 dns_names->clear();
617 if (cert_handle_) {
618 scoped_ptr_malloc<CERT_ALT_NAME_INFO> alt_name_info;
619 GetCertSubjectAltName(cert_handle_, &alt_name_info);
620 CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
621 if (alt_name) {
622 int num_entries = alt_name->cAltEntry;
623 for (int i = 0; i < num_entries; i++) {
624 // dNSName is an ASN.1 IA5String representing a string of ASCII
625 // characters, so we can use WideToASCII here.
626 if (alt_name->rgAltEntry[i].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME)
627 dns_names->push_back(
628 WideToASCII(alt_name->rgAltEntry[i].pwszDNSName));
629 }
630 }
631 }
632 if (dns_names->empty())
633 dns_names->push_back(subject_.common_name);
634 }
635
636 class GlobalCertStore {
637 public:
cert_store()638 HCERTSTORE cert_store() {
639 return cert_store_;
640 }
641
642 private:
643 friend struct base::DefaultLazyInstanceTraits<GlobalCertStore>;
644
GlobalCertStore()645 GlobalCertStore()
646 : cert_store_(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL)) {
647 }
648
~GlobalCertStore()649 ~GlobalCertStore() {
650 CertCloseStore(cert_store_, 0 /* flags */);
651 }
652
653 const HCERTSTORE cert_store_;
654
655 DISALLOW_COPY_AND_ASSIGN(GlobalCertStore);
656 };
657
658 static base::LazyInstance<GlobalCertStore> g_cert_store(
659 base::LINKER_INITIALIZED);
660
661 // static
cert_store()662 HCERTSTORE X509Certificate::cert_store() {
663 return g_cert_store.Get().cert_store();
664 }
665
Verify(const std::string & hostname,int flags,CertVerifyResult * verify_result) const666 int X509Certificate::Verify(const std::string& hostname,
667 int flags,
668 CertVerifyResult* verify_result) const {
669 verify_result->Reset();
670 if (!cert_handle_)
671 return ERR_UNEXPECTED;
672
673 if (IsBlacklisted()) {
674 verify_result->cert_status |= CERT_STATUS_REVOKED;
675 return ERR_CERT_REVOKED;
676 }
677
678 // Build and validate certificate chain.
679
680 CERT_CHAIN_PARA chain_para;
681 memset(&chain_para, 0, sizeof(chain_para));
682 chain_para.cbSize = sizeof(chain_para);
683 // ExtendedKeyUsage.
684 // We still need to request szOID_SERVER_GATED_CRYPTO and szOID_SGC_NETSCAPE
685 // today because some certificate chains need them. IE also requests these
686 // two usages.
687 static const LPSTR usage[] = {
688 szOID_PKIX_KP_SERVER_AUTH,
689 szOID_SERVER_GATED_CRYPTO,
690 szOID_SGC_NETSCAPE
691 };
692 chain_para.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
693 chain_para.RequestedUsage.Usage.cUsageIdentifier = arraysize(usage);
694 chain_para.RequestedUsage.Usage.rgpszUsageIdentifier =
695 const_cast<LPSTR*>(usage);
696 // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains.
697 DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT;
698 if (flags & VERIFY_REV_CHECKING_ENABLED) {
699 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
700 chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
701 } else {
702 chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
703 // EV requires revocation checking.
704 flags &= ~VERIFY_EV_CERT;
705 }
706
707 // Get the certificatePolicies extension of the certificate.
708 scoped_ptr_malloc<CERT_POLICIES_INFO> policies_info;
709 LPSTR ev_policy_oid = NULL;
710 if (flags & VERIFY_EV_CERT) {
711 GetCertPoliciesInfo(cert_handle_, &policies_info);
712 if (policies_info.get()) {
713 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
714 for (DWORD i = 0; i < policies_info->cPolicyInfo; ++i) {
715 LPSTR policy_oid = policies_info->rgPolicyInfo[i].pszPolicyIdentifier;
716 if (metadata->IsEVPolicyOID(policy_oid)) {
717 ev_policy_oid = policy_oid;
718 chain_para.RequestedIssuancePolicy.dwType = USAGE_MATCH_TYPE_AND;
719 chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 1;
720 chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier =
721 &ev_policy_oid;
722 break;
723 }
724 }
725 }
726 }
727
728 // For non-test scenarios, use the default HCERTCHAINENGINE, NULL, which
729 // corresponds to HCCE_CURRENT_USER and is is initialized as needed by
730 // crypt32. However, when testing, it is necessary to create a new
731 // HCERTCHAINENGINE and use that instead. This is because each
732 // HCERTCHAINENGINE maintains a cache of information about certificates
733 // encountered, and each test run may modify the trust status of a
734 // certificate.
735 ScopedHCERTCHAINENGINE chain_engine(NULL);
736 if (TestRootCerts::HasInstance())
737 chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine());
738
739 PCCERT_CHAIN_CONTEXT chain_context;
740 // IE passes a non-NULL pTime argument that specifies the current system
741 // time. IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the
742 // chain_flags argument.
743 if (!CertGetCertificateChain(
744 chain_engine,
745 cert_handle_,
746 NULL, // current system time
747 cert_handle_->hCertStore,
748 &chain_para,
749 chain_flags,
750 NULL, // reserved
751 &chain_context)) {
752 return MapSecurityError(GetLastError());
753 }
754 if (chain_context->TrustStatus.dwErrorStatus &
755 CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
756 ev_policy_oid = NULL;
757 chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 0;
758 chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = NULL;
759 CertFreeCertificateChain(chain_context);
760 if (!CertGetCertificateChain(
761 chain_engine,
762 cert_handle_,
763 NULL, // current system time
764 cert_handle_->hCertStore,
765 &chain_para,
766 chain_flags,
767 NULL, // reserved
768 &chain_context)) {
769 return MapSecurityError(GetLastError());
770 }
771 }
772 ScopedCertChainContext scoped_chain_context(chain_context);
773
774 GetCertChainInfo(chain_context, verify_result);
775 verify_result->cert_status |= MapCertChainErrorStatusToCertStatus(
776 chain_context->TrustStatus.dwErrorStatus);
777
778 // Treat certificates signed using broken signature algorithms as invalid.
779 if (verify_result->has_md4)
780 verify_result->cert_status |= CERT_STATUS_INVALID;
781
782 // Flag certificates signed using weak signature algorithms.
783 if (verify_result->has_md2)
784 verify_result->cert_status |= CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
785
786 // Flag certificates that have a Subject common name with a NULL character.
787 if (CertSubjectCommonNameHasNull(cert_handle_))
788 verify_result->cert_status |= CERT_STATUS_INVALID;
789
790 std::wstring wstr_hostname = ASCIIToWide(hostname);
791
792 SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para;
793 memset(&extra_policy_para, 0, sizeof(extra_policy_para));
794 extra_policy_para.cbSize = sizeof(extra_policy_para);
795 extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
796 extra_policy_para.fdwChecks = 0;
797 extra_policy_para.pwszServerName =
798 const_cast<wchar_t*>(wstr_hostname.c_str());
799
800 CERT_CHAIN_POLICY_PARA policy_para;
801 memset(&policy_para, 0, sizeof(policy_para));
802 policy_para.cbSize = sizeof(policy_para);
803 policy_para.dwFlags = 0;
804 policy_para.pvExtraPolicyPara = &extra_policy_para;
805
806 CERT_CHAIN_POLICY_STATUS policy_status;
807 memset(&policy_status, 0, sizeof(policy_status));
808 policy_status.cbSize = sizeof(policy_status);
809
810 if (!CertVerifyCertificateChainPolicy(
811 CERT_CHAIN_POLICY_SSL,
812 chain_context,
813 &policy_para,
814 &policy_status)) {
815 return MapSecurityError(GetLastError());
816 }
817
818 if (policy_status.dwError) {
819 verify_result->cert_status |= MapNetErrorToCertStatus(
820 MapSecurityError(policy_status.dwError));
821
822 // CertVerifyCertificateChainPolicy reports only one error (in
823 // policy_status.dwError) if the certificate has multiple errors.
824 // CertGetCertificateChain doesn't report certificate name mismatch, so
825 // CertVerifyCertificateChainPolicy is the only function that can report
826 // certificate name mismatch.
827 //
828 // To prevent a potential certificate name mismatch from being hidden by
829 // some other certificate error, if we get any other certificate error,
830 // we call CertVerifyCertificateChainPolicy again, ignoring all other
831 // certificate errors. Both extra_policy_para.fdwChecks and
832 // policy_para.dwFlags allow us to ignore certificate errors, so we set
833 // them both.
834 if (policy_status.dwError != CERT_E_CN_NO_MATCH) {
835 const DWORD extra_ignore_flags =
836 0x00000080 | // SECURITY_FLAG_IGNORE_REVOCATION
837 0x00000100 | // SECURITY_FLAG_IGNORE_UNKNOWN_CA
838 0x00002000 | // SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
839 0x00000200; // SECURITY_FLAG_IGNORE_WRONG_USAGE
840 extra_policy_para.fdwChecks = extra_ignore_flags;
841 const DWORD ignore_flags =
842 CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS |
843 CERT_CHAIN_POLICY_IGNORE_INVALID_BASIC_CONSTRAINTS_FLAG |
844 CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG |
845 CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG |
846 CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG |
847 CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG |
848 CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS |
849 CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG |
850 CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG |
851 CERT_CHAIN_POLICY_IGNORE_NOT_SUPPORTED_CRITICAL_EXT_FLAG |
852 CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG;
853 policy_para.dwFlags = ignore_flags;
854 if (!CertVerifyCertificateChainPolicy(
855 CERT_CHAIN_POLICY_SSL,
856 chain_context,
857 &policy_para,
858 &policy_status)) {
859 return MapSecurityError(GetLastError());
860 }
861 if (policy_status.dwError) {
862 verify_result->cert_status |= MapNetErrorToCertStatus(
863 MapSecurityError(policy_status.dwError));
864 }
865 }
866 }
867
868 // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
869 // compatible with WinHTTP, which doesn't report this error (bug 3004).
870 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
871
872 if (IsCertStatusError(verify_result->cert_status))
873 return MapCertStatusToNetError(verify_result->cert_status);
874
875 AppendPublicKeyHashes(chain_context, &verify_result->public_key_hashes);
876 verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(chain_context);
877
878 if (ev_policy_oid && CheckEV(chain_context, ev_policy_oid))
879 verify_result->cert_status |= CERT_STATUS_IS_EV;
880
881 if (IsPublicKeyBlacklisted(verify_result->public_key_hashes)) {
882 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
883 return MapCertStatusToNetError(verify_result->cert_status);
884 }
885
886 return OK;
887 }
888
GetDEREncoded(std::string * encoded)889 bool X509Certificate::GetDEREncoded(std::string* encoded) {
890 if (!cert_handle_->pbCertEncoded || !cert_handle_->cbCertEncoded)
891 return false;
892 encoded->clear();
893 encoded->append(reinterpret_cast<char*>(cert_handle_->pbCertEncoded),
894 cert_handle_->cbCertEncoded);
895 return true;
896 }
897
898 // Returns true if the certificate is an extended-validation certificate.
899 //
900 // This function checks the certificatePolicies extensions of the
901 // certificates in the certificate chain according to Section 7 (pp. 11-12)
902 // of the EV Certificate Guidelines Version 1.0 at
903 // http://cabforum.org/EV_Certificate_Guidelines.pdf.
CheckEV(PCCERT_CHAIN_CONTEXT chain_context,const char * policy_oid) const904 bool X509Certificate::CheckEV(PCCERT_CHAIN_CONTEXT chain_context,
905 const char* policy_oid) const {
906 DCHECK(chain_context->cChain != 0);
907 // If the cert doesn't match any of the policies, the
908 // CERT_TRUST_IS_NOT_VALID_FOR_USAGE bit (0x10) in
909 // chain_context->TrustStatus.dwErrorStatus is set.
910 DWORD error_status = chain_context->TrustStatus.dwErrorStatus;
911 DWORD info_status = chain_context->TrustStatus.dwInfoStatus;
912 if (!chain_context->cChain || error_status != CERT_TRUST_NO_ERROR)
913 return false;
914
915 // Check the end certificate simple chain (chain_context->rgpChain[0]).
916 // If the end certificate's certificatePolicies extension contains the
917 // EV policy OID of the root CA, return true.
918 PCERT_CHAIN_ELEMENT* element = chain_context->rgpChain[0]->rgpElement;
919 int num_elements = chain_context->rgpChain[0]->cElement;
920 if (num_elements < 2)
921 return false;
922
923 // Look up the EV policy OID of the root CA.
924 PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext;
925 SHA1Fingerprint fingerprint = CalculateFingerprint(root_cert);
926 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
927 return metadata->HasEVPolicyOID(fingerprint, policy_oid);
928 }
929
VerifyEV() const930 bool X509Certificate::VerifyEV() const {
931 // We don't call this private method, but we do need to implement it because
932 // it's defined in x509_certificate.h. We perform EV checking in the
933 // Verify() above.
934 NOTREACHED();
935 return false;
936 }
937
938 // static
IsSameOSCert(X509Certificate::OSCertHandle a,X509Certificate::OSCertHandle b)939 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
940 X509Certificate::OSCertHandle b) {
941 DCHECK(a && b);
942 if (a == b)
943 return true;
944 return a->cbCertEncoded == b->cbCertEncoded &&
945 memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
946 }
947
948 // static
CreateOSCertHandleFromBytes(const char * data,int length)949 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
950 const char* data, int length) {
951 OSCertHandle cert_handle = NULL;
952 if (!CertAddEncodedCertificateToStore(
953 NULL, // the cert won't be persisted in any cert store
954 X509_ASN_ENCODING,
955 reinterpret_cast<const BYTE*>(data), length,
956 CERT_STORE_ADD_USE_EXISTING,
957 &cert_handle))
958 return NULL;
959
960 return cert_handle;
961 }
962
CreateOSCertHandlesFromBytes(const char * data,int length,Format format)963 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
964 const char* data, int length, Format format) {
965 OSCertHandles results;
966 switch (format) {
967 case FORMAT_SINGLE_CERTIFICATE: {
968 OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
969 if (handle != NULL)
970 results.push_back(handle);
971 break;
972 }
973 case FORMAT_PKCS7:
974 results = ParsePKCS7(data, length);
975 break;
976 default:
977 NOTREACHED() << "Certificate format " << format << " unimplemented";
978 break;
979 }
980
981 return results;
982 }
983
984
985 // static
DupOSCertHandle(OSCertHandle cert_handle)986 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
987 OSCertHandle cert_handle) {
988 return CertDuplicateCertificateContext(cert_handle);
989 }
990
991 // static
FreeOSCertHandle(OSCertHandle cert_handle)992 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
993 CertFreeCertificateContext(cert_handle);
994 }
995
996 // static
CalculateFingerprint(OSCertHandle cert)997 SHA1Fingerprint X509Certificate::CalculateFingerprint(
998 OSCertHandle cert) {
999 DCHECK(NULL != cert->pbCertEncoded);
1000 DCHECK(0 != cert->cbCertEncoded);
1001
1002 BOOL rv;
1003 SHA1Fingerprint sha1;
1004 DWORD sha1_size = sizeof(sha1.data);
1005 rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
1006 cert->cbCertEncoded, sha1.data, &sha1_size);
1007 DCHECK(rv && sha1_size == sizeof(sha1.data));
1008 if (!rv)
1009 memset(sha1.data, 0, sizeof(sha1.data));
1010 return sha1;
1011 }
1012
1013 // static
1014 X509Certificate::OSCertHandle
ReadCertHandleFromPickle(const Pickle & pickle,void ** pickle_iter)1015 X509Certificate::ReadCertHandleFromPickle(const Pickle& pickle,
1016 void** pickle_iter) {
1017 const char* data;
1018 int length;
1019 if (!pickle.ReadData(pickle_iter, &data, &length))
1020 return NULL;
1021
1022 OSCertHandle cert_handle = NULL;
1023 if (!CertAddSerializedElementToStore(
1024 NULL, // the cert won't be persisted in any cert store
1025 reinterpret_cast<const BYTE*>(data), length,
1026 CERT_STORE_ADD_USE_EXISTING, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
1027 NULL, reinterpret_cast<const void **>(&cert_handle))) {
1028 return NULL;
1029 }
1030
1031 return cert_handle;
1032 }
1033
1034 // static
WriteCertHandleToPickle(OSCertHandle cert_handle,Pickle * pickle)1035 bool X509Certificate::WriteCertHandleToPickle(OSCertHandle cert_handle,
1036 Pickle* pickle) {
1037 DWORD length = 0;
1038 if (!CertSerializeCertificateStoreElement(cert_handle, 0, NULL, &length))
1039 return false;
1040
1041 std::vector<BYTE> buffer(length);
1042 // Serialize |cert_handle| in a way that will preserve any extended
1043 // attributes set on the handle, such as the location to the certificate's
1044 // private key.
1045 if (!CertSerializeCertificateStoreElement(cert_handle, 0, &buffer[0],
1046 &length)) {
1047 return false;
1048 }
1049
1050 return pickle->WriteData(reinterpret_cast<const char*>(&buffer[0]),
1051 length);
1052 }
1053
1054 } // namespace net
1055