Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(257)

Side by Side Diff: net/ssl/ssl_platform_key_win.cc

Issue 1178193002: Sign CertificateVerify messages on a background thread. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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.
4
5 #include "net/ssl/ssl_platform_key.h"
6
7 #include <windows.h>
8 #include <NCrypt.h>
9
10 #include <algorithm>
11 #include <string>
12 #include <vector>
13
14 #include <openssl/bn.h>
15 #include <openssl/ecdsa.h>
16 #include <openssl/evp.h>
17 #include <openssl/x509.h>
18
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/macros.h"
22 #include "base/stl_util.h"
23 #include "base/win/windows_version.h"
24 #include "crypto/openssl_util.h"
25 #include "crypto/scoped_capi_types.h"
26 #include "crypto/wincrypt_shim.h"
27 #include "net/base/net_errors.h"
28 #include "net/cert/x509_certificate.h"
29 #include "net/ssl/scoped_openssl_types.h"
30 #include "net/ssl/ssl_private_key.h"
31 #include "net/ssl/threaded_ssl_private_key.h"
32
33 namespace net {
34
35 namespace {
36
37 using NCryptFreeObjectFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_HANDLE);
38 using NCryptSignHashFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_KEY_HANDLE, // hKey
39 VOID*, // pPaddingInfo
40 BYTE*, // pbHashValue
41 DWORD, // cbHashValue
42 BYTE*, // pbSignature
43 DWORD, // cbSignature
44 DWORD*, // pcbResult
45 DWORD); // dwFlags
46
47 class CNGFunctions {
48 public:
49 CNGFunctions() : ncrypt_free_object_(nullptr), ncrypt_sign_hash_(nullptr) {
50 HMODULE ncrypt = GetModuleHandle(L"ncrypt.dll");
51 if (ncrypt != nullptr) {
52 ncrypt_free_object_ = reinterpret_cast<NCryptFreeObjectFunc>(
53 GetProcAddress(ncrypt, "NCryptFreeObject"));
54 ncrypt_sign_hash_ = reinterpret_cast<NCryptSignHashFunc>(
55 GetProcAddress(ncrypt, "NCryptSignHash"));
56 }
57 }
58
59 NCryptFreeObjectFunc ncrypt_free_object() const {
60 return ncrypt_free_object_;
61 }
62
63 NCryptSignHashFunc ncrypt_sign_hash() const { return ncrypt_sign_hash_; }
64
65 private:
66 NCryptFreeObjectFunc ncrypt_free_object_;
67 NCryptSignHashFunc ncrypt_sign_hash_;
68 };
69
70 base::LazyInstance<CNGFunctions>::Leaky g_cng_functions =
71 LAZY_INSTANCE_INITIALIZER;
72
73 class SSLPrivateKeyCAPI : public ThreadedSSLPrivateKey::Core {
74 public:
75 // Takes ownership of |provider|.
76 SSLPrivateKeyCAPI(HCRYPTPROV provider, DWORD key_spec, size_t max_length)
77 : provider_(provider), key_spec_(key_spec), max_length_(max_length) {}
78
79 SSLPrivateKey::Type GetType() override { return SSLPrivateKey::Type::RSA; }
80
81 bool SupportsHash(SSLPrivateKey::Hash hash) override {
82 // If the key is in CAPI, assume conservatively that the CAPI service
83 // provider may only be able to sign pre-TLS-1.2 and SHA-1 hashes.
84 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
85 hash == SSLPrivateKey::Hash::SHA1;
86 }
87
88 size_t GetMaxSignatureLength() override { return max_length_; }
89
90 Error SignDigest(SSLPrivateKey::Hash hash,
91 const base::StringPiece& input,
92 std::vector<uint8_t>* signature) override {
93 ALG_ID hash_alg;
94 switch (hash) {
95 case SSLPrivateKey::Hash::MD5_SHA1:
96 hash_alg = CALG_SSL3_SHAMD5;
97 break;
98 case SSLPrivateKey::Hash::SHA1:
99 hash_alg = CALG_SHA1;
100 break;
101 case SSLPrivateKey::Hash::SHA256:
102 hash_alg = CALG_SHA_256;
103 break;
104 case SSLPrivateKey::Hash::SHA384:
105 hash_alg = CALG_SHA_384;
106 break;
107 case SSLPrivateKey::Hash::SHA512:
108 hash_alg = CALG_SHA_512;
109 break;
110 default:
111 NOTREACHED();
112 return ERR_FAILED;
113 }
114
115 crypto::ScopedHCRYPTHASH hash_handle;
116 if (!CryptCreateHash(provider_, hash_alg, 0, 0, hash_handle.receive())) {
117 PLOG(ERROR) << "CreateCreateHash failed";
118 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
119 }
120 DWORD hash_len;
121 DWORD arg_len = sizeof(hash_len);
122 if (!CryptGetHashParam(hash_handle.get(), HP_HASHSIZE,
123 reinterpret_cast<BYTE*>(&hash_len), &arg_len, 0)) {
124 PLOG(ERROR) << "CryptGetHashParam HP_HASHSIZE failed";
125 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
126 }
127 if (hash_len != input.size())
128 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
129 if (!CryptSetHashParam(
130 hash_handle.get(), HP_HASHVAL,
131 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
132 0)) {
133 PLOG(ERROR) << "CryptSetHashParam HP_HASHVAL failed";
134 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
135 }
136 DWORD signature_len = 0;
137 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0, nullptr,
138 &signature_len)) {
139 PLOG(ERROR) << "CryptSignHash failed";
140 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
141 }
142 signature->resize(signature_len);
143 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0,
144 vector_as_array(signature), &signature_len)) {
145 PLOG(ERROR) << "CryptSignHash failed";
146 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
147 }
148 signature->resize(signature_len);
149
150 // CryptoAPI signs in little-endian, so reverse it.
151 std::reverse(signature->begin(), signature->end());
152 return OK;
153 }
154
155 private:
156 ~SSLPrivateKeyCAPI() override {}
157
158 crypto::ScopedHCRYPTPROV provider_;
159 DWORD key_spec_;
160 size_t max_length_;
161 };
162
163 class SSLPrivateKeyCNG : public ThreadedSSLPrivateKey::Core {
164 public:
165 // Takes ownership of |key|.
166 SSLPrivateKeyCNG(NCRYPT_KEY_HANDLE key,
167 SSLPrivateKey::Type type,
168 size_t max_length)
169 : key_(key), type_(type), max_length_(max_length) {}
170
171 SSLPrivateKey::Type GetType() override { return type_; }
172
173 bool SupportsHash(SSLPrivateKey::Hash hash) override {
174 // If the key is a 1024-bit RSA, assume conservatively that it may only be
175 // able to sign SHA-1 hashes. This is the case for older Estonian ID cards
176 // that have 1024-bit RSA keys. (For an RSA key, the maximum signature
177 // length is the size of the modulus in bytes.)
178 //
179 // CNG does provide NCryptIsAlgSupported and NCryptEnumAlgorithms functions,
180 // however they seem to both return NTE_NOT_SUPPORTED when querying the
181 // NCRYPT_PROV_HANDLE at the key's NCRYPT_PROVIDER_HANDLE_PROPERTY.
182 if (type_ == SSLPrivateKey::Type::RSA && max_length_ <= 1024 / 8) {
183 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
184 hash == SSLPrivateKey::Hash::SHA1;
185 }
186 return true;
187 }
188
189 size_t GetMaxSignatureLength() override { return max_length_; }
190
191 Error SignDigest(SSLPrivateKey::Hash hash,
192 const base::StringPiece& input,
193 std::vector<uint8_t>* signature) override {
194 crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
195
196 BCRYPT_PKCS1_PADDING_INFO rsa_padding_info;
197 void* padding_info = nullptr;
198 DWORD flags = 0;
199 if (type_ == SSLPrivateKey::Type::RSA) {
200 switch (hash) {
201 case SSLPrivateKey::Hash::MD5_SHA1:
202 rsa_padding_info.pszAlgId = nullptr;
203 break;
204 case SSLPrivateKey::Hash::SHA1:
205 rsa_padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
206 break;
207 case SSLPrivateKey::Hash::SHA256:
208 rsa_padding_info.pszAlgId = BCRYPT_SHA256_ALGORITHM;
209 break;
210 case SSLPrivateKey::Hash::SHA384:
211 rsa_padding_info.pszAlgId = BCRYPT_SHA384_ALGORITHM;
212 break;
213 case SSLPrivateKey::Hash::SHA512:
214 rsa_padding_info.pszAlgId = BCRYPT_SHA512_ALGORITHM;
215 break;
216 default:
217 NOTREACHED();
218 return ERR_FAILED;
219 }
220 padding_info = &rsa_padding_info;
221 flags |= BCRYPT_PAD_PKCS1;
222 }
223
224 DWORD signature_len;
225 SECURITY_STATUS status = g_cng_functions.Get().ncrypt_sign_hash()(
226 key_, padding_info,
227 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
228 input.size(), nullptr, 0, &signature_len, flags);
229 if (FAILED(status)) {
230 LOG(ERROR) << "NCryptSignHash failed: " << status;
231 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
232 }
233 signature->resize(signature_len);
234 status = g_cng_functions.Get().ncrypt_sign_hash()(
235 key_, padding_info,
236 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
237 input.size(), vector_as_array(signature), signature_len, &signature_len,
238 flags);
239 if (FAILED(status)) {
240 LOG(ERROR) << "NCryptSignHash failed: " << status;
241 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
242 }
243 signature->resize(signature_len);
244
245 // CNG emits raw ECDSA signatures, but BoringSSL expects a DER-encoded
246 // ECDSA-Sig-Value.
247 if (type_ == SSLPrivateKey::Type::ECDSA) {
248 if (signature->size() % 2 != 0) {
249 LOG(ERROR) << "Bad signature length";
250 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
251 }
252 size_t order_len = signature->size() / 2;
253
254 // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
255 crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
256 if (!sig)
257 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
258 sig->r = BN_bin2bn(vector_as_array(signature), order_len, nullptr);
259 sig->s =
260 BN_bin2bn(vector_as_array(signature) + order_len, order_len, nullptr);
261 if (!sig->r || !sig->s)
262 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
263
264 int len = i2d_ECDSA_SIG(sig.get(), nullptr);
265 if (len < 0)
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);
270 if (len < 0)
271 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
272 signature->resize(len);
273 }
274
275 return OK;
276 }
277
278 private:
279 ~SSLPrivateKeyCNG() override {
280 g_cng_functions.Get().ncrypt_free_object()(key_);
281 }
282
283 NCRYPT_KEY_HANDLE key_;
284 SSLPrivateKey::Type type_;
285 size_t max_length_;
286 };
287
288 // Determines the key type and maximum signature length of |certificate|'s
289 // public key.
290 bool GetKeyInfo(const X509Certificate* certificate,
291 SSLPrivateKey::Type* out_type,
292 size_t* out_max_length) {
293 crypto::OpenSSLErrStackTracer tracker(FROM_HERE);
294
295 std::string der_encoded;
296 if (!X509Certificate::GetDEREncoded(certificate->os_cert_handle(),
297 &der_encoded))
298 return false;
299 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data());
300 ScopedX509 x509(d2i_X509(NULL, &bytes, der_encoded.size()));
301 if (!x509)
302 return false;
303 crypto::ScopedEVP_PKEY key(X509_get_pubkey(x509.get()));
304 if (!key)
305 return false;
306 switch (EVP_PKEY_id(key.get())) {
307 case EVP_PKEY_RSA:
308 *out_type = SSLPrivateKey::Type::RSA;
309 break;
310 case EVP_PKEY_EC:
311 *out_type = SSLPrivateKey::Type::ECDSA;
312 break;
313 default:
314 return false;
315 }
316 *out_max_length = EVP_PKEY_size(key.get());
317 return true;
318 }
319
320 } // namespace
321
322 scoped_ptr<SSLPrivateKey> FetchClientCertPrivateKey(
323 const X509Certificate* certificate,
324 const scoped_refptr<base::TaskRunner>& task_runner) {
325 // Rather than query the private key for metadata, extract the public key from
326 // the certificate without using Windows APIs. CAPI and CNG do not
327 // consistently work depending on the system. See https://crbug.com/468345.
328 SSLPrivateKey::Type key_type;
329 size_t max_length;
330 if (!GetKeyInfo(certificate, &key_type, &max_length))
331 return nullptr;
332
333 PCCERT_CONTEXT cert_context = certificate->os_cert_handle();
334
335 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE prov_or_key = 0;
336 DWORD key_spec = 0;
337 BOOL must_free = FALSE;
338 DWORD flags = 0;
339 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
340 flags |= CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
341
342 if (!CryptAcquireCertificatePrivateKey(cert_context, flags, nullptr,
343 &prov_or_key, &key_spec, &must_free)) {
344 PLOG(WARNING) << "Could not acquire private key";
345 return nullptr;
346 }
347
348 // Should never get a cached handle back - ownership must always be
349 // transferred.
350 CHECK_EQ(must_free, TRUE);
351
352 scoped_refptr<ThreadedSSLPrivateKey::Core> key;
353 if (key_spec == CERT_NCRYPT_KEY_SPEC) {
354 key = new SSLPrivateKeyCNG(prov_or_key, key_type, max_length);
355 } else {
356 DCHECK(SSLPrivateKey::Type::RSA == key_type);
357 key = new SSLPrivateKeyCAPI(prov_or_key, key_spec, max_length);
358 }
359 return make_scoped_ptr(new ThreadedSSLPrivateKey(key, task_runner));
360 }
361
362 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698