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