1 // Copyright 2015 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/ssl/ssl_platform_key.h"
14 #include <openssl/bn.h>
15 #include <openssl/ecdsa.h>
16 #include <openssl/evp.h>
17 #include <openssl/x509.h>
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/macros.h"
22 #include "base/sequenced_task_runner.h"
23 #include "base/stl_util.h"
24 #include "base/win/windows_version.h"
25 #include "crypto/openssl_util.h"
26 #include "crypto/scoped_capi_types.h"
27 #include "crypto/wincrypt_shim.h"
28 #include "net/base/net_errors.h"
29 #include "net/cert/x509_certificate.h"
30 #include "net/ssl/scoped_openssl_types.h"
31 #include "net/ssl/ssl_private_key.h"
32 #include "net/ssl/threaded_ssl_private_key.h"
38 using NCryptFreeObjectFunc
= SECURITY_STATUS(WINAPI
*)(NCRYPT_HANDLE
);
39 using NCryptSignHashFunc
= SECURITY_STATUS(WINAPI
*)(NCRYPT_KEY_HANDLE
, // hKey
40 VOID
*, // pPaddingInfo
50 CNGFunctions() : ncrypt_free_object_(nullptr), ncrypt_sign_hash_(nullptr) {
51 HMODULE ncrypt
= GetModuleHandle(L
"ncrypt.dll");
52 if (ncrypt
!= nullptr) {
53 ncrypt_free_object_
= reinterpret_cast<NCryptFreeObjectFunc
>(
54 GetProcAddress(ncrypt
, "NCryptFreeObject"));
55 ncrypt_sign_hash_
= reinterpret_cast<NCryptSignHashFunc
>(
56 GetProcAddress(ncrypt
, "NCryptSignHash"));
60 NCryptFreeObjectFunc
ncrypt_free_object() const {
61 return ncrypt_free_object_
;
64 NCryptSignHashFunc
ncrypt_sign_hash() const { return ncrypt_sign_hash_
; }
67 NCryptFreeObjectFunc ncrypt_free_object_
;
68 NCryptSignHashFunc ncrypt_sign_hash_
;
71 base::LazyInstance
<CNGFunctions
>::Leaky g_cng_functions
=
72 LAZY_INSTANCE_INITIALIZER
;
74 class SSLPlatformKeyCAPI
: public ThreadedSSLPrivateKey::Delegate
{
76 // Takes ownership of |provider|.
77 SSLPlatformKeyCAPI(HCRYPTPROV provider
, DWORD key_spec
, size_t max_length
)
78 : provider_(provider
), key_spec_(key_spec
), max_length_(max_length
) {}
80 ~SSLPlatformKeyCAPI() override
{}
82 SSLPrivateKey::Type
GetType() override
{ return SSLPrivateKey::Type::RSA
; }
84 bool SupportsHash(SSLPrivateKey::Hash hash
) override
{
85 // If the key is in CAPI, assume conservatively that the CAPI service
86 // provider may only be able to sign pre-TLS-1.2 and SHA-1 hashes.
87 return hash
== SSLPrivateKey::Hash::MD5_SHA1
||
88 hash
== SSLPrivateKey::Hash::SHA1
;
91 size_t GetMaxSignatureLengthInBytes() override
{ return max_length_
; }
93 Error
SignDigest(SSLPrivateKey::Hash hash
,
94 const base::StringPiece
& input
,
95 std::vector
<uint8_t>* signature
) override
{
98 case SSLPrivateKey::Hash::MD5_SHA1
:
99 hash_alg
= CALG_SSL3_SHAMD5
;
101 case SSLPrivateKey::Hash::SHA1
:
102 hash_alg
= CALG_SHA1
;
104 case SSLPrivateKey::Hash::SHA256
:
105 hash_alg
= CALG_SHA_256
;
107 case SSLPrivateKey::Hash::SHA384
:
108 hash_alg
= CALG_SHA_384
;
110 case SSLPrivateKey::Hash::SHA512
:
111 hash_alg
= CALG_SHA_512
;
114 DCHECK_NE(static_cast<ALG_ID
>(0), hash_alg
);
116 crypto::ScopedHCRYPTHASH hash_handle
;
117 if (!CryptCreateHash(provider_
, hash_alg
, 0, 0, hash_handle
.receive())) {
118 PLOG(ERROR
) << "CreateCreateHash failed";
119 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
122 DWORD arg_len
= sizeof(hash_len
);
123 if (!CryptGetHashParam(hash_handle
.get(), HP_HASHSIZE
,
124 reinterpret_cast<BYTE
*>(&hash_len
), &arg_len
, 0)) {
125 PLOG(ERROR
) << "CryptGetHashParam HP_HASHSIZE failed";
126 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
128 if (hash_len
!= input
.size())
129 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
130 if (!CryptSetHashParam(
131 hash_handle
.get(), HP_HASHVAL
,
132 const_cast<BYTE
*>(reinterpret_cast<const BYTE
*>(input
.data())),
134 PLOG(ERROR
) << "CryptSetHashParam HP_HASHVAL failed";
135 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
137 DWORD signature_len
= 0;
138 if (!CryptSignHash(hash_handle
.get(), key_spec_
, nullptr, 0, nullptr,
140 PLOG(ERROR
) << "CryptSignHash failed";
141 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
143 signature
->resize(signature_len
);
144 if (!CryptSignHash(hash_handle
.get(), key_spec_
, nullptr, 0,
145 vector_as_array(signature
), &signature_len
)) {
146 PLOG(ERROR
) << "CryptSignHash failed";
147 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
149 signature
->resize(signature_len
);
151 // CryptoAPI signs in little-endian, so reverse it.
152 std::reverse(signature
->begin(), signature
->end());
157 crypto::ScopedHCRYPTPROV provider_
;
161 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCAPI
);
164 class SSLPlatformKeyCNG
: public ThreadedSSLPrivateKey::Delegate
{
166 // Takes ownership of |key|.
167 SSLPlatformKeyCNG(NCRYPT_KEY_HANDLE key
,
168 SSLPrivateKey::Type type
,
170 : key_(key
), type_(type
), max_length_(max_length
) {}
172 ~SSLPlatformKeyCNG() override
{
173 g_cng_functions
.Get().ncrypt_free_object()(key_
);
176 SSLPrivateKey::Type
GetType() override
{ return type_
; }
178 bool SupportsHash(SSLPrivateKey::Hash hash
) override
{
179 // If the key is a 1024-bit RSA, assume conservatively that it may only be
180 // able to sign SHA-1 hashes. This is the case for older Estonian ID cards
181 // that have 1024-bit RSA keys. (For an RSA key, the maximum signature
182 // length is the size of the modulus in bytes.)
184 // CNG does provide NCryptIsAlgSupported and NCryptEnumAlgorithms functions,
185 // however they seem to both return NTE_NOT_SUPPORTED when querying the
186 // NCRYPT_PROV_HANDLE at the key's NCRYPT_PROVIDER_HANDLE_PROPERTY.
187 if (type_
== SSLPrivateKey::Type::RSA
&& max_length_
<= 1024 / 8) {
188 return hash
== SSLPrivateKey::Hash::MD5_SHA1
||
189 hash
== SSLPrivateKey::Hash::SHA1
;
194 size_t GetMaxSignatureLengthInBytes() override
{ return max_length_
; }
196 Error
SignDigest(SSLPrivateKey::Hash hash
,
197 const base::StringPiece
& input
,
198 std::vector
<uint8_t>* signature
) override
{
199 crypto::OpenSSLErrStackTracer
tracer(FROM_HERE
);
201 BCRYPT_PKCS1_PADDING_INFO rsa_padding_info
= {0};
202 void* padding_info
= nullptr;
204 if (type_
== SSLPrivateKey::Type::RSA
) {
206 case SSLPrivateKey::Hash::MD5_SHA1
:
207 rsa_padding_info
.pszAlgId
= nullptr;
209 case SSLPrivateKey::Hash::SHA1
:
210 rsa_padding_info
.pszAlgId
= BCRYPT_SHA1_ALGORITHM
;
212 case SSLPrivateKey::Hash::SHA256
:
213 rsa_padding_info
.pszAlgId
= BCRYPT_SHA256_ALGORITHM
;
215 case SSLPrivateKey::Hash::SHA384
:
216 rsa_padding_info
.pszAlgId
= BCRYPT_SHA384_ALGORITHM
;
218 case SSLPrivateKey::Hash::SHA512
:
219 rsa_padding_info
.pszAlgId
= BCRYPT_SHA512_ALGORITHM
;
222 padding_info
= &rsa_padding_info
;
223 flags
|= BCRYPT_PAD_PKCS1
;
227 SECURITY_STATUS status
= g_cng_functions
.Get().ncrypt_sign_hash()(
229 const_cast<BYTE
*>(reinterpret_cast<const BYTE
*>(input
.data())),
230 input
.size(), nullptr, 0, &signature_len
, flags
);
231 if (FAILED(status
)) {
232 LOG(ERROR
) << "NCryptSignHash failed: " << status
;
233 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
235 signature
->resize(signature_len
);
236 status
= g_cng_functions
.Get().ncrypt_sign_hash()(
238 const_cast<BYTE
*>(reinterpret_cast<const BYTE
*>(input
.data())),
239 input
.size(), vector_as_array(signature
), signature_len
, &signature_len
,
241 if (FAILED(status
)) {
242 LOG(ERROR
) << "NCryptSignHash failed: " << status
;
243 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
245 signature
->resize(signature_len
);
247 // CNG emits raw ECDSA signatures, but BoringSSL expects a DER-encoded
249 if (type_
== SSLPrivateKey::Type::ECDSA
) {
250 if (signature
->size() % 2 != 0) {
251 LOG(ERROR
) << "Bad signature length";
252 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
254 size_t order_len
= signature
->size() / 2;
256 // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
257 crypto::ScopedECDSA_SIG
sig(ECDSA_SIG_new());
258 if (!sig
|| !BN_bin2bn(vector_as_array(signature
), order_len
, sig
->r
) ||
259 !BN_bin2bn(vector_as_array(signature
) + order_len
, order_len
,
261 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
264 int len
= i2d_ECDSA_SIG(sig
.get(), nullptr);
266 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
267 signature
->resize(len
);
268 uint8_t* ptr
= vector_as_array(signature
);
269 len
= i2d_ECDSA_SIG(sig
.get(), &ptr
);
271 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
;
272 signature
->resize(len
);
279 NCRYPT_KEY_HANDLE key_
;
280 SSLPrivateKey::Type type_
;
283 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCNG
);
286 // Determines the key type and maximum signature length of |certificate|'s
288 bool GetKeyInfo(const X509Certificate
* certificate
,
289 SSLPrivateKey::Type
* out_type
,
290 size_t* out_max_length
) {
291 crypto::OpenSSLErrStackTracer
tracker(FROM_HERE
);
293 std::string der_encoded
;
294 if (!X509Certificate::GetDEREncoded(certificate
->os_cert_handle(),
297 const uint8_t* bytes
= reinterpret_cast<const uint8_t*>(der_encoded
.data());
298 ScopedX509
x509(d2i_X509(nullptr, &bytes
, der_encoded
.size()));
301 crypto::ScopedEVP_PKEY
key(X509_get_pubkey(x509
.get()));
304 switch (EVP_PKEY_id(key
.get())) {
306 *out_type
= SSLPrivateKey::Type::RSA
;
309 *out_type
= SSLPrivateKey::Type::ECDSA
;
314 *out_max_length
= EVP_PKEY_size(key
.get());
320 scoped_ptr
<SSLPrivateKey
> FetchClientCertPrivateKey(
321 X509Certificate
* certificate
,
322 scoped_refptr
<base::SequencedTaskRunner
> task_runner
) {
323 // Rather than query the private key for metadata, extract the public key from
324 // the certificate without using Windows APIs. CAPI and CNG do not
325 // consistently work depending on the system. See https://crbug.com/468345.
326 SSLPrivateKey::Type key_type
;
328 if (!GetKeyInfo(certificate
, &key_type
, &max_length
))
331 PCCERT_CONTEXT cert_context
= certificate
->os_cert_handle();
333 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE prov_or_key
= 0;
335 BOOL must_free
= FALSE
;
337 if (base::win::GetVersion() >= base::win::VERSION_VISTA
)
338 flags
|= CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG
;
340 if (!CryptAcquireCertificatePrivateKey(cert_context
, flags
, nullptr,
341 &prov_or_key
, &key_spec
, &must_free
)) {
342 PLOG(WARNING
) << "Could not acquire private key";
346 // Should never get a cached handle back - ownership must always be
348 CHECK_EQ(must_free
, TRUE
);
350 scoped_ptr
<ThreadedSSLPrivateKey::Delegate
> delegate
;
351 if (key_spec
== CERT_NCRYPT_KEY_SPEC
) {
352 delegate
.reset(new SSLPlatformKeyCNG(prov_or_key
, key_type
, max_length
));
354 DCHECK(SSLPrivateKey::Type::RSA
== key_type
);
355 delegate
.reset(new SSLPlatformKeyCAPI(prov_or_key
, key_spec
, max_length
));
357 return make_scoped_ptr(
358 new ThreadedSSLPrivateKey(delegate
.Pass(), task_runner
.Pass()));