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

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

Issue 761903003: Update from https://crrev.com/306655 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years 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/openssl_platform_key_mac.cc ('k') | net/ssl/ssl_connection_status_flags.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/ssl/openssl_platform_key.h" 5 #include "net/ssl/openssl_platform_key.h"
6 6
7 #include <windows.h>
8 #include <NCrypt.h>
9
10 #include <string.h>
11
12 #include <algorithm>
13 #include <vector>
14
15 #include <openssl/bn.h>
16 #include <openssl/digest.h>
17 #include <openssl/ec_key.h>
18 #include <openssl/err.h>
19 #include <openssl/engine.h>
20 #include <openssl/evp.h>
21 #include <openssl/md5.h>
22 #include <openssl/obj_mac.h>
23 #include <openssl/rsa.h>
24 #include <openssl/sha.h>
25
26 #include "base/debug/debugger.h"
27 #include "base/debug/stack_trace.h"
28 #include "base/lazy_instance.h"
7 #include "base/logging.h" 29 #include "base/logging.h"
30 #include "base/memory/scoped_ptr.h"
31 #include "base/win/windows_version.h"
32 #include "crypto/scoped_capi_types.h"
33 #include "crypto/wincrypt_shim.h"
34 #include "net/base/net_errors.h"
35 #include "net/cert/x509_certificate.h"
36 #include "net/ssl/openssl_ssl_util.h"
8 37
9 namespace net { 38 namespace net {
10 39
40 namespace {
41
42 using NCryptFreeObjectFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_HANDLE);
43 using NCryptGetPropertyFunc =
44 SECURITY_STATUS(WINAPI*)(NCRYPT_HANDLE, // hObject
45 LPCWSTR, // pszProperty
46 PBYTE, // pbOutput
47 DWORD, // cbOutput
48 DWORD*, // pcbResult
49 DWORD); // dwFlags
50 using NCryptSignHashFunc =
51 SECURITY_STATUS(WINAPI*)(NCRYPT_KEY_HANDLE, // hKey
52 VOID*, // pPaddingInfo
53 PBYTE, // pbHashValue
54 DWORD, // cbHashValue
55 PBYTE, // pbSignature
56 DWORD, // cbSignature
57 DWORD*, // pcbResult
58 DWORD); // dwFlags
59
60 class CNGFunctions {
61 public:
62 CNGFunctions()
63 : ncrypt_free_object_(nullptr),
64 ncrypt_get_property_(nullptr),
65 ncrypt_sign_hash_(nullptr) {
66 HMODULE ncrypt = GetModuleHandle(L"ncrypt.dll");
67 if (ncrypt != nullptr) {
68 ncrypt_free_object_ = reinterpret_cast<NCryptFreeObjectFunc>(
69 GetProcAddress(ncrypt, "NCryptFreeObject"));
70 ncrypt_get_property_ = reinterpret_cast<NCryptGetPropertyFunc>(
71 GetProcAddress(ncrypt, "NCryptGetProperty"));
72 ncrypt_sign_hash_ = reinterpret_cast<NCryptSignHashFunc>(
73 GetProcAddress(ncrypt, "NCryptSignHash"));
74 }
75 }
76
77 NCryptFreeObjectFunc ncrypt_free_object() const {
78 return ncrypt_free_object_;
79 }
80
81 NCryptGetPropertyFunc ncrypt_get_property() const {
82 return ncrypt_get_property_;
83 }
84
85 NCryptSignHashFunc ncrypt_sign_hash() const { return ncrypt_sign_hash_; }
86
87 private:
88 NCryptFreeObjectFunc ncrypt_free_object_;
89 NCryptGetPropertyFunc ncrypt_get_property_;
90 NCryptSignHashFunc ncrypt_sign_hash_;
91 };
92
93 base::LazyInstance<CNGFunctions>::Leaky g_cng_functions =
94 LAZY_INSTANCE_INITIALIZER;
95
96 struct CERT_KEY_CONTEXTDeleter {
97 void operator()(PCERT_KEY_CONTEXT key) {
98 if (key->dwKeySpec == CERT_NCRYPT_KEY_SPEC) {
99 g_cng_functions.Get().ncrypt_free_object()(key->hNCryptKey);
100 } else {
101 CryptReleaseContext(key->hCryptProv, 0);
102 }
103 delete key;
104 }
105 };
106
107 using ScopedCERT_KEY_CONTEXT =
108 scoped_ptr<CERT_KEY_CONTEXT, CERT_KEY_CONTEXTDeleter>;
109
110 // KeyExData contains the data that is contained in the EX_DATA of the
111 // RSA and ECDSA objects that are created to wrap Windows system keys.
112 struct KeyExData {
113 KeyExData(ScopedCERT_KEY_CONTEXT key, DWORD key_length)
114 : key(key.Pass()), key_length(key_length) {}
115
116 ScopedCERT_KEY_CONTEXT key;
117 DWORD key_length;
118 };
119
120 // ExDataDup is called when one of the RSA or EC_KEY objects is
121 // duplicated. This is not supported and should never happen.
122 int ExDataDup(CRYPTO_EX_DATA* to,
123 const CRYPTO_EX_DATA* from,
124 void** from_d,
125 int idx,
126 long argl,
127 void* argp) {
128 CHECK_EQ((void*)nullptr, *from_d);
129 return 0;
130 }
131
132 // ExDataFree is called when one of the RSA or EC_KEY objects is freed.
133 void ExDataFree(void* parent,
134 void* ptr,
135 CRYPTO_EX_DATA* ex_data,
136 int idx,
137 long argl,
138 void* argp) {
139 KeyExData* data = reinterpret_cast<KeyExData*>(ptr);
140 delete data;
141 }
142
143 extern const RSA_METHOD win_rsa_method;
144 extern const ECDSA_METHOD win_ecdsa_method;
145
146 // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA
147 // by forwarding the requested operations to CAPI or CNG.
148 class BoringSSLEngine {
149 public:
150 BoringSSLEngine()
151 : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
152 nullptr /* argp */,
153 nullptr /* new_func */,
154 ExDataDup,
155 ExDataFree)),
156 ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
157 nullptr /* argp */,
158 nullptr /* new_func */,
159 ExDataDup,
160 ExDataFree)),
161 engine_(ENGINE_new()) {
162 ENGINE_set_RSA_method(engine_, &win_rsa_method, sizeof(win_rsa_method));
163 ENGINE_set_ECDSA_method(engine_, &win_ecdsa_method,
164 sizeof(win_ecdsa_method));
165 }
166
167 int rsa_ex_index() const { return rsa_index_; }
168 int ec_key_ex_index() const { return ec_key_index_; }
169
170 const ENGINE* engine() const { return engine_; }
171
172 private:
173 const int rsa_index_;
174 const int ec_key_index_;
175 ENGINE* const engine_;
176 };
177
178 base::LazyInstance<BoringSSLEngine>::Leaky global_boringssl_engine =
179 LAZY_INSTANCE_INITIALIZER;
180
181 // Custom RSA_METHOD that uses the platform APIs for signing.
182
183 const KeyExData* RsaGetExData(const RSA* rsa) {
184 return reinterpret_cast<const KeyExData*>(
185 RSA_get_ex_data(rsa, global_boringssl_engine.Get().rsa_ex_index()));
186 }
187
188 size_t RsaMethodSize(const RSA* rsa) {
189 const KeyExData* ex_data = RsaGetExData(rsa);
190 return (ex_data->key_length + 7) / 8;
191 }
192
193 // Signs |in| using |rsa| with PKCS #1 padding. If |hash_nid| is NID_md5_sha1,
194 // |in| is a TLS MD5/SHA-1 concatenation and should be signed as-is. Otherwise
195 // |in| is a standard hash function and should be prefixed with the
196 // corresponding DigestInfo before signing. The signature is written to |out|
197 // and its length written to |*out_len|. This function returns true on success
198 // and false on failure.
199 bool RsaSignPKCS1(const RSA* rsa,
200 int hash_nid,
201 const uint8_t* in,
202 size_t in_len,
203 uint8_t* out,
204 size_t max_out,
205 size_t* out_len) {
206 const KeyExData* ex_data = RsaGetExData(rsa);
207 if (!ex_data) {
208 NOTREACHED();
209 OPENSSL_PUT_ERROR(RSA, RSA_sign, ERR_R_INTERNAL_ERROR);
210 return false;
211 }
212
213 if (ex_data->key->dwKeySpec == CERT_NCRYPT_KEY_SPEC) {
214 BCRYPT_PKCS1_PADDING_INFO rsa_padding_info;
215 switch (hash_nid) {
216 case NID_md5_sha1:
217 rsa_padding_info.pszAlgId = nullptr;
218 break;
219 case NID_sha1:
220 rsa_padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
221 break;
222 case NID_sha256:
223 rsa_padding_info.pszAlgId = BCRYPT_SHA256_ALGORITHM;
224 break;
225 case NID_sha384:
226 rsa_padding_info.pszAlgId = BCRYPT_SHA384_ALGORITHM;
227 break;
228 case NID_sha512:
229 rsa_padding_info.pszAlgId = BCRYPT_SHA512_ALGORITHM;
230 break;
231 default:
232 OPENSSL_PUT_ERROR(RSA, RSA_sign, RSA_R_UNKNOWN_ALGORITHM_TYPE);
233 return false;
234 }
235
236 DWORD signature_len;
237 SECURITY_STATUS ncrypt_status = g_cng_functions.Get().ncrypt_sign_hash()(
238 ex_data->key->hNCryptKey, &rsa_padding_info, const_cast<PBYTE>(in),
239 in_len, out, max_out, &signature_len, BCRYPT_PAD_PKCS1);
240 if (FAILED(ncrypt_status) || signature_len == 0) {
241 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
242 return false;
243 }
244 *out_len = signature_len;
245 return true;
246 }
247
248 ALG_ID hash_alg;
249 switch (hash_nid) {
250 case NID_md5_sha1:
251 hash_alg = CALG_SSL3_SHAMD5;
252 break;
253 case NID_sha1:
254 hash_alg = CALG_SHA1;
255 break;
256 case NID_sha256:
257 hash_alg = CALG_SHA_256;
258 break;
259 case NID_sha384:
260 hash_alg = CALG_SHA_384;
261 break;
262 case NID_sha512:
263 hash_alg = CALG_SHA_512;
264 break;
265 default:
266 OPENSSL_PUT_ERROR(RSA, RSA_sign, RSA_R_UNKNOWN_ALGORITHM_TYPE);
267 return false;
268 }
269
270 HCRYPTHASH hash;
271 if (!CryptCreateHash(ex_data->key->hCryptProv, hash_alg, 0, 0, &hash)) {
272 PLOG(ERROR) << "CreateCreateHash failed";
273 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
274 return false;
275 }
276 DWORD hash_len;
277 DWORD arg_len = sizeof(hash_len);
278 if (!CryptGetHashParam(hash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&hash_len),
279 &arg_len, 0)) {
280 PLOG(ERROR) << "CryptGetHashParam HP_HASHSIZE failed";
281 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
282 return false;
283 }
284 if (hash_len != in_len) {
285 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
286 return false;
287 }
288 if (!CryptSetHashParam(hash, HP_HASHVAL, const_cast<BYTE*>(in), 0)) {
289 PLOG(ERROR) << "CryptSetHashParam HP_HASHVAL failed";
290 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
291 return false;
292 }
293 DWORD signature_len = max_out;
294 if (!CryptSignHash(hash, ex_data->key->dwKeySpec, nullptr, 0, out,
295 &signature_len)) {
296 PLOG(ERROR) << "CryptSignHash failed";
297 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
298 return false;
299 }
300
301 /* CryptoAPI signs in little-endian, so reverse it. */
302 std::reverse(out, out + signature_len);
303 *out_len = signature_len;
304 return true;
305 }
306
307 int RsaMethodSign(int hash_nid,
308 const uint8_t* in,
309 unsigned in_len,
310 uint8_t* out,
311 unsigned* out_len,
312 const RSA* rsa) {
313 // TOD(davidben): Switch BoringSSL's sign hook to using size_t rather than
314 // unsigned.
315 size_t len;
316 if (!RsaSignPKCS1(rsa, hash_nid, in, in_len, out, RSA_size(rsa), &len))
317 return 0;
318 *out_len = len;
319 return 1;
320 }
321
322 int RsaMethodEncrypt(RSA* rsa,
323 size_t* out_len,
324 uint8_t* out,
325 size_t max_out,
326 const uint8_t* in,
327 size_t in_len,
328 int padding) {
329 NOTIMPLEMENTED();
330 OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE);
331 return 0;
332 }
333
334 int RsaMethodSignRaw(RSA* rsa,
335 size_t* out_len,
336 uint8_t* out,
337 size_t max_out,
338 const uint8_t* in,
339 size_t in_len,
340 int padding) {
341 DCHECK_EQ(RSA_PKCS1_PADDING, padding);
342 if (padding != RSA_PKCS1_PADDING) {
343 OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_UNKNOWN_PADDING_TYPE);
344 return 0;
345 }
346
347 // BoringSSL calls only sign_raw, not sign, in pre-TLS-1.2 MD5/SHA1
348 // signatures. This hook is implemented only for that case.
349 //
350 // TODO(davidben): Make client auth in BoringSSL call RSA_sign with
351 // NID_md5_sha1. https://crbug.com/437023
352 if (in_len != MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH) {
353 OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_INVALID_MESSAGE_LENGTH);
354 return 0;
355 }
356 if (!RsaSignPKCS1(rsa, NID_md5_sha1, in, in_len, out, max_out, out_len))
357 return 0;
358 return 1;
359 }
360
361 int RsaMethodDecrypt(RSA* rsa,
362 size_t* out_len,
363 uint8_t* out,
364 size_t max_out,
365 const uint8_t* in,
366 size_t in_len,
367 int padding) {
368 NOTIMPLEMENTED();
369 OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE);
370 return 0;
371 }
372
373 int RsaMethodVerifyRaw(RSA* rsa,
374 size_t* out_len,
375 uint8_t* out,
376 size_t max_out,
377 const uint8_t* in,
378 size_t in_len,
379 int padding) {
380 NOTIMPLEMENTED();
381 OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_UNKNOWN_ALGORITHM_TYPE);
382 return 0;
383 }
384
385 int RsaMethodSupportsDigest(const RSA* rsa, const EVP_MD* md) {
386 const KeyExData* ex_data = RsaGetExData(rsa);
387 if (!ex_data) {
388 NOTREACHED();
389 return 0;
390 }
391
392 int hash_nid = EVP_MD_type(md);
393 if (ex_data->key->dwKeySpec == CERT_NCRYPT_KEY_SPEC) {
394 // Only hashes which appear in RsaSignPKCS1 are supported.
395 if (hash_nid != NID_sha1 && hash_nid != NID_sha256 &&
396 hash_nid != NID_sha384 && hash_nid != NID_sha512) {
397 return 0;
398 }
399
400 // If the key is a 1024-bit RSA, assume conservatively that it may only be
401 // able to sign SHA-1 hashes. This is the case for older Estonian ID cards
402 // that have 1024-bit RSA keys.
403 //
404 // CNG does provide NCryptIsAlgSupported and NCryptEnumAlgorithms functions,
405 // however they seem to both return NTE_NOT_SUPPORTED when querying the
406 // NCRYPT_PROV_HANDLE at the key's NCRYPT_PROVIDER_HANDLE_PROPERTY.
407 if (ex_data->key_length <= 1024 && hash_nid != NID_sha1)
408 return 0;
409
410 return 1;
411 } else {
412 // If the key is in CAPI, assume conservatively that the CAPI service
413 // provider may only be able to sign SHA-1 hashes.
414 return hash_nid == NID_sha1;
415 }
416 }
417
418 const RSA_METHOD win_rsa_method = {
419 {
420 0, // references
421 1, // is_static
422 },
423 nullptr, // app_data
424
425 nullptr, // init
426 nullptr, // finish
427 RsaMethodSize,
428 RsaMethodSign,
429 nullptr, // verify
430 RsaMethodEncrypt,
431 RsaMethodSignRaw,
432 RsaMethodDecrypt,
433 RsaMethodVerifyRaw,
434 nullptr, // private_transform
435 nullptr, // mod_exp
436 nullptr, // bn_mod_exp
437 RSA_FLAG_OPAQUE,
438 nullptr, // keygen
439 RsaMethodSupportsDigest,
440 };
441
442 // Custom ECDSA_METHOD that uses the platform APIs.
443 // Note that for now, only signing through ECDSA_sign() is really supported.
444 // all other method pointers are either stubs returning errors, or no-ops.
445
446 const KeyExData* EcKeyGetExData(const EC_KEY* ec_key) {
447 return reinterpret_cast<const KeyExData*>(EC_KEY_get_ex_data(
448 ec_key, global_boringssl_engine.Get().ec_key_ex_index()));
449 }
450
451 size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) {
452 const KeyExData* ex_data = EcKeyGetExData(ec_key);
453 // Windows doesn't distinguish the sizes of the curve's degree (which
454 // determines the size of a point on the curve) and the base point's order
455 // (which determines the size of a scalar). For P-256, P-384, and P-521, these
456 // two sizes are the same.
457 //
458 // See
459 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375520(v=vs.85).a spx
460 // which uses the same length for both.
461 return (ex_data->key_length + 7) / 8;
462 }
463
464 int EcdsaMethodSign(const uint8_t* digest,
465 size_t digest_len,
466 uint8_t* out_sig,
467 unsigned int* out_sig_len,
468 EC_KEY* ec_key) {
469 const KeyExData* ex_data = EcKeyGetExData(ec_key);
470 // Only CNG supports ECDSA.
471 if (!ex_data || ex_data->key->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
472 NOTREACHED();
473 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
474 return 0;
475 }
476
477 size_t degree = (ex_data->key_length + 7) / 8;
478 if (degree == 0) {
479 NOTREACHED();
480 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
481 return 0;
482 }
483 std::vector<uint8_t> raw_sig(degree * 2);
484
485 DWORD signature_len;
486 SECURITY_STATUS ncrypt_status = g_cng_functions.Get().ncrypt_sign_hash()(
487 ex_data->key->hNCryptKey, nullptr, const_cast<PBYTE>(digest), digest_len,
488 &raw_sig[0], raw_sig.size(), &signature_len, 0);
489 if (FAILED(ncrypt_status) || signature_len != raw_sig.size()) {
490 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
491 return 0;
492 }
493
494 // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
495 crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
496 if (!sig) {
497 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
498 return 0;
499 }
500 sig->r = BN_bin2bn(&raw_sig[0], degree, nullptr);
501 sig->s = BN_bin2bn(&raw_sig[degree], degree, nullptr);
502 if (!sig->r || !sig->s) {
503 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
504 return 0;
505 }
506
507 // Ensure the DER-encoded signature fits in the bounds.
508 int len = i2d_ECDSA_SIG(sig.get(), nullptr);
509 if (len < 0 || len > ECDSA_size(ec_key)) {
510 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
511 return 0;
512 }
513
514 len = i2d_ECDSA_SIG(sig.get(), &out_sig);
515 if (len < 0) {
516 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
517 return 0;
518 }
519 *out_sig_len = len;
520 return 1;
521 }
522
523 int EcdsaMethodVerify(const uint8_t* digest,
524 size_t digest_len,
525 const uint8_t* sig,
526 size_t sig_len,
527 EC_KEY* eckey) {
528 NOTIMPLEMENTED();
529 OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_NOT_IMPLEMENTED);
530 return 0;
531 }
532
533 const ECDSA_METHOD win_ecdsa_method = {
534 {
535 0, // references
536 1, // is_static
537 },
538 nullptr, // app_data
539
540 nullptr, // init
541 nullptr, // finish
542 EcdsaMethodGroupOrderSize,
543 EcdsaMethodSign,
544 EcdsaMethodVerify,
545 ECDSA_FLAG_OPAQUE,
546 };
547
548 // Determines the key type and length of |key|. The type is returned as an
549 // OpenSSL EVP_PKEY type. The key length for RSA key is the size of the RSA
550 // modulus in bits. For an ECDSA key, it is the number of bits to represent the
551 // group order. It returns true on success and false on failure.
552 bool GetKeyInfo(PCERT_KEY_CONTEXT key, int* out_type, DWORD* out_length) {
553 if (key->dwKeySpec == CERT_NCRYPT_KEY_SPEC) {
554 DWORD prop_len;
555 SECURITY_STATUS status = g_cng_functions.Get().ncrypt_get_property()(
556 key->hNCryptKey, NCRYPT_ALGORITHM_GROUP_PROPERTY, nullptr, 0, &prop_len,
557 0);
558 if (FAILED(status) || prop_len == 0 || prop_len % 2 != 0) {
559 LOG(ERROR) << "Could not query CNG key type: " << status;
560 return false;
561 }
562
563 std::vector<BYTE> prop_buf(prop_len);
564 status = g_cng_functions.Get().ncrypt_get_property()(
565 key->hNCryptKey, NCRYPT_ALGORITHM_GROUP_PROPERTY, &prop_buf[0],
566 prop_buf.size(), &prop_len, 0);
567 if (FAILED(status) || prop_len == 0 || prop_len % 2 != 0) {
568 LOG(ERROR) << "Could not query CNG key type: " << status;
569 return false;
570 }
571
572 int type;
573 const wchar_t* alg = reinterpret_cast<const wchar_t*>(&prop_buf[0]);
574 if (wcsncmp(NCRYPT_RSA_ALGORITHM_GROUP, alg, prop_len / 2) == 0) {
575 type = EVP_PKEY_RSA;
576 } else if (wcsncmp(NCRYPT_ECDSA_ALGORITHM_GROUP, alg, prop_len / 2) == 0 ||
577 wcsncmp(NCRYPT_ECDH_ALGORITHM_GROUP, alg, prop_len / 2) == 0) {
578 // Importing an ECDSA key via PKCS #12 seems to label it as ECDH rather
579 // than ECDSA, so also allow ECDH.
580 type = EVP_PKEY_EC;
581 } else {
582 LOG(ERROR) << "Unknown CNG key type: "
583 << std::wstring(alg, wcsnlen(alg, prop_len / 2));
584 return false;
585 }
586
587 DWORD length;
588 prop_len;
589 status = g_cng_functions.Get().ncrypt_get_property()(
590 key->hNCryptKey, NCRYPT_LENGTH_PROPERTY,
591 reinterpret_cast<BYTE*>(&length), sizeof(DWORD), &prop_len, 0);
592 if (FAILED(status)) {
593 LOG(ERROR) << "Could not get CNG key length " << status;
594 return false;
595 }
596 DCHECK_EQ(sizeof(DWORD), prop_len);
597
598 *out_type = type;
599 *out_length = length;
600 return true;
601 }
602
603 crypto::ScopedHCRYPTKEY hcryptkey;
604 if (!CryptGetUserKey(key->hCryptProv, key->dwKeySpec, hcryptkey.receive())) {
605 PLOG(ERROR) << "Could not get CAPI key handle";
606 return false;
607 }
608
609 ALG_ID alg_id;
610 DWORD prop_len = sizeof(alg_id);
611 if (!CryptGetKeyParam(hcryptkey.get(), KP_ALGID,
612 reinterpret_cast<BYTE*>(&alg_id), &prop_len, 0)) {
613 PLOG(ERROR) << "Could not query CAPI key type";
614 return false;
615 }
616
617 if (alg_id != CALG_RSA_SIGN && alg_id != CALG_RSA_KEYX) {
618 LOG(ERROR) << "Unknown CAPI key type: " << alg_id;
619 return false;
620 }
621
622 DWORD length;
623 prop_len = sizeof(DWORD);
624 if (!CryptGetKeyParam(hcryptkey.get(), KP_KEYLEN,
625 reinterpret_cast<BYTE*>(&length), &prop_len, 0)) {
626 PLOG(ERROR) << "Could not get CAPI key length";
627 return false;
628 }
629 DCHECK_EQ(sizeof(DWORD), prop_len);
630
631 *out_type = EVP_PKEY_RSA;
632 *out_length = length;
633 return true;
634 }
635
636 crypto::ScopedEVP_PKEY CreateRSAWrapper(ScopedCERT_KEY_CONTEXT key,
637 DWORD key_length) {
638 crypto::ScopedRSA rsa(RSA_new_method(global_boringssl_engine.Get().engine()));
639 if (!rsa)
640 return nullptr;
641
642 RSA_set_ex_data(rsa.get(), global_boringssl_engine.Get().rsa_ex_index(),
643 new KeyExData(key.Pass(), key_length));
644
645 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
646 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
647 return nullptr;
648 return pkey.Pass();
649 }
650
651 crypto::ScopedEVP_PKEY CreateECDSAWrapper(ScopedCERT_KEY_CONTEXT key,
652 DWORD key_length) {
653 crypto::ScopedEC_KEY ec_key(
654 EC_KEY_new_method(global_boringssl_engine.Get().engine()));
655 if (!ec_key)
656 return nullptr;
657
658 EC_KEY_set_ex_data(ec_key.get(),
659 global_boringssl_engine.Get().ec_key_ex_index(),
660 new KeyExData(key.Pass(), key_length));
661
662 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
663 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()))
664 return nullptr;
665
666 return pkey.Pass();
667 }
668
669 } // namespace
670
11 crypto::ScopedEVP_PKEY FetchClientCertPrivateKey( 671 crypto::ScopedEVP_PKEY FetchClientCertPrivateKey(
12 const X509Certificate* certificate) { 672 const X509Certificate* certificate) {
13 // TODO(davidben): Implement on Windows. 673 PCCERT_CONTEXT cert_context = certificate->os_cert_handle();
14 NOTIMPLEMENTED(); 674
15 return crypto::ScopedEVP_PKEY(); 675 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov = 0;
676 DWORD key_spec = 0;
677 BOOL must_free = FALSE;
678 DWORD flags = 0;
679 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
680 flags |= CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
681
682 if (!CryptAcquireCertificatePrivateKey(cert_context, flags, nullptr,
683 &crypt_prov, &key_spec, &must_free)) {
684 PLOG(WARNING) << "Could not acquire private key";
685 return nullptr;
686 }
687
688 // Should never get a cached handle back - ownership must always be
689 // transferred.
690 CHECK_EQ(must_free, TRUE);
691 ScopedCERT_KEY_CONTEXT key(new CERT_KEY_CONTEXT);
692 key->dwKeySpec = key_spec;
693 key->hCryptProv = crypt_prov;
694
695 int key_type;
696 DWORD key_length;
697 if (!GetKeyInfo(key.get(), &key_type, &key_length))
698 return nullptr;
699
700 switch (key_type) {
701 case EVP_PKEY_RSA:
702 return CreateRSAWrapper(key.Pass(), key_length);
703 case EVP_PKEY_EC:
704 return CreateECDSAWrapper(key.Pass(), key_length);
705 default:
706 return nullptr;
707 }
16 } 708 }
17 709
18 } // namespace net 710 } // namespace net
OLDNEW
« no previous file with comments | « net/ssl/openssl_platform_key_mac.cc ('k') | net/ssl/ssl_connection_status_flags.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698