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

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: rebase Created 5 years, 5 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
« no previous file with comments | « net/ssl/ssl_platform_key_nss.cc ('k') | net/ssl/ssl_private_key.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/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"
33
34 namespace net {
35
36 namespace {
37
38 using NCryptFreeObjectFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_HANDLE);
39 using NCryptSignHashFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_KEY_HANDLE, // hKey
40 VOID*, // pPaddingInfo
41 BYTE*, // pbHashValue
42 DWORD, // cbHashValue
43 BYTE*, // pbSignature
44 DWORD, // cbSignature
45 DWORD*, // pcbResult
46 DWORD); // dwFlags
47
48 class CNGFunctions {
49 public:
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"));
57 }
58 }
59
60 NCryptFreeObjectFunc ncrypt_free_object() const {
61 return ncrypt_free_object_;
62 }
63
64 NCryptSignHashFunc ncrypt_sign_hash() const { return ncrypt_sign_hash_; }
65
66 private:
67 NCryptFreeObjectFunc ncrypt_free_object_;
68 NCryptSignHashFunc ncrypt_sign_hash_;
69 };
70
71 base::LazyInstance<CNGFunctions>::Leaky g_cng_functions =
72 LAZY_INSTANCE_INITIALIZER;
73
74 class SSLPlatformKeyCAPI : public ThreadedSSLPrivateKey::Delegate {
75 public:
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) {}
79
80 ~SSLPlatformKeyCAPI() override {}
81
82 SSLPrivateKey::Type GetType() override { return SSLPrivateKey::Type::RSA; }
83
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;
89 }
90
91 size_t GetMaxSignatureLengthInBytes() override { return max_length_; }
92
93 Error SignDigest(SSLPrivateKey::Hash hash,
94 const base::StringPiece& input,
95 std::vector<uint8_t>* signature) override {
96 ALG_ID hash_alg = 0;
97 switch (hash) {
98 case SSLPrivateKey::Hash::MD5_SHA1:
99 hash_alg = CALG_SSL3_SHAMD5;
100 break;
101 case SSLPrivateKey::Hash::SHA1:
102 hash_alg = CALG_SHA1;
103 break;
104 case SSLPrivateKey::Hash::SHA256:
105 hash_alg = CALG_SHA_256;
106 break;
107 case SSLPrivateKey::Hash::SHA384:
108 hash_alg = CALG_SHA_384;
109 break;
110 case SSLPrivateKey::Hash::SHA512:
111 hash_alg = CALG_SHA_512;
112 break;
113 }
114 if (hash_alg == 0) {
115 // TODO(davidben): When Windows is built regularly with clang and the enum
116 // switches are checked statically, this can be downgraded to a DCHECK.
davidben 2015/06/29 17:37:52 Replaced with a DCHECK_NE, since it seems clang/wi
117 NOTREACHED();
118 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
119 }
120
121 crypto::ScopedHCRYPTHASH hash_handle;
122 if (!CryptCreateHash(provider_, hash_alg, 0, 0, hash_handle.receive())) {
123 PLOG(ERROR) << "CreateCreateHash failed";
124 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
125 }
126 DWORD hash_len;
127 DWORD arg_len = sizeof(hash_len);
128 if (!CryptGetHashParam(hash_handle.get(), HP_HASHSIZE,
129 reinterpret_cast<BYTE*>(&hash_len), &arg_len, 0)) {
130 PLOG(ERROR) << "CryptGetHashParam HP_HASHSIZE failed";
131 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
132 }
133 if (hash_len != input.size())
134 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
135 if (!CryptSetHashParam(
136 hash_handle.get(), HP_HASHVAL,
137 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
138 0)) {
139 PLOG(ERROR) << "CryptSetHashParam HP_HASHVAL failed";
140 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
141 }
142 DWORD signature_len = 0;
143 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0, nullptr,
144 &signature_len)) {
145 PLOG(ERROR) << "CryptSignHash failed";
146 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
147 }
148 signature->resize(signature_len);
149 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0,
150 vector_as_array(signature), &signature_len)) {
151 PLOG(ERROR) << "CryptSignHash failed";
152 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
153 }
154 signature->resize(signature_len);
155
156 // CryptoAPI signs in little-endian, so reverse it.
157 std::reverse(signature->begin(), signature->end());
158 return OK;
159 }
160
161 private:
162 crypto::ScopedHCRYPTPROV provider_;
163 DWORD key_spec_;
164 size_t max_length_;
165
166 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCAPI);
167 };
168
169 class SSLPlatformKeyCNG : public ThreadedSSLPrivateKey::Delegate {
170 public:
171 // Takes ownership of |key|.
172 SSLPlatformKeyCNG(NCRYPT_KEY_HANDLE key,
173 SSLPrivateKey::Type type,
174 size_t max_length)
175 : key_(key), type_(type), max_length_(max_length) {}
176
177 ~SSLPlatformKeyCNG() override {
178 g_cng_functions.Get().ncrypt_free_object()(key_);
179 }
180
181 SSLPrivateKey::Type GetType() override { return type_; }
182
183 bool SupportsHash(SSLPrivateKey::Hash hash) override {
184 // If the key is a 1024-bit RSA, assume conservatively that it may only be
185 // able to sign SHA-1 hashes. This is the case for older Estonian ID cards
186 // that have 1024-bit RSA keys. (For an RSA key, the maximum signature
187 // length is the size of the modulus in bytes.)
188 //
189 // CNG does provide NCryptIsAlgSupported and NCryptEnumAlgorithms functions,
190 // however they seem to both return NTE_NOT_SUPPORTED when querying the
191 // NCRYPT_PROV_HANDLE at the key's NCRYPT_PROVIDER_HANDLE_PROPERTY.
192 if (type_ == SSLPrivateKey::Type::RSA && max_length_ <= 1024 / 8) {
193 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
194 hash == SSLPrivateKey::Hash::SHA1;
195 }
196 return true;
197 }
198
199 size_t GetMaxSignatureLengthInBytes() override { return max_length_; }
200
201 Error SignDigest(SSLPrivateKey::Hash hash,
202 const base::StringPiece& input,
203 std::vector<uint8_t>* signature) override {
204 crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
205
206 BCRYPT_PKCS1_PADDING_INFO rsa_padding_info = {0};
207 void* padding_info = nullptr;
208 DWORD flags = 0;
209 if (type_ == SSLPrivateKey::Type::RSA) {
210 switch (hash) {
211 case SSLPrivateKey::Hash::MD5_SHA1:
212 rsa_padding_info.pszAlgId = nullptr;
213 break;
214 case SSLPrivateKey::Hash::SHA1:
215 rsa_padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
216 break;
217 case SSLPrivateKey::Hash::SHA256:
218 rsa_padding_info.pszAlgId = BCRYPT_SHA256_ALGORITHM;
219 break;
220 case SSLPrivateKey::Hash::SHA384:
221 rsa_padding_info.pszAlgId = BCRYPT_SHA384_ALGORITHM;
222 break;
223 case SSLPrivateKey::Hash::SHA512:
224 rsa_padding_info.pszAlgId = BCRYPT_SHA512_ALGORITHM;
225 break;
226 }
227 padding_info = &rsa_padding_info;
228 flags |= BCRYPT_PAD_PKCS1;
229 }
230
231 DWORD signature_len;
232 SECURITY_STATUS status = g_cng_functions.Get().ncrypt_sign_hash()(
233 key_, padding_info,
234 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
235 input.size(), nullptr, 0, &signature_len, flags);
236 if (FAILED(status)) {
237 LOG(ERROR) << "NCryptSignHash failed: " << status;
238 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
239 }
240 signature->resize(signature_len);
241 status = g_cng_functions.Get().ncrypt_sign_hash()(
242 key_, padding_info,
243 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
244 input.size(), vector_as_array(signature), signature_len, &signature_len,
245 flags);
246 if (FAILED(status)) {
247 LOG(ERROR) << "NCryptSignHash failed: " << status;
248 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
249 }
250 signature->resize(signature_len);
251
252 // CNG emits raw ECDSA signatures, but BoringSSL expects a DER-encoded
253 // ECDSA-Sig-Value.
254 if (type_ == SSLPrivateKey::Type::ECDSA) {
255 if (signature->size() % 2 != 0) {
256 LOG(ERROR) << "Bad signature length";
257 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
258 }
259 size_t order_len = signature->size() / 2;
260
261 // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
262 crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
263 if (!sig || !BN_bin2bn(vector_as_array(signature), order_len, sig->r) ||
264 !BN_bin2bn(vector_as_array(signature) + order_len, order_len,
265 sig->s)) {
266 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
267 }
268
269 int len = i2d_ECDSA_SIG(sig.get(), nullptr);
270 if (len < 0)
271 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
272 signature->resize(len);
273 uint8_t* ptr = vector_as_array(signature);
274 len = i2d_ECDSA_SIG(sig.get(), &ptr);
275 if (len < 0)
276 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
277 signature->resize(len);
278 }
279
280 return OK;
281 }
282
283 private:
284 NCRYPT_KEY_HANDLE key_;
285 SSLPrivateKey::Type type_;
286 size_t max_length_;
287
288 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCNG);
289 };
290
291 // Determines the key type and maximum signature length of |certificate|'s
292 // public key.
293 bool GetKeyInfo(const X509Certificate* certificate,
294 SSLPrivateKey::Type* out_type,
295 size_t* out_max_length) {
296 crypto::OpenSSLErrStackTracer tracker(FROM_HERE);
297
298 std::string der_encoded;
299 if (!X509Certificate::GetDEREncoded(certificate->os_cert_handle(),
300 &der_encoded))
301 return false;
302 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data());
303 ScopedX509 x509(d2i_X509(nullptr, &bytes, der_encoded.size()));
304 if (!x509)
305 return false;
306 crypto::ScopedEVP_PKEY key(X509_get_pubkey(x509.get()));
307 if (!key)
308 return false;
309 switch (EVP_PKEY_id(key.get())) {
310 case EVP_PKEY_RSA:
311 *out_type = SSLPrivateKey::Type::RSA;
312 break;
313 case EVP_PKEY_EC:
314 *out_type = SSLPrivateKey::Type::ECDSA;
315 break;
316 default:
317 return false;
318 }
319 *out_max_length = EVP_PKEY_size(key.get());
320 return true;
321 }
322
323 } // namespace
324
325 scoped_ptr<SSLPrivateKey> FetchClientCertPrivateKey(
326 X509Certificate* certificate,
327 scoped_refptr<base::SequencedTaskRunner> task_runner) {
328 // Rather than query the private key for metadata, extract the public key from
329 // the certificate without using Windows APIs. CAPI and CNG do not
330 // consistently work depending on the system. See https://crbug.com/468345.
331 SSLPrivateKey::Type key_type;
332 size_t max_length;
333 if (!GetKeyInfo(certificate, &key_type, &max_length))
334 return nullptr;
335
336 PCCERT_CONTEXT cert_context = certificate->os_cert_handle();
337
338 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE prov_or_key = 0;
339 DWORD key_spec = 0;
340 BOOL must_free = FALSE;
341 DWORD flags = 0;
342 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
343 flags |= CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
344
345 if (!CryptAcquireCertificatePrivateKey(cert_context, flags, nullptr,
346 &prov_or_key, &key_spec, &must_free)) {
347 PLOG(WARNING) << "Could not acquire private key";
348 return nullptr;
349 }
350
351 // Should never get a cached handle back - ownership must always be
352 // transferred.
353 CHECK_EQ(must_free, TRUE);
354
355 scoped_ptr<ThreadedSSLPrivateKey::Delegate> delegate;
356 if (key_spec == CERT_NCRYPT_KEY_SPEC) {
357 delegate.reset(new SSLPlatformKeyCNG(prov_or_key, key_type, max_length));
358 } else {
359 DCHECK(SSLPrivateKey::Type::RSA == key_type);
360 delegate.reset(new SSLPlatformKeyCAPI(prov_or_key, key_spec, max_length));
361 }
362 return make_scoped_ptr(
363 new ThreadedSSLPrivateKey(delegate.Pass(), task_runner.Pass()));
364 }
365
366 } // namespace net
OLDNEW
« no previous file with comments | « net/ssl/ssl_platform_key_nss.cc ('k') | net/ssl/ssl_private_key.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698