Chromium Code Reviews| Index: net/cert/x509_util_win.cc |
| diff --git a/net/cert/x509_util_win.cc b/net/cert/x509_util_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..deef96dfa6d7ecc26d52a87d8c68bef3f379363b |
| --- /dev/null |
| +++ b/net/cert/x509_util_win.cc |
| @@ -0,0 +1,153 @@ |
| +// Copyright 2017 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_util_win.h" |
| + |
| +#include "crypto/scoped_capi_types.h" |
| +#include "crypto/sha2.h" |
| +#include "net/cert/x509_certificate.h" |
| +#include "net/net_features.h" |
| +#include "third_party/boringssl/src/include/openssl/pool.h" |
| + |
| +namespace net { |
| + |
| +namespace x509_util { |
| + |
| +namespace { |
| + |
| +typedef crypto::ScopedCAPIHandle< |
| + HCERTSTORE, |
| + crypto::CAPIDestroyerWithFlags<HCERTSTORE, CertCloseStore, 0>> |
|
davidben
2017/06/15 23:55:18
Nit: using
mattm
2017/06/16 21:39:31
Done.
|
| + ScopedHCERTSTORE; |
| + |
| +} // namespace |
| + |
| +scoped_refptr<X509Certificate> CreateX509CertificateFromCertContexts( |
| + PCCERT_CONTEXT os_cert, |
| + const std::vector<PCCERT_CONTEXT>& os_chain) { |
| +#if BUILDFLAG(USE_BYTE_CERTS) |
| + if (!os_cert || !os_cert->pbCertEncoded || !os_cert->cbCertEncoded) |
| + return nullptr; |
| + bssl::UniquePtr<CRYPTO_BUFFER> cert_handle( |
| + X509Certificate::CreateOSCertHandleFromBytes( |
| + reinterpret_cast<const char*>(os_cert->pbCertEncoded), |
| + os_cert->cbCertEncoded)); |
| + if (!cert_handle) |
| + return nullptr; |
| + std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates; |
| + X509Certificate::OSCertHandles intermediates_raw; |
| + for (PCCERT_CONTEXT os_intermediate : os_chain) { |
| + if (!os_intermediate || !os_intermediate->pbCertEncoded || |
| + !os_intermediate->cbCertEncoded) |
| + return nullptr; |
| + bssl::UniquePtr<CRYPTO_BUFFER> intermediate_cert_handle( |
| + X509Certificate::CreateOSCertHandleFromBytes( |
| + reinterpret_cast<const char*>(os_intermediate->pbCertEncoded), |
| + os_intermediate->cbCertEncoded)); |
| + if (!intermediate_cert_handle) |
| + return nullptr; |
| + intermediates_raw.push_back(intermediate_cert_handle.get()); |
| + intermediates.push_back(std::move(intermediate_cert_handle)); |
| + } |
| + scoped_refptr<X509Certificate> result( |
| + X509Certificate::CreateFromHandle(cert_handle.get(), intermediates_raw)); |
| + return result; |
| +#else |
| + return X509Certificate::CreateFromHandle(os_cert, os_chain); |
| +#endif |
| +} |
| + |
| +ScopedPCCERT_CONTEXT CreateCertContextWithChain(const X509Certificate* cert) { |
| + // Create an in-memory certificate store to hold the certificate and its |
| + // intermediate certificates. 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 nullptr; |
| + |
| + PCCERT_CONTEXT primary_cert = nullptr; |
| + |
| +#if BUILDFLAG(USE_BYTE_CERTS) |
| + BOOL ok = CertAddEncodedCertificateToStore( |
| + store.get(), X509_ASN_ENCODING, |
| + reinterpret_cast<const BYTE*>(CRYPTO_BUFFER_data(cert->os_cert_handle())), |
|
davidben
2017/06/15 23:55:18
Optional: It's probably safe to assume this cast i
mattm
2017/06/16 21:39:31
Done.
|
| + base::checked_cast<DWORD>(CRYPTO_BUFFER_len(cert->os_cert_handle())), |
| + CERT_STORE_ADD_ALWAYS, &primary_cert); |
| + if (!ok || !primary_cert) |
| + return nullptr; |
| + ScopedPCCERT_CONTEXT scoped_primary_cert(primary_cert); |
| + |
| + for (X509Certificate::OSCertHandle intermediate : |
| + cert->GetIntermediateCertificates()) { |
| + ok = CertAddEncodedCertificateToStore( |
| + store.get(), X509_ASN_ENCODING, |
| + reinterpret_cast<const BYTE*>(CRYPTO_BUFFER_data(intermediate)), |
| + base::checked_cast<DWORD>(CRYPTO_BUFFER_len(intermediate)), |
| + CERT_STORE_ADD_ALWAYS, NULL); |
| + if (!ok) |
| + return nullptr; |
| + } |
| +#else |
| + PCCERT_CONTEXT os_cert_handle = cert->os_cert_handle(); |
| + const std::vector<PCCERT_CONTEXT>& intermediate_ca_certs = |
| + cert->GetIntermediateCertificates(); |
| + |
| + // 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. |
| + BOOL ok = CertAddCertificateContextToStore( |
| + store.get(), os_cert_handle, CERT_STORE_ADD_ALWAYS, &primary_cert); |
| + if (!ok || !primary_cert) |
| + return nullptr; |
| + ScopedPCCERT_CONTEXT scoped_primary_cert(primary_cert); |
| + |
| + for (size_t i = 0; i < intermediate_ca_certs.size(); ++i) { |
|
davidben
2017/06/15 23:55:18
Nit:
for (PCCERT_CONTEXT intermediate : intermedia
mattm
2017/06/16 21:39:31
Done.
|
| + CertAddCertificateContextToStore(store.get(), intermediate_ca_certs[i], |
| + CERT_STORE_ADD_ALWAYS, NULL); |
| + } |
| +#endif |
| + |
| + // 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. |
|
davidben
2017/06/15 23:55:18
This comment confuses me a little. It reads like y
mattm
2017/06/16 21:39:31
Done.
|
| + return scoped_primary_cert; |
| +} |
| + |
| +SHA256HashValue CalculateFingerprint256(PCCERT_CONTEXT cert) { |
| + DCHECK(NULL != cert->pbCertEncoded); |
| + DCHECK_NE(0u, cert->cbCertEncoded); |
| + |
| + SHA256HashValue sha256; |
| + size_t sha256_size = sizeof(sha256.data); |
|
davidben
2017/06/15 23:55:18
Nit: Any reason to stash this into a variable? You
mattm
2017/06/16 21:39:31
Done.
|
| + |
| + // Use crypto::SHA256HashString for two reasons: |
| + // * < Windows Vista does not have universal SHA-256 support. |
| + // * More efficient on Windows > Vista (less overhead since non-default CSP |
| + // is not needed). |
| + base::StringPiece der_cert(reinterpret_cast<const char*>(cert->pbCertEncoded), |
| + cert->cbCertEncoded); |
| + crypto::SHA256HashString(der_cert, sha256.data, sha256_size); |
| + return sha256; |
| +} |
| + |
| +bool IsSelfSigned(PCCERT_CONTEXT cert_handle) { |
| + bool valid_signature = !!CryptVerifyCertificateSignatureEx( |
| + NULL, X509_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, |
| + reinterpret_cast<void*>(const_cast<PCERT_CONTEXT>(cert_handle)), |
| + CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, |
| + reinterpret_cast<void*>(const_cast<PCERT_CONTEXT>(cert_handle)), 0, NULL); |
| + if (!valid_signature) |
| + return false; |
| + return !!CertCompareCertificateName(X509_ASN_ENCODING, |
| + &cert_handle->pCertInfo->Subject, |
| + &cert_handle->pCertInfo->Issuer); |
| +} |
| + |
| +} // namespace x509_util |
| + |
| +} // namespace net |