summaryrefslogtreecommitdiffstats
path: root/net/cert/x509_certificate_win.cc
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-29 21:48:11 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-29 21:48:11 +0000
commit6e7845aed4759ab35d722ce0551b5a90d21e7640 (patch)
treef29a3f007f7ded842d2096446ff7ecaf186cb362 /net/cert/x509_certificate_win.cc
parenta6b4f91d970aa2b71b0f3552dbc11e94f7650fd5 (diff)
downloadchromium_src-6e7845aed4759ab35d722ce0551b5a90d21e7640.zip
chromium_src-6e7845aed4759ab35d722ce0551b5a90d21e7640.tar.gz
chromium_src-6e7845aed4759ab35d722ce0551b5a90d21e7640.tar.bz2
net: extract net/cert out of net/base
This introduces the following dependency of net/base on things outside: net/base/openssl_client_key_store.cc:#include "net/cert/x509_certificate.h" BUG=70818 Review URL: https://codereview.chromium.org/13006020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191450 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/cert/x509_certificate_win.cc')
-rw-r--r--net/cert/x509_certificate_win.cc505
1 files changed, 505 insertions, 0 deletions
diff --git a/net/cert/x509_certificate_win.cc b/net/cert/x509_certificate_win.cc
new file mode 100644
index 0000000..6a72328
--- /dev/null
+++ b/net/cert/x509_certificate_win.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <blapi.h> // Implement CalculateChainFingerprint() with NSS.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/sha1.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "crypto/capi_util.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/scoped_capi_types.h"
+#include "net/base/net_errors.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+using base::Time;
+
+namespace net {
+
+namespace {
+
+typedef crypto::ScopedCAPIHandle<
+ HCERTSTORE,
+ crypto::CAPIDestroyerWithFlags<HCERTSTORE,
+ CertCloseStore, 0> > ScopedHCERTSTORE;
+
+void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded,
+ SYSTEMTIME* system_time) {
+ system_time->wYear = exploded.year;
+ system_time->wMonth = exploded.month;
+ system_time->wDayOfWeek = exploded.day_of_week;
+ system_time->wDay = exploded.day_of_month;
+ system_time->wHour = exploded.hour;
+ system_time->wMinute = exploded.minute;
+ system_time->wSecond = exploded.second;
+ system_time->wMilliseconds = exploded.millisecond;
+}
+
+//-----------------------------------------------------------------------------
+
+// Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
+// structure and stores it in *output.
+void GetCertSubjectAltName(PCCERT_CONTEXT cert,
+ scoped_ptr_malloc<CERT_ALT_NAME_INFO>* output) {
+ PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
+ cert->pCertInfo->cExtension,
+ cert->pCertInfo->rgExtension);
+ if (!extension)
+ return;
+
+ CRYPT_DECODE_PARA decode_para;
+ decode_para.cbSize = sizeof(decode_para);
+ decode_para.pfnAlloc = crypto::CryptAlloc;
+ decode_para.pfnFree = crypto::CryptFree;
+ CERT_ALT_NAME_INFO* alt_name_info = NULL;
+ DWORD alt_name_info_size = 0;
+ BOOL rv;
+ rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ szOID_SUBJECT_ALT_NAME2,
+ extension->Value.pbData,
+ extension->Value.cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decode_para,
+ &alt_name_info,
+ &alt_name_info_size);
+ if (rv)
+ output->reset(alt_name_info);
+}
+
+void AddCertsFromStore(HCERTSTORE store,
+ X509Certificate::OSCertHandles* results) {
+ PCCERT_CONTEXT cert = NULL;
+
+ while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
+ PCCERT_CONTEXT to_add = NULL;
+ if (CertAddCertificateContextToStore(
+ NULL, // The cert won't be persisted in any cert store. This breaks
+ // any association the context currently has to |store|, which
+ // allows us, the caller, to safely close |store| without
+ // releasing the cert handles.
+ cert,
+ CERT_STORE_ADD_USE_EXISTING,
+ &to_add) && to_add != NULL) {
+ // When processing stores generated from PKCS#7/PKCS#12 files, it
+ // appears that the order returned is the inverse of the order that it
+ // appeared in the file.
+ // TODO(rsleevi): Ensure this order is consistent across all Win
+ // versions
+ results->insert(results->begin(), to_add);
+ }
+ }
+}
+
+X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) {
+ X509Certificate::OSCertHandles results;
+ CERT_BLOB data_blob;
+ data_blob.cbData = length;
+ data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data));
+
+ HCERTSTORE out_store = NULL;
+
+ DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED;
+
+ if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types,
+ CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
+ &out_store, NULL, NULL) || out_store == NULL) {
+ return results;
+ }
+
+ AddCertsFromStore(out_store, &results);
+ CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG);
+
+ return results;
+}
+
+// Given a CERT_NAME_BLOB, returns true if it appears in a given list,
+// formatted as a vector of strings holding DER-encoded X.509
+// DistinguishedName entries.
+bool IsCertNameBlobInIssuerList(
+ CERT_NAME_BLOB* name_blob,
+ const std::vector<std::string>& issuer_names) {
+ for (std::vector<std::string>::const_iterator it = issuer_names.begin();
+ it != issuer_names.end(); ++it) {
+ CERT_NAME_BLOB issuer_blob;
+ issuer_blob.pbData =
+ reinterpret_cast<BYTE*>(const_cast<char*>(it->data()));
+ issuer_blob.cbData = static_cast<DWORD>(it->length());
+
+ BOOL rb = CertCompareCertificateName(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuer_blob, name_blob);
+ if (rb)
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+void X509Certificate::Initialize() {
+ DCHECK(cert_handle_);
+ subject_.ParseDistinguishedName(cert_handle_->pCertInfo->Subject.pbData,
+ cert_handle_->pCertInfo->Subject.cbData);
+ issuer_.ParseDistinguishedName(cert_handle_->pCertInfo->Issuer.pbData,
+ cert_handle_->pCertInfo->Issuer.cbData);
+
+ valid_start_ = Time::FromFileTime(cert_handle_->pCertInfo->NotBefore);
+ valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
+
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+
+ const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
+ scoped_array<uint8> serial_bytes(new uint8[serial->cbData]);
+ for (unsigned i = 0; i < serial->cbData; i++)
+ serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
+ serial_number_ = std::string(
+ reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
+}
+
+// static
+X509Certificate* X509Certificate::CreateSelfSigned(
+ crypto::RSAPrivateKey* key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::TimeDelta valid_duration) {
+ // Get the ASN.1 encoding of the certificate subject.
+ std::wstring w_subject = ASCIIToWide(subject);
+ DWORD encoded_subject_length = 0;
+ if (!CertStrToName(
+ X509_ASN_ENCODING,
+ w_subject.c_str(),
+ CERT_X500_NAME_STR, NULL, NULL, &encoded_subject_length, NULL)) {
+ return NULL;
+ }
+
+ scoped_array<BYTE> encoded_subject(new BYTE[encoded_subject_length]);
+ if (!CertStrToName(
+ X509_ASN_ENCODING,
+ w_subject.c_str(),
+ CERT_X500_NAME_STR, NULL,
+ encoded_subject.get(),
+ &encoded_subject_length, NULL)) {
+ return NULL;
+ }
+
+ CERT_NAME_BLOB subject_name;
+ memset(&subject_name, 0, sizeof(subject_name));
+ subject_name.cbData = encoded_subject_length;
+ subject_name.pbData = encoded_subject.get();
+
+ CRYPT_ALGORITHM_IDENTIFIER sign_algo;
+ memset(&sign_algo, 0, sizeof(sign_algo));
+ sign_algo.pszObjId = szOID_RSA_SHA1RSA;
+
+ base::Time not_before = base::Time::Now();
+ base::Time not_after = not_before + valid_duration;
+ base::Time::Exploded exploded;
+
+ // Create the system time structs representing our exploded times.
+ not_before.UTCExplode(&exploded);
+ SYSTEMTIME start_time;
+ ExplodedTimeToSystemTime(exploded, &start_time);
+ not_after.UTCExplode(&exploded);
+ SYSTEMTIME end_time;
+ ExplodedTimeToSystemTime(exploded, &end_time);
+
+ PCCERT_CONTEXT cert_handle =
+ CertCreateSelfSignCertificate(key->provider(), &subject_name,
+ CERT_CREATE_SELFSIGN_NO_KEY_INFO, NULL,
+ &sign_algo, &start_time, &end_time, NULL);
+ DCHECK(cert_handle) << "Failed to create self-signed certificate: "
+ << GetLastError();
+ if (!cert_handle)
+ return NULL;
+
+ X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles());
+ FreeOSCertHandle(cert_handle);
+ return cert;
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ if (!cert_handle_)
+ return;
+
+ scoped_ptr_malloc<CERT_ALT_NAME_INFO> alt_name_info;
+ GetCertSubjectAltName(cert_handle_, &alt_name_info);
+ CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
+ if (alt_name) {
+ int num_entries = alt_name->cAltEntry;
+ for (int i = 0; i < num_entries; i++) {
+ // dNSName is an ASN.1 IA5String representing a string of ASCII
+ // characters, so we can use WideToASCII here.
+ const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
+
+ if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
+ dns_names->push_back(WideToASCII(entry.pwszDNSName));
+ } else if (ip_addrs &&
+ entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
+ ip_addrs->push_back(std::string(
+ reinterpret_cast<const char*>(entry.IPAddress.pbData),
+ entry.IPAddress.cbData));
+ }
+ }
+ }
+}
+
+PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const {
+ // Create an in-memory certificate store to hold this certificate and
+ // any intermediate certificates in |intermediate_ca_certs_|. The store
+ // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
+ // until the PCCERT_CONTEXT is freed.
+ ScopedHCERTSTORE store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, 0, NULL,
+ CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL));
+ if (!store.get())
+ return NULL;
+
+ // NOTE: This preserves all of the properties of |os_cert_handle()| except
+ // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
+ // properties that hold access to already-opened private keys. If a handle
+ // has already been unlocked (eg: PIN prompt), then the first time that the
+ // identity is used for client auth, it may prompt the user again.
+ PCCERT_CONTEXT primary_cert;
+ BOOL ok = CertAddCertificateContextToStore(store.get(), os_cert_handle(),
+ CERT_STORE_ADD_ALWAYS,
+ &primary_cert);
+ if (!ok || !primary_cert)
+ return NULL;
+
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
+ CertAddCertificateContextToStore(store.get(), intermediate_ca_certs_[i],
+ CERT_STORE_ADD_ALWAYS, NULL);
+ }
+
+ // Note: |store| is explicitly not released, as the call to CertCloseStore()
+ // when |store| goes out of scope will not actually free the store. Instead,
+ // the store will be freed when |primary_cert| is freed.
+ return primary_cert;
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+ std::string* encoded) {
+ if (!cert_handle->pbCertEncoded || !cert_handle->cbCertEncoded)
+ return false;
+ encoded->assign(reinterpret_cast<char*>(cert_handle->pbCertEncoded),
+ cert_handle->cbCertEncoded);
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+ return a->cbCertEncoded == b->cbCertEncoded &&
+ memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ OSCertHandle cert_handle = NULL;
+ if (!CertAddEncodedCertificateToStore(
+ NULL, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(data),
+ length, CERT_STORE_ADD_USE_EXISTING, &cert_handle))
+ return NULL;
+
+ return cert_handle;
+}
+
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data, int length, Format format) {
+ OSCertHandles results;
+ switch (format) {
+ case FORMAT_SINGLE_CERTIFICATE: {
+ OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
+ if (handle != NULL)
+ results.push_back(handle);
+ break;
+ }
+ case FORMAT_PKCS7:
+ results = ParsePKCS7(data, length);
+ break;
+ default:
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+
+ return results;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle cert_handle) {
+ return CertDuplicateCertificateContext(cert_handle);
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ CertFreeCertificateContext(cert_handle);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(
+ OSCertHandle cert) {
+ DCHECK(NULL != cert->pbCertEncoded);
+ DCHECK_NE(static_cast<DWORD>(0), cert->cbCertEncoded);
+
+ BOOL rv;
+ SHA1HashValue sha1;
+ DWORD sha1_size = sizeof(sha1.data);
+ rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
+ cert->cbCertEncoded, sha1.data, &sha1_size);
+ DCHECK(rv && sha1_size == sizeof(sha1.data));
+ if (!rv)
+ memset(sha1.data, 0, sizeof(sha1.data));
+ return sha1;
+}
+
+// TODO(wtc): This function is implemented with NSS low-level hash
+// functions to ensure it is fast. Reimplement this function with
+// CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead.
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ SHA1Context* sha1_ctx = SHA1_NewContext();
+ if (!sha1_ctx)
+ return sha1;
+ SHA1_Begin(sha1_ctx);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ PCCERT_CONTEXT ca_cert = intermediates[i];
+ SHA1_Update(sha1_ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded);
+ }
+ unsigned int result_len;
+ SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH);
+ SHA1_DestroyContext(sha1_ctx, PR_TRUE);
+
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return NULL;
+
+ // Legacy serialized certificates were serialized with extended attributes,
+ // rather than as DER only. As a result, these serialized certificates are
+ // not portable across platforms and may have side-effects on Windows due
+ // to extended attributes being serialized/deserialized -
+ // http://crbug.com/118706. To avoid deserializing these attributes, write
+ // the deserialized cert into a temporary cert store and then create a new
+ // cert from the DER - that is, without attributes.
+ ScopedHCERTSTORE store(
+ CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL));
+ if (!store.get())
+ return NULL;
+
+ OSCertHandle cert_handle = NULL;
+ if (!CertAddSerializedElementToStore(
+ store.get(), reinterpret_cast<const BYTE*>(data), length,
+ CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
+ NULL, reinterpret_cast<const void **>(&cert_handle))) {
+ return NULL;
+ }
+
+ std::string encoded;
+ bool ok = GetDEREncoded(cert_handle, &encoded);
+ FreeOSCertHandle(cert_handle);
+ cert_handle = NULL;
+
+ if (ok)
+ cert_handle = CreateOSCertHandleFromBytes(encoded.data(), encoded.size());
+ return cert_handle;
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ return pickle->WriteData(
+ reinterpret_cast<char*>(cert_handle->pbCertEncoded),
+ cert_handle->cbCertEncoded);
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ *type = kPublicKeyTypeUnknown;
+ *size_bits = 0;
+
+ PCCRYPT_OID_INFO oid_info = CryptFindOIDInfo(
+ CRYPT_OID_INFO_OID_KEY,
+ cert_handle->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId,
+ CRYPT_PUBKEY_ALG_OID_GROUP_ID);
+ if (!oid_info)
+ return;
+
+ CHECK_EQ(oid_info->dwGroupId,
+ static_cast<DWORD>(CRYPT_PUBKEY_ALG_OID_GROUP_ID));
+
+ *size_bits = CertGetPublicKeyLength(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert_handle->pCertInfo->SubjectPublicKeyInfo);
+
+ switch (oid_info->Algid) {
+ case CALG_RSA_SIGN:
+ case CALG_RSA_KEYX:
+ *type = kPublicKeyTypeRSA;
+ break;
+ case CALG_DSS_SIGN:
+ *type = kPublicKeyTypeDSA;
+ break;
+ case CALG_ECDSA:
+ *type = kPublicKeyTypeECDSA;
+ break;
+ case CALG_ECDH:
+ *type = kPublicKeyTypeECDH;
+ break;
+ }
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+
+ // If the certificate's issuer in the list?
+ if (IsCertNameBlobInIssuerList(&cert_handle_->pCertInfo->Issuer,
+ valid_issuers)) {
+ return true;
+ }
+ // Otherwise, is any of the intermediate CA subjects in the list?
+ for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
+ it != intermediate_ca_certs_.end(); ++it) {
+ if (IsCertNameBlobInIssuerList(&(*it)->pCertInfo->Issuer,
+ valid_issuers)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace net