1 // Copyright 2017 The Chromium Authors
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/cert/x509_util_win.h"
6
7 #include <string_view>
8
9 #include "base/logging.h"
10 #include "crypto/scoped_capi_types.h"
11 #include "crypto/sha2.h"
12 #include "net/cert/x509_certificate.h"
13 #include "net/cert/x509_util.h"
14 #include "net/net_buildflags.h"
15 #include "third_party/boringssl/src/include/openssl/pool.h"
16
17 namespace net {
18
19 namespace x509_util {
20
CertContextAsSpan(PCCERT_CONTEXT os_cert)21 base::span<const uint8_t> CertContextAsSpan(PCCERT_CONTEXT os_cert) {
22 // SAFETY: `os_cert` is a pointer to a CERT_CONTEXT which contains a pointer
23 // to the certificate DER encoded data in `pbCertEncoded` of length
24 // `cbCertEncoded`.
25 return UNSAFE_BUFFERS(
26 base::span(os_cert->pbCertEncoded, os_cert->cbCertEncoded));
27 }
28
CreateX509CertificateFromCertContexts(PCCERT_CONTEXT os_cert,const std::vector<PCCERT_CONTEXT> & os_chain)29 scoped_refptr<X509Certificate> CreateX509CertificateFromCertContexts(
30 PCCERT_CONTEXT os_cert,
31 const std::vector<PCCERT_CONTEXT>& os_chain) {
32 return CreateX509CertificateFromCertContexts(os_cert, os_chain, {});
33 }
34
CreateX509CertificateFromCertContexts(PCCERT_CONTEXT os_cert,const std::vector<PCCERT_CONTEXT> & os_chain,X509Certificate::UnsafeCreateOptions options)35 scoped_refptr<X509Certificate> CreateX509CertificateFromCertContexts(
36 PCCERT_CONTEXT os_cert,
37 const std::vector<PCCERT_CONTEXT>& os_chain,
38 X509Certificate::UnsafeCreateOptions options) {
39 if (!os_cert || !os_cert->pbCertEncoded || !os_cert->cbCertEncoded)
40 return nullptr;
41 bssl::UniquePtr<CRYPTO_BUFFER> cert_handle(
42 x509_util::CreateCryptoBuffer(CertContextAsSpan(os_cert)));
43
44 std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
45 for (PCCERT_CONTEXT os_intermediate : os_chain) {
46 if (!os_intermediate || !os_intermediate->pbCertEncoded ||
47 !os_intermediate->cbCertEncoded)
48 return nullptr;
49 intermediates.push_back(
50 x509_util::CreateCryptoBuffer(CertContextAsSpan(os_intermediate)));
51 }
52
53 return X509Certificate::CreateFromBufferUnsafeOptions(
54 std::move(cert_handle), std::move(intermediates), options);
55 }
56
CreateCertContextWithChain(const X509Certificate * cert)57 crypto::ScopedPCCERT_CONTEXT CreateCertContextWithChain(
58 const X509Certificate* cert) {
59 return CreateCertContextWithChain(cert, InvalidIntermediateBehavior::kFail);
60 }
61
CreateCertContextWithChain(const X509Certificate * cert,InvalidIntermediateBehavior invalid_intermediate_behavior)62 crypto::ScopedPCCERT_CONTEXT CreateCertContextWithChain(
63 const X509Certificate* cert,
64 InvalidIntermediateBehavior invalid_intermediate_behavior) {
65 // Create an in-memory certificate store to hold the certificate and its
66 // intermediate certificates. The store will be referenced in the returned
67 // PCCERT_CONTEXT, and will not be freed until the PCCERT_CONTEXT is freed.
68 crypto::ScopedHCERTSTORE store(
69 CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL,
70 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr));
71 if (!store.is_valid())
72 return nullptr;
73
74 PCCERT_CONTEXT primary_cert = nullptr;
75
76 BOOL ok = CertAddEncodedCertificateToStore(
77 store.get(), X509_ASN_ENCODING, CRYPTO_BUFFER_data(cert->cert_buffer()),
78 base::checked_cast<DWORD>(CRYPTO_BUFFER_len(cert->cert_buffer())),
79 CERT_STORE_ADD_ALWAYS, &primary_cert);
80 if (!ok || !primary_cert)
81 return nullptr;
82 crypto::ScopedPCCERT_CONTEXT scoped_primary_cert(primary_cert);
83
84 for (const auto& intermediate : cert->intermediate_buffers()) {
85 ok = CertAddEncodedCertificateToStore(
86 store.get(), X509_ASN_ENCODING, CRYPTO_BUFFER_data(intermediate.get()),
87 base::checked_cast<DWORD>(CRYPTO_BUFFER_len(intermediate.get())),
88 CERT_STORE_ADD_ALWAYS, nullptr);
89 if (!ok) {
90 if (invalid_intermediate_behavior == InvalidIntermediateBehavior::kFail)
91 return nullptr;
92 LOG(WARNING) << "error parsing intermediate";
93 }
94 }
95
96 // Note: |primary_cert| retains a reference to |store|, so the store will
97 // actually be freed when |primary_cert| is freed.
98 return scoped_primary_cert;
99 }
100
CalculateFingerprint256(PCCERT_CONTEXT cert)101 SHA256HashValue CalculateFingerprint256(PCCERT_CONTEXT cert) {
102 DCHECK(nullptr != cert->pbCertEncoded);
103 DCHECK_NE(0u, cert->cbCertEncoded);
104
105 SHA256HashValue sha256;
106
107 // Use crypto::SHA256HashString for two reasons:
108 // * < Windows Vista does not have universal SHA-256 support.
109 // * More efficient on Windows > Vista (less overhead since non-default CSP
110 // is not needed).
111 crypto::SHA256HashString(base::as_string_view(CertContextAsSpan(cert)),
112 sha256.data, sizeof(sha256.data));
113 return sha256;
114 }
115
IsSelfSigned(PCCERT_CONTEXT cert_handle)116 bool IsSelfSigned(PCCERT_CONTEXT cert_handle) {
117 bool valid_signature = !!CryptVerifyCertificateSignatureEx(
118 NULL, X509_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
119 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT>(cert_handle)),
120 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
121 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT>(cert_handle)), 0,
122 nullptr);
123 if (!valid_signature)
124 return false;
125 return !!CertCompareCertificateName(X509_ASN_ENCODING,
126 &cert_handle->pCertInfo->Subject,
127 &cert_handle->pCertInfo->Issuer);
128 }
129
130 } // namespace x509_util
131
132 } // namespace net
133