OLD | NEW |
| (Empty) |
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/base/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/string_util.h" | |
14 #include "base/utf_string_conversions.h" | |
15 #include "crypto/capi_util.h" | |
16 #include "crypto/rsa_private_key.h" | |
17 #include "crypto/scoped_capi_types.h" | |
18 #include "net/base/net_errors.h" | |
19 | |
20 #pragma comment(lib, "crypt32.lib") | |
21 | |
22 using base::Time; | |
23 | |
24 namespace net { | |
25 | |
26 namespace { | |
27 | |
28 typedef crypto::ScopedCAPIHandle< | |
29 HCERTSTORE, | |
30 crypto::CAPIDestroyerWithFlags<HCERTSTORE, | |
31 CertCloseStore, 0> > ScopedHCERTSTORE; | |
32 | |
33 void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded, | |
34 SYSTEMTIME* system_time) { | |
35 system_time->wYear = exploded.year; | |
36 system_time->wMonth = exploded.month; | |
37 system_time->wDayOfWeek = exploded.day_of_week; | |
38 system_time->wDay = exploded.day_of_month; | |
39 system_time->wHour = exploded.hour; | |
40 system_time->wMinute = exploded.minute; | |
41 system_time->wSecond = exploded.second; | |
42 system_time->wMilliseconds = exploded.millisecond; | |
43 } | |
44 | |
45 //----------------------------------------------------------------------------- | |
46 | |
47 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO | |
48 // structure and stores it in *output. | |
49 void GetCertSubjectAltName(PCCERT_CONTEXT cert, | |
50 scoped_ptr_malloc<CERT_ALT_NAME_INFO>* 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_array<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 // static | |
169 X509Certificate* X509Certificate::CreateSelfSigned( | |
170 crypto::RSAPrivateKey* key, | |
171 const std::string& subject, | |
172 uint32 serial_number, | |
173 base::TimeDelta valid_duration) { | |
174 // Get the ASN.1 encoding of the certificate subject. | |
175 std::wstring w_subject = ASCIIToWide(subject); | |
176 DWORD encoded_subject_length = 0; | |
177 if (!CertStrToName( | |
178 X509_ASN_ENCODING, | |
179 w_subject.c_str(), | |
180 CERT_X500_NAME_STR, NULL, NULL, &encoded_subject_length, NULL)) { | |
181 return NULL; | |
182 } | |
183 | |
184 scoped_array<BYTE> encoded_subject(new BYTE[encoded_subject_length]); | |
185 if (!CertStrToName( | |
186 X509_ASN_ENCODING, | |
187 w_subject.c_str(), | |
188 CERT_X500_NAME_STR, NULL, | |
189 encoded_subject.get(), | |
190 &encoded_subject_length, NULL)) { | |
191 return NULL; | |
192 } | |
193 | |
194 CERT_NAME_BLOB subject_name; | |
195 memset(&subject_name, 0, sizeof(subject_name)); | |
196 subject_name.cbData = encoded_subject_length; | |
197 subject_name.pbData = encoded_subject.get(); | |
198 | |
199 CRYPT_ALGORITHM_IDENTIFIER sign_algo; | |
200 memset(&sign_algo, 0, sizeof(sign_algo)); | |
201 sign_algo.pszObjId = szOID_RSA_SHA1RSA; | |
202 | |
203 base::Time not_before = base::Time::Now(); | |
204 base::Time not_after = not_before + valid_duration; | |
205 base::Time::Exploded exploded; | |
206 | |
207 // Create the system time structs representing our exploded times. | |
208 not_before.UTCExplode(&exploded); | |
209 SYSTEMTIME start_time; | |
210 ExplodedTimeToSystemTime(exploded, &start_time); | |
211 not_after.UTCExplode(&exploded); | |
212 SYSTEMTIME end_time; | |
213 ExplodedTimeToSystemTime(exploded, &end_time); | |
214 | |
215 PCCERT_CONTEXT cert_handle = | |
216 CertCreateSelfSignCertificate(key->provider(), &subject_name, | |
217 CERT_CREATE_SELFSIGN_NO_KEY_INFO, NULL, | |
218 &sign_algo, &start_time, &end_time, NULL); | |
219 DCHECK(cert_handle) << "Failed to create self-signed certificate: " | |
220 << GetLastError(); | |
221 if (!cert_handle) | |
222 return NULL; | |
223 | |
224 X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles()); | |
225 FreeOSCertHandle(cert_handle); | |
226 return cert; | |
227 } | |
228 | |
229 void X509Certificate::GetSubjectAltName( | |
230 std::vector<std::string>* dns_names, | |
231 std::vector<std::string>* ip_addrs) const { | |
232 if (dns_names) | |
233 dns_names->clear(); | |
234 if (ip_addrs) | |
235 ip_addrs->clear(); | |
236 | |
237 if (!cert_handle_) | |
238 return; | |
239 | |
240 scoped_ptr_malloc<CERT_ALT_NAME_INFO> alt_name_info; | |
241 GetCertSubjectAltName(cert_handle_, &alt_name_info); | |
242 CERT_ALT_NAME_INFO* alt_name = alt_name_info.get(); | |
243 if (alt_name) { | |
244 int num_entries = alt_name->cAltEntry; | |
245 for (int i = 0; i < num_entries; i++) { | |
246 // dNSName is an ASN.1 IA5String representing a string of ASCII | |
247 // characters, so we can use WideToASCII here. | |
248 const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i]; | |
249 | |
250 if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) { | |
251 dns_names->push_back(WideToASCII(entry.pwszDNSName)); | |
252 } else if (ip_addrs && | |
253 entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) { | |
254 ip_addrs->push_back(std::string( | |
255 reinterpret_cast<const char*>(entry.IPAddress.pbData), | |
256 entry.IPAddress.cbData)); | |
257 } | |
258 } | |
259 } | |
260 } | |
261 | |
262 PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const { | |
263 // Create an in-memory certificate store to hold this certificate and | |
264 // any intermediate certificates in |intermediate_ca_certs_|. The store | |
265 // will be referenced in the returned PCCERT_CONTEXT, and will not be freed | |
266 // until the PCCERT_CONTEXT is freed. | |
267 ScopedHCERTSTORE store(CertOpenStore( | |
268 CERT_STORE_PROV_MEMORY, 0, NULL, | |
269 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL)); | |
270 if (!store.get()) | |
271 return NULL; | |
272 | |
273 // NOTE: This preserves all of the properties of |os_cert_handle()| except | |
274 // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two | |
275 // properties that hold access to already-opened private keys. If a handle | |
276 // has already been unlocked (eg: PIN prompt), then the first time that the | |
277 // identity is used for client auth, it may prompt the user again. | |
278 PCCERT_CONTEXT primary_cert; | |
279 BOOL ok = CertAddCertificateContextToStore(store.get(), os_cert_handle(), | |
280 CERT_STORE_ADD_ALWAYS, | |
281 &primary_cert); | |
282 if (!ok || !primary_cert) | |
283 return NULL; | |
284 | |
285 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { | |
286 CertAddCertificateContextToStore(store.get(), intermediate_ca_certs_[i], | |
287 CERT_STORE_ADD_ALWAYS, NULL); | |
288 } | |
289 | |
290 // Note: |store| is explicitly not released, as the call to CertCloseStore() | |
291 // when |store| goes out of scope will not actually free the store. Instead, | |
292 // the store will be freed when |primary_cert| is freed. | |
293 return primary_cert; | |
294 } | |
295 | |
296 // static | |
297 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle, | |
298 std::string* encoded) { | |
299 if (!cert_handle->pbCertEncoded || !cert_handle->cbCertEncoded) | |
300 return false; | |
301 encoded->assign(reinterpret_cast<char*>(cert_handle->pbCertEncoded), | |
302 cert_handle->cbCertEncoded); | |
303 return true; | |
304 } | |
305 | |
306 // static | |
307 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, | |
308 X509Certificate::OSCertHandle b) { | |
309 DCHECK(a && b); | |
310 if (a == b) | |
311 return true; | |
312 return a->cbCertEncoded == b->cbCertEncoded && | |
313 memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0; | |
314 } | |
315 | |
316 // static | |
317 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( | |
318 const char* data, int length) { | |
319 OSCertHandle cert_handle = NULL; | |
320 if (!CertAddEncodedCertificateToStore( | |
321 NULL, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(data), | |
322 length, CERT_STORE_ADD_USE_EXISTING, &cert_handle)) | |
323 return NULL; | |
324 | |
325 return cert_handle; | |
326 } | |
327 | |
328 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( | |
329 const char* data, int length, Format format) { | |
330 OSCertHandles results; | |
331 switch (format) { | |
332 case FORMAT_SINGLE_CERTIFICATE: { | |
333 OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); | |
334 if (handle != NULL) | |
335 results.push_back(handle); | |
336 break; | |
337 } | |
338 case FORMAT_PKCS7: | |
339 results = ParsePKCS7(data, length); | |
340 break; | |
341 default: | |
342 NOTREACHED() << "Certificate format " << format << " unimplemented"; | |
343 break; | |
344 } | |
345 | |
346 return results; | |
347 } | |
348 | |
349 // static | |
350 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( | |
351 OSCertHandle cert_handle) { | |
352 return CertDuplicateCertificateContext(cert_handle); | |
353 } | |
354 | |
355 // static | |
356 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { | |
357 CertFreeCertificateContext(cert_handle); | |
358 } | |
359 | |
360 // static | |
361 SHA1HashValue X509Certificate::CalculateFingerprint( | |
362 OSCertHandle cert) { | |
363 DCHECK(NULL != cert->pbCertEncoded); | |
364 DCHECK_NE(static_cast<DWORD>(0), cert->cbCertEncoded); | |
365 | |
366 BOOL rv; | |
367 SHA1HashValue sha1; | |
368 DWORD sha1_size = sizeof(sha1.data); | |
369 rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded, | |
370 cert->cbCertEncoded, sha1.data, &sha1_size); | |
371 DCHECK(rv && sha1_size == sizeof(sha1.data)); | |
372 if (!rv) | |
373 memset(sha1.data, 0, sizeof(sha1.data)); | |
374 return sha1; | |
375 } | |
376 | |
377 // TODO(wtc): This function is implemented with NSS low-level hash | |
378 // functions to ensure it is fast. Reimplement this function with | |
379 // CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead. | |
380 // static | |
381 SHA1HashValue X509Certificate::CalculateCAFingerprint( | |
382 const OSCertHandles& intermediates) { | |
383 SHA1HashValue sha1; | |
384 memset(sha1.data, 0, sizeof(sha1.data)); | |
385 | |
386 SHA1Context* sha1_ctx = SHA1_NewContext(); | |
387 if (!sha1_ctx) | |
388 return sha1; | |
389 SHA1_Begin(sha1_ctx); | |
390 for (size_t i = 0; i < intermediates.size(); ++i) { | |
391 PCCERT_CONTEXT ca_cert = intermediates[i]; | |
392 SHA1_Update(sha1_ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded); | |
393 } | |
394 unsigned int result_len; | |
395 SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH); | |
396 SHA1_DestroyContext(sha1_ctx, PR_TRUE); | |
397 | |
398 return sha1; | |
399 } | |
400 | |
401 // static | |
402 X509Certificate::OSCertHandle | |
403 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) { | |
404 const char* data; | |
405 int length; | |
406 if (!pickle_iter->ReadData(&data, &length)) | |
407 return NULL; | |
408 | |
409 // Legacy serialized certificates were serialized with extended attributes, | |
410 // rather than as DER only. As a result, these serialized certificates are | |
411 // not portable across platforms and may have side-effects on Windows due | |
412 // to extended attributes being serialized/deserialized - | |
413 // http://crbug.com/118706. To avoid deserializing these attributes, write | |
414 // the deserialized cert into a temporary cert store and then create a new | |
415 // cert from the DER - that is, without attributes. | |
416 ScopedHCERTSTORE store( | |
417 CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL)); | |
418 if (!store.get()) | |
419 return NULL; | |
420 | |
421 OSCertHandle cert_handle = NULL; | |
422 if (!CertAddSerializedElementToStore( | |
423 store.get(), reinterpret_cast<const BYTE*>(data), length, | |
424 CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, | |
425 NULL, reinterpret_cast<const void **>(&cert_handle))) { | |
426 return NULL; | |
427 } | |
428 | |
429 std::string encoded; | |
430 bool ok = GetDEREncoded(cert_handle, &encoded); | |
431 FreeOSCertHandle(cert_handle); | |
432 cert_handle = NULL; | |
433 | |
434 if (ok) | |
435 cert_handle = CreateOSCertHandleFromBytes(encoded.data(), encoded.size()); | |
436 return cert_handle; | |
437 } | |
438 | |
439 // static | |
440 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle, | |
441 Pickle* pickle) { | |
442 return pickle->WriteData( | |
443 reinterpret_cast<char*>(cert_handle->pbCertEncoded), | |
444 cert_handle->cbCertEncoded); | |
445 } | |
446 | |
447 // static | |
448 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle, | |
449 size_t* size_bits, | |
450 PublicKeyType* type) { | |
451 *type = kPublicKeyTypeUnknown; | |
452 *size_bits = 0; | |
453 | |
454 PCCRYPT_OID_INFO oid_info = CryptFindOIDInfo( | |
455 CRYPT_OID_INFO_OID_KEY, | |
456 cert_handle->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, | |
457 CRYPT_PUBKEY_ALG_OID_GROUP_ID); | |
458 if (!oid_info) | |
459 return; | |
460 | |
461 CHECK_EQ(oid_info->dwGroupId, | |
462 static_cast<DWORD>(CRYPT_PUBKEY_ALG_OID_GROUP_ID)); | |
463 | |
464 *size_bits = CertGetPublicKeyLength( | |
465 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, | |
466 &cert_handle->pCertInfo->SubjectPublicKeyInfo); | |
467 | |
468 switch (oid_info->Algid) { | |
469 case CALG_RSA_SIGN: | |
470 case CALG_RSA_KEYX: | |
471 *type = kPublicKeyTypeRSA; | |
472 break; | |
473 case CALG_DSS_SIGN: | |
474 *type = kPublicKeyTypeDSA; | |
475 break; | |
476 case CALG_ECDSA: | |
477 *type = kPublicKeyTypeECDSA; | |
478 break; | |
479 case CALG_ECDH: | |
480 *type = kPublicKeyTypeECDH; | |
481 break; | |
482 } | |
483 } | |
484 | |
485 bool X509Certificate::IsIssuedByEncoded( | |
486 const std::vector<std::string>& valid_issuers) { | |
487 | |
488 // If the certificate's issuer in the list? | |
489 if (IsCertNameBlobInIssuerList(&cert_handle_->pCertInfo->Issuer, | |
490 valid_issuers)) { | |
491 return true; | |
492 } | |
493 // Otherwise, is any of the intermediate CA subjects in the list? | |
494 for (OSCertHandles::iterator it = intermediate_ca_certs_.begin(); | |
495 it != intermediate_ca_certs_.end(); ++it) { | |
496 if (IsCertNameBlobInIssuerList(&(*it)->pCertInfo->Issuer, | |
497 valid_issuers)) { | |
498 return true; | |
499 } | |
500 } | |
501 | |
502 return false; | |
503 } | |
504 | |
505 } // namespace net | |
OLD | NEW |