Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Side by Side Diff: net/base/x509_certificate_win.cc

Issue 13006020: net: extract net/cert out of net/base (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/x509_certificate_unittest.cc ('k') | net/base/x509_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « net/base/x509_certificate_unittest.cc ('k') | net/base/x509_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698