Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / net / cert / x509_certificate_win.cc
blob808a2847f9bd2287dc53c0838e216fea1e1e90e2
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 "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/pickle.h"
10 #include "base/sha1.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "crypto/capi_util.h"
14 #include "crypto/scoped_capi_types.h"
15 #include "crypto/sha2.h"
16 #include "net/base/net_errors.h"
18 // Implement CalculateChainFingerprint() with our native crypto library.
19 #if defined(USE_OPENSSL)
20 #include <openssl/sha.h>
21 #else
22 #include <blapi.h>
23 #endif
25 #pragma comment(lib, "crypt32.lib")
27 using base::Time;
29 namespace net {
31 namespace {
33 typedef crypto::ScopedCAPIHandle<
34 HCERTSTORE,
35 crypto::CAPIDestroyerWithFlags<HCERTSTORE,
36 CertCloseStore, 0> > ScopedHCERTSTORE;
38 //-----------------------------------------------------------------------------
40 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
41 // structure and stores it in *output.
42 void GetCertSubjectAltName(
43 PCCERT_CONTEXT cert,
44 scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter>* output) {
45 PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
46 cert->pCertInfo->cExtension,
47 cert->pCertInfo->rgExtension);
48 if (!extension)
49 return;
51 CRYPT_DECODE_PARA decode_para;
52 decode_para.cbSize = sizeof(decode_para);
53 decode_para.pfnAlloc = crypto::CryptAlloc;
54 decode_para.pfnFree = crypto::CryptFree;
55 CERT_ALT_NAME_INFO* alt_name_info = NULL;
56 DWORD alt_name_info_size = 0;
57 BOOL rv;
58 rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
59 szOID_SUBJECT_ALT_NAME2,
60 extension->Value.pbData,
61 extension->Value.cbData,
62 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
63 &decode_para,
64 &alt_name_info,
65 &alt_name_info_size);
66 if (rv)
67 output->reset(alt_name_info);
70 void AddCertsFromStore(HCERTSTORE store,
71 X509Certificate::OSCertHandles* results) {
72 PCCERT_CONTEXT cert = NULL;
74 while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
75 PCCERT_CONTEXT to_add = NULL;
76 if (CertAddCertificateContextToStore(
77 NULL, // The cert won't be persisted in any cert store. This breaks
78 // any association the context currently has to |store|, which
79 // allows us, the caller, to safely close |store| without
80 // releasing the cert handles.
81 cert,
82 CERT_STORE_ADD_USE_EXISTING,
83 &to_add) && to_add != NULL) {
84 // When processing stores generated from PKCS#7/PKCS#12 files, it
85 // appears that the order returned is the inverse of the order that it
86 // appeared in the file.
87 // TODO(rsleevi): Ensure this order is consistent across all Win
88 // versions
89 results->insert(results->begin(), to_add);
94 X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) {
95 X509Certificate::OSCertHandles results;
96 CERT_BLOB data_blob;
97 data_blob.cbData = length;
98 data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data));
100 HCERTSTORE out_store = NULL;
102 DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
103 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
104 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED;
106 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types,
107 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
108 &out_store, NULL, NULL) || out_store == NULL) {
109 return results;
112 AddCertsFromStore(out_store, &results);
113 CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG);
115 return results;
118 // Given a CERT_NAME_BLOB, returns true if it appears in a given list,
119 // formatted as a vector of strings holding DER-encoded X.509
120 // DistinguishedName entries.
121 bool IsCertNameBlobInIssuerList(
122 CERT_NAME_BLOB* name_blob,
123 const std::vector<std::string>& issuer_names) {
124 for (std::vector<std::string>::const_iterator it = issuer_names.begin();
125 it != issuer_names.end(); ++it) {
126 CERT_NAME_BLOB issuer_blob;
127 issuer_blob.pbData =
128 reinterpret_cast<BYTE*>(const_cast<char*>(it->data()));
129 issuer_blob.cbData = static_cast<DWORD>(it->length());
131 BOOL rb = CertCompareCertificateName(
132 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuer_blob, name_blob);
133 if (rb)
134 return true;
136 return false;
139 } // namespace
141 void X509Certificate::Initialize() {
142 DCHECK(cert_handle_);
143 subject_.ParseDistinguishedName(cert_handle_->pCertInfo->Subject.pbData,
144 cert_handle_->pCertInfo->Subject.cbData);
145 issuer_.ParseDistinguishedName(cert_handle_->pCertInfo->Issuer.pbData,
146 cert_handle_->pCertInfo->Issuer.cbData);
148 valid_start_ = Time::FromFileTime(cert_handle_->pCertInfo->NotBefore);
149 valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
151 fingerprint_ = CalculateFingerprint(cert_handle_);
152 ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
154 const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
155 scoped_ptr<uint8_t[]> serial_bytes(new uint8_t[serial->cbData]);
156 for (unsigned i = 0; i < serial->cbData; i++)
157 serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
158 serial_number_ = std::string(
159 reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
162 void X509Certificate::GetSubjectAltName(
163 std::vector<std::string>* dns_names,
164 std::vector<std::string>* ip_addrs) const {
165 if (dns_names)
166 dns_names->clear();
167 if (ip_addrs)
168 ip_addrs->clear();
170 if (!cert_handle_)
171 return;
173 scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter> alt_name_info;
174 GetCertSubjectAltName(cert_handle_, &alt_name_info);
175 CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
176 if (alt_name) {
177 int num_entries = alt_name->cAltEntry;
178 for (int i = 0; i < num_entries; i++) {
179 // dNSName is an ASN.1 IA5String representing a string of ASCII
180 // characters, so we can use UTF16ToASCII here.
181 const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
183 if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
184 dns_names->push_back(base::UTF16ToASCII(entry.pwszDNSName));
185 } else if (ip_addrs &&
186 entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
187 ip_addrs->push_back(std::string(
188 reinterpret_cast<const char*>(entry.IPAddress.pbData),
189 entry.IPAddress.cbData));
195 PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const {
196 // Create an in-memory certificate store to hold this certificate and
197 // any intermediate certificates in |intermediate_ca_certs_|. The store
198 // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
199 // until the PCCERT_CONTEXT is freed.
200 ScopedHCERTSTORE store(CertOpenStore(
201 CERT_STORE_PROV_MEMORY, 0, NULL,
202 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL));
203 if (!store.get())
204 return NULL;
206 // NOTE: This preserves all of the properties of |os_cert_handle()| except
207 // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
208 // properties that hold access to already-opened private keys. If a handle
209 // has already been unlocked (eg: PIN prompt), then the first time that the
210 // identity is used for client auth, it may prompt the user again.
211 PCCERT_CONTEXT primary_cert;
212 BOOL ok = CertAddCertificateContextToStore(store.get(), os_cert_handle(),
213 CERT_STORE_ADD_ALWAYS,
214 &primary_cert);
215 if (!ok || !primary_cert)
216 return NULL;
218 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
219 CertAddCertificateContextToStore(store.get(), intermediate_ca_certs_[i],
220 CERT_STORE_ADD_ALWAYS, NULL);
223 // Note: |store| is explicitly not released, as the call to CertCloseStore()
224 // when |store| goes out of scope will not actually free the store. Instead,
225 // the store will be freed when |primary_cert| is freed.
226 return primary_cert;
229 // static
230 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
231 std::string* encoded) {
232 if (!cert_handle || !cert_handle->pbCertEncoded ||
233 !cert_handle->cbCertEncoded) {
234 return false;
236 encoded->assign(reinterpret_cast<char*>(cert_handle->pbCertEncoded),
237 cert_handle->cbCertEncoded);
238 return true;
241 // static
242 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
243 X509Certificate::OSCertHandle b) {
244 DCHECK(a && b);
245 if (a == b)
246 return true;
247 return a->cbCertEncoded == b->cbCertEncoded &&
248 memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
251 // static
252 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
253 const char* data, int length) {
254 OSCertHandle cert_handle = NULL;
255 if (!CertAddEncodedCertificateToStore(
256 NULL, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(data),
257 length, CERT_STORE_ADD_USE_EXISTING, &cert_handle))
258 return NULL;
260 return cert_handle;
263 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
264 const char* data, int length, Format format) {
265 OSCertHandles results;
266 switch (format) {
267 case FORMAT_SINGLE_CERTIFICATE: {
268 OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
269 if (handle != NULL)
270 results.push_back(handle);
271 break;
273 case FORMAT_PKCS7:
274 results = ParsePKCS7(data, length);
275 break;
276 default:
277 NOTREACHED() << "Certificate format " << format << " unimplemented";
278 break;
281 return results;
284 // static
285 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
286 OSCertHandle cert_handle) {
287 return CertDuplicateCertificateContext(cert_handle);
290 // static
291 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
292 CertFreeCertificateContext(cert_handle);
295 // static
296 SHA1HashValue X509Certificate::CalculateFingerprint(
297 OSCertHandle cert) {
298 DCHECK(NULL != cert->pbCertEncoded);
299 DCHECK_NE(static_cast<DWORD>(0), cert->cbCertEncoded);
301 BOOL rv;
302 SHA1HashValue sha1;
303 DWORD sha1_size = sizeof(sha1.data);
304 rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
305 cert->cbCertEncoded, sha1.data, &sha1_size);
306 DCHECK(rv && sha1_size == sizeof(sha1.data));
307 if (!rv)
308 memset(sha1.data, 0, sizeof(sha1.data));
309 return sha1;
312 // static
313 SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) {
314 DCHECK(NULL != cert->pbCertEncoded);
315 DCHECK_NE(0u, cert->cbCertEncoded);
317 SHA256HashValue sha256;
318 size_t sha256_size = sizeof(sha256.data);
320 // Use crypto::SHA256HashString for two reasons:
321 // * < Windows Vista does not have universal SHA-256 support.
322 // * More efficient on Windows > Vista (less overhead since non-default CSP
323 // is not needed).
324 base::StringPiece der_cert(reinterpret_cast<const char*>(cert->pbCertEncoded),
325 cert->cbCertEncoded);
326 crypto::SHA256HashString(der_cert, sha256.data, sha256_size);
327 return sha256;
330 SHA1HashValue X509Certificate::CalculateCAFingerprint(
331 const OSCertHandles& intermediates) {
332 SHA1HashValue sha1;
333 memset(sha1.data, 0, sizeof(sha1.data));
335 #if defined(USE_OPENSSL)
336 SHA_CTX ctx;
337 if (!SHA1_Init(&ctx))
338 return sha1;
339 for (size_t i = 0; i < intermediates.size(); ++i) {
340 PCCERT_CONTEXT ca_cert = intermediates[i];
341 if (!SHA1_Update(&ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded))
342 return sha1;
344 SHA1_Final(sha1.data, &ctx);
345 #else // !USE_OPENSSL
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);
357 #endif // USE_OPENSSL
359 return sha1;
362 // static
363 X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle(
364 base::PickleIterator* pickle_iter) {
365 const char* data;
366 int length;
367 if (!pickle_iter->ReadData(&data, &length))
368 return NULL;
370 // Legacy serialized certificates were serialized with extended attributes,
371 // rather than as DER only. As a result, these serialized certificates are
372 // not portable across platforms and may have side-effects on Windows due
373 // to extended attributes being serialized/deserialized -
374 // http://crbug.com/118706. To avoid deserializing these attributes, write
375 // the deserialized cert into a temporary cert store and then create a new
376 // cert from the DER - that is, without attributes.
377 ScopedHCERTSTORE store(
378 CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL));
379 if (!store.get())
380 return NULL;
382 OSCertHandle cert_handle = NULL;
383 if (!CertAddSerializedElementToStore(
384 store.get(), reinterpret_cast<const BYTE*>(data), length,
385 CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
386 NULL, reinterpret_cast<const void **>(&cert_handle))) {
387 return NULL;
390 std::string encoded;
391 bool ok = GetDEREncoded(cert_handle, &encoded);
392 FreeOSCertHandle(cert_handle);
393 cert_handle = NULL;
395 if (ok)
396 cert_handle = CreateOSCertHandleFromBytes(encoded.data(), encoded.size());
397 return cert_handle;
400 // static
401 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
402 base::Pickle* pickle) {
403 return pickle->WriteData(
404 reinterpret_cast<char*>(cert_handle->pbCertEncoded),
405 cert_handle->cbCertEncoded);
408 // static
409 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
410 size_t* size_bits,
411 PublicKeyType* type) {
412 *type = kPublicKeyTypeUnknown;
413 *size_bits = 0;
415 PCCRYPT_OID_INFO oid_info = CryptFindOIDInfo(
416 CRYPT_OID_INFO_OID_KEY,
417 cert_handle->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId,
418 CRYPT_PUBKEY_ALG_OID_GROUP_ID);
419 if (!oid_info)
420 return;
422 CHECK_EQ(oid_info->dwGroupId,
423 static_cast<DWORD>(CRYPT_PUBKEY_ALG_OID_GROUP_ID));
425 *size_bits = CertGetPublicKeyLength(
426 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
427 &cert_handle->pCertInfo->SubjectPublicKeyInfo);
429 if (IS_SPECIAL_OID_INFO_ALGID(oid_info->Algid)) {
430 // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
431 // (0xFFFFFFFE). Need to handle it as a special case.
432 if (strcmp(oid_info->pszOID, szOID_ECC_PUBLIC_KEY) == 0) {
433 *type = kPublicKeyTypeECDSA;
434 } else {
435 NOTREACHED();
437 return;
439 switch (oid_info->Algid) {
440 case CALG_RSA_SIGN:
441 case CALG_RSA_KEYX:
442 *type = kPublicKeyTypeRSA;
443 break;
444 case CALG_DSS_SIGN:
445 *type = kPublicKeyTypeDSA;
446 break;
447 case CALG_ECDSA:
448 *type = kPublicKeyTypeECDSA;
449 break;
450 case CALG_ECDH:
451 *type = kPublicKeyTypeECDH;
452 break;
456 bool X509Certificate::IsIssuedByEncoded(
457 const std::vector<std::string>& valid_issuers) {
459 // If the certificate's issuer in the list?
460 if (IsCertNameBlobInIssuerList(&cert_handle_->pCertInfo->Issuer,
461 valid_issuers)) {
462 return true;
464 // Otherwise, is any of the intermediate CA subjects in the list?
465 for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
466 it != intermediate_ca_certs_.end(); ++it) {
467 if (IsCertNameBlobInIssuerList(&(*it)->pCertInfo->Issuer,
468 valid_issuers)) {
469 return true;
473 return false;
476 // static
477 bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle) {
478 return !!CryptVerifyCertificateSignatureEx(
479 NULL,
480 X509_ASN_ENCODING,
481 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
482 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT>(cert_handle)),
483 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
484 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT>(cert_handle)),
486 NULL);
489 } // namespace net