OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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/android/keystore_openssl.h" | |
6 | |
7 #include <jni.h> | |
8 #include <openssl/bn.h> | |
9 #include <openssl/ec.h> | |
10 #include <openssl/engine.h> | |
11 #include <openssl/err.h> | |
12 #include <openssl/evp.h> | |
13 #include <openssl/rsa.h> | |
14 #include <stdint.h> | |
15 | |
16 #include <memory> | |
17 | |
18 #include "base/android/build_info.h" | |
19 #include "base/android/scoped_java_ref.h" | |
20 #include "base/lazy_instance.h" | |
21 #include "base/logging.h" | |
22 #include "crypto/openssl_util.h" | |
23 #include "net/android/keystore.h" | |
24 #include "net/android/legacy_openssl.h" | |
25 #include "net/ssl/scoped_openssl_types.h" | |
26 #include "net/ssl/ssl_client_cert_type.h" | |
27 | |
28 // IMPORTANT NOTE: The following code will currently only work when used | |
29 // to implement client certificate support with OpenSSL. That's because | |
30 // only the signing operations used in this use case are implemented here. | |
31 // | |
32 // Generally speaking, OpenSSL provides many different ways to sign | |
33 // digests. This code doesn't support all these cases, only the ones that | |
34 // are required to sign the digest during the OpenSSL handshake for TLS. | |
35 // | |
36 // The OpenSSL EVP_PKEY type is a generic wrapper around key pairs. | |
37 // Internally, it can hold a pointer to a RSA or ECDSA structure, which model | |
38 // keypair implementations of each respective crypto algorithm. | |
39 // | |
40 // The RSA type has a 'method' field pointer to a vtable-like structure | |
41 // called a RSA_METHOD. This contains several function pointers that | |
42 // correspond to operations on RSA keys (e.g. decode/encode with public | |
43 // key, decode/encode with private key, signing, validation), as well as | |
44 // a few flags. | |
45 // | |
46 // For example, the RSA_sign() function will call "method->rsa_sign()" if | |
47 // method->rsa_sign is not NULL, otherwise, it will perform a regular | |
48 // signing operation using the other fields in the RSA structure (which | |
49 // are used to hold the typical modulus / exponent / parameters for the | |
50 // key pair). | |
51 // | |
52 // This source file thus defines a custom RSA_METHOD structure whose | |
53 // fields point to static methods used to implement the corresponding | |
54 // RSA operation using platform Android APIs. | |
55 // | |
56 // However, the platform APIs require a jobject JNI reference to work. It must | |
57 // be stored in the RSA instance, or made accessible when the custom RSA | |
58 // methods are called. This is done by storing it in a |KeyExData| structure | |
59 // that's referenced by the key using |EX_DATA|. | |
60 | |
61 using base::android::JavaRef; | |
62 using base::android::ScopedJavaGlobalRef; | |
63 using base::android::ScopedJavaLocalRef; | |
64 | |
65 namespace net { | |
66 namespace android { | |
67 | |
68 namespace { | |
69 | |
70 extern const RSA_METHOD android_rsa_method; | |
71 extern const ECDSA_METHOD android_ecdsa_method; | |
72 | |
73 // KeyExData contains the data that is contained in the EX_DATA of the RSA and | |
74 // EC_KEY objects that are created to wrap Android system keys. | |
75 struct KeyExData { | |
76 // private_key contains a reference to a Java, private-key object. | |
77 ScopedJavaGlobalRef<jobject> private_key; | |
78 // legacy_rsa, if not NULL, points to an RSA* in the system's OpenSSL (which | |
79 // might not be ABI compatible with Chromium). | |
80 AndroidRSA* legacy_rsa; | |
81 // cached_size contains the "size" of the key. This is the size of the | |
82 // modulus (in bytes) for RSA, or the group order size for ECDSA. This | |
83 // avoids calling into Java to calculate the size. | |
84 size_t cached_size; | |
85 }; | |
86 | |
87 // ExDataDup is called when one of the RSA or EC_KEY objects is duplicated. We | |
88 // don't support this and it should never happen. | |
89 int ExDataDup(CRYPTO_EX_DATA* to, | |
90 const CRYPTO_EX_DATA* from, | |
91 void** from_d, | |
92 int index, | |
93 long argl, | |
94 void* argp) { | |
95 CHECK_EQ((void*)NULL, *from_d); | |
96 return 0; | |
97 } | |
98 | |
99 // ExDataFree is called when one of the RSA or EC_KEY objects is freed. | |
100 void ExDataFree(void* parent, | |
101 void* ptr, | |
102 CRYPTO_EX_DATA* ad, | |
103 int index, | |
104 long argl, | |
105 void* argp) { | |
106 // Ensure the global JNI reference created with this wrapper is | |
107 // properly destroyed with it. | |
108 KeyExData* ex_data = reinterpret_cast<KeyExData*>(ptr); | |
109 delete ex_data; | |
110 } | |
111 | |
112 // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA by | |
113 // forwarding the requested operations to the Java libraries. | |
114 class BoringSSLEngine { | |
115 public: | |
116 BoringSSLEngine() | |
117 : rsa_index_(RSA_get_ex_new_index(0 /* argl */, | |
118 NULL /* argp */, | |
119 NULL /* new_func */, | |
120 ExDataDup, | |
121 ExDataFree)), | |
122 ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */, | |
123 NULL /* argp */, | |
124 NULL /* new_func */, | |
125 ExDataDup, | |
126 ExDataFree)), | |
127 engine_(ENGINE_new()) { | |
128 ENGINE_set_RSA_method(engine_, &android_rsa_method, | |
129 sizeof(android_rsa_method)); | |
130 ENGINE_set_ECDSA_method(engine_, &android_ecdsa_method, | |
131 sizeof(android_ecdsa_method)); | |
132 } | |
133 | |
134 int rsa_ex_index() const { return rsa_index_; } | |
135 int ec_key_ex_index() const { return ec_key_index_; } | |
136 | |
137 const ENGINE* engine() const { return engine_; } | |
138 | |
139 private: | |
140 const int rsa_index_; | |
141 const int ec_key_index_; | |
142 ENGINE* const engine_; | |
143 }; | |
144 | |
145 base::LazyInstance<BoringSSLEngine>::Leaky global_boringssl_engine = | |
146 LAZY_INSTANCE_INITIALIZER; | |
147 | |
148 // VectorBignumSize returns the number of bytes needed to represent the bignum | |
149 // given in |v|, i.e. the length of |v| less any leading zero bytes. | |
150 size_t VectorBignumSize(const std::vector<uint8_t>& v) { | |
151 size_t size = v.size(); | |
152 // Ignore any leading zero bytes. | |
153 for (size_t i = 0; i < v.size() && v[i] == 0; i++) { | |
154 size--; | |
155 } | |
156 return size; | |
157 } | |
158 | |
159 KeyExData* RsaGetExData(const RSA* rsa) { | |
160 return reinterpret_cast<KeyExData*>( | |
161 RSA_get_ex_data(rsa, global_boringssl_engine.Get().rsa_ex_index())); | |
162 } | |
163 | |
164 size_t RsaMethodSize(const RSA* rsa) { | |
165 const KeyExData* ex_data = RsaGetExData(rsa); | |
166 return ex_data->cached_size; | |
167 } | |
168 | |
169 int RsaMethodEncrypt(RSA* rsa, | |
170 size_t* out_len, | |
171 uint8_t* out, | |
172 size_t max_out, | |
173 const uint8_t* in, | |
174 size_t in_len, | |
175 int padding) { | |
176 NOTIMPLEMENTED(); | |
177 OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); | |
178 return 0; | |
179 } | |
180 | |
181 int RsaMethodSignRaw(RSA* rsa, | |
182 size_t* out_len, | |
183 uint8_t* out, | |
184 size_t max_out, | |
185 const uint8_t* in, | |
186 size_t in_len, | |
187 int padding) { | |
188 DCHECK_EQ(RSA_PKCS1_PADDING, padding); | |
189 if (padding != RSA_PKCS1_PADDING) { | |
190 // TODO(davidben): If we need to, we can implement RSA_NO_PADDING | |
191 // by using javax.crypto.Cipher and picking either the | |
192 // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as | |
193 // appropriate. I believe support for both of these was added in | |
194 // the same Android version as the "NONEwithRSA" | |
195 // java.security.Signature algorithm, so the same version checks | |
196 // for GetRsaLegacyKey should work. | |
197 OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE); | |
198 return 0; | |
199 } | |
200 | |
201 // Retrieve private key JNI reference. | |
202 const KeyExData* ex_data = RsaGetExData(rsa); | |
203 if (!ex_data || !ex_data->private_key.obj()) { | |
204 LOG(WARNING) << "Null JNI reference passed to RsaMethodSignRaw!"; | |
205 OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); | |
206 return 0; | |
207 } | |
208 | |
209 // Pre-4.2 legacy codepath. | |
210 if (ex_data->legacy_rsa) { | |
211 int ret = ex_data->legacy_rsa->meth->rsa_priv_enc( | |
212 in_len, in, out, ex_data->legacy_rsa, ANDROID_RSA_PKCS1_PADDING); | |
213 if (ret < 0) { | |
214 LOG(WARNING) << "Could not sign message in RsaMethodSignRaw!"; | |
215 // System OpenSSL will use a separate error queue, so it is still | |
216 // necessary to push a new error. | |
217 // | |
218 // TODO(davidben): It would be good to also clear the system error queue | |
219 // if there were some way to convince Java to do it. (Without going | |
220 // through Java, it's difficult to get a handle on a system OpenSSL | |
221 // function; dlopen loads a second copy.) | |
222 OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); | |
223 return 0; | |
224 } | |
225 *out_len = ret; | |
226 return 1; | |
227 } | |
228 | |
229 base::StringPiece from_piece(reinterpret_cast<const char*>(in), in_len); | |
230 std::vector<uint8_t> result; | |
231 // For RSA keys, this function behaves as RSA_private_encrypt with | |
232 // PKCS#1 padding. | |
233 if (!RawSignDigestWithPrivateKey(ex_data->private_key, from_piece, &result)) { | |
234 LOG(WARNING) << "Could not sign message in RsaMethodSignRaw!"; | |
235 OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); | |
236 return 0; | |
237 } | |
238 | |
239 size_t expected_size = static_cast<size_t>(RSA_size(rsa)); | |
240 if (result.size() > expected_size) { | |
241 LOG(ERROR) << "RSA Signature size mismatch, actual: " << result.size() | |
242 << ", expected <= " << expected_size; | |
243 OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); | |
244 return 0; | |
245 } | |
246 | |
247 if (max_out < expected_size) { | |
248 OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE); | |
249 return 0; | |
250 } | |
251 | |
252 // Copy result to OpenSSL-provided buffer. RawSignDigestWithPrivateKey | |
253 // should pad with leading 0s, but if it doesn't, pad the result. | |
254 size_t zero_pad = expected_size - result.size(); | |
255 memset(out, 0, zero_pad); | |
256 memcpy(out + zero_pad, &result[0], result.size()); | |
257 *out_len = expected_size; | |
258 | |
259 return 1; | |
260 } | |
261 | |
262 int RsaMethodDecrypt(RSA* rsa, | |
263 size_t* out_len, | |
264 uint8_t* out, | |
265 size_t max_out, | |
266 const uint8_t* in, | |
267 size_t in_len, | |
268 int padding) { | |
269 NOTIMPLEMENTED(); | |
270 OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); | |
271 return 0; | |
272 } | |
273 | |
274 int RsaMethodVerifyRaw(RSA* rsa, | |
275 size_t* out_len, | |
276 uint8_t* out, | |
277 size_t max_out, | |
278 const uint8_t* in, | |
279 size_t in_len, | |
280 int padding) { | |
281 NOTIMPLEMENTED(); | |
282 OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); | |
283 return 0; | |
284 } | |
285 | |
286 const RSA_METHOD android_rsa_method = { | |
287 { | |
288 0 /* references */, 1 /* is_static */ | |
289 } /* common */, | |
290 nullptr /* app_data */, | |
291 | |
292 nullptr /* init */, | |
293 nullptr /* finish */, | |
294 RsaMethodSize, | |
295 nullptr /* sign */, | |
296 nullptr /* verify */, | |
297 RsaMethodEncrypt, | |
298 RsaMethodSignRaw, | |
299 RsaMethodDecrypt, | |
300 RsaMethodVerifyRaw, | |
301 nullptr /* private_transform */, | |
302 nullptr /* mod_exp */, | |
303 nullptr /* bn_mod_exp */, | |
304 RSA_FLAG_OPAQUE, | |
305 nullptr /* keygen */, | |
306 nullptr /* multi_prime_keygen */, | |
307 nullptr /* supports_digest */, | |
308 }; | |
309 | |
310 // Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object. | |
311 // |private_key| is the JNI reference (local or global) to the object. | |
312 // |legacy_rsa|, if non-NULL, is a pointer to the system OpenSSL RSA object | |
313 // backing |private_key|. This parameter is only used for Android < 4.2 to | |
314 // implement key operations not exposed by the platform. | |
315 // Returns a new EVP_PKEY on success, NULL otherwise. | |
316 // On success, this creates a new global JNI reference to the object | |
317 // that is owned by and destroyed with the EVP_PKEY. I.e. caller can | |
318 // free |private_key| after the call. | |
319 crypto::ScopedEVP_PKEY CreateRsaPkeyWrapper( | |
320 const JavaRef<jobject>& private_key, | |
321 AndroidRSA* legacy_rsa, | |
322 const crypto::OpenSSLErrStackTracer& tracer) { | |
323 crypto::ScopedRSA rsa(RSA_new_method(global_boringssl_engine.Get().engine())); | |
324 | |
325 std::vector<uint8_t> modulus; | |
326 if (!GetRSAKeyModulus(private_key, &modulus)) { | |
327 LOG(ERROR) << "Failed to get private key modulus"; | |
328 return nullptr; | |
329 } | |
330 | |
331 std::unique_ptr<KeyExData> ex_data(new KeyExData); | |
332 ex_data->private_key.Reset(private_key); | |
333 if (ex_data->private_key.is_null()) { | |
334 LOG(ERROR) << "Could not create global JNI reference"; | |
335 return nullptr; | |
336 } | |
337 ex_data->legacy_rsa = legacy_rsa; | |
338 ex_data->cached_size = VectorBignumSize(modulus); | |
339 | |
340 RSA_set_ex_data(rsa.get(), global_boringssl_engine.Get().rsa_ex_index(), | |
341 ex_data.release()); | |
342 | |
343 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); | |
344 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) | |
345 return nullptr; | |
346 return pkey; | |
347 } | |
348 | |
349 // On Android < 4.2, the libkeystore.so ENGINE uses CRYPTO_EX_DATA and is not | |
350 // added to the global engine list. If all references to it are dropped, OpenSSL | |
351 // will dlclose the module, leaving a dangling function pointer in the RSA | |
352 // CRYPTO_EX_DATA class. To work around this, leak an extra reference to the | |
353 // ENGINE we extract in GetRsaLegacyKey. | |
354 // | |
355 // In 4.2, this change avoids the problem: | |
356 // https://android.googlesource.com/platform/libcore/+/106a8928fb4249f2f3d4dba1d
ddbe73ca5cb3d61 | |
357 // | |
358 // https://crbug.com/381465 | |
359 class KeystoreEngineWorkaround { | |
360 public: | |
361 KeystoreEngineWorkaround() {} | |
362 | |
363 void LeakEngine(const JavaRef<jobject>& private_key) { | |
364 if (!engine_.is_null()) | |
365 return; | |
366 ScopedJavaLocalRef<jobject> engine = | |
367 GetOpenSSLEngineForPrivateKey(private_key); | |
368 if (engine.is_null()) { | |
369 NOTREACHED(); | |
370 return; | |
371 } | |
372 engine_.Reset(engine); | |
373 } | |
374 | |
375 private: | |
376 ScopedJavaGlobalRef<jobject> engine_; | |
377 }; | |
378 | |
379 void LeakEngine(const JavaRef<jobject>& private_key) { | |
380 static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance = | |
381 LAZY_INSTANCE_INITIALIZER; | |
382 s_instance.Get().LeakEngine(private_key); | |
383 } | |
384 | |
385 // Creates an EVP_PKEY wrapper corresponding to the RSA key | |
386 // |private_key|. Returns nullptr on failure. | |
387 crypto::ScopedEVP_PKEY GetRsaPkeyWrapper(const JavaRef<jobject>& private_key) { | |
388 const int kAndroid42ApiLevel = 17; | |
389 crypto::OpenSSLErrStackTracer tracer(FROM_HERE); | |
390 | |
391 if (base::android::BuildInfo::GetInstance()->sdk_int() >= | |
392 kAndroid42ApiLevel) { | |
393 return CreateRsaPkeyWrapper(private_key, nullptr, tracer); | |
394 } | |
395 | |
396 // Route around platform limitation: if Android < 4.2, then | |
397 // base::android::RawSignDigestWithPrivateKey() cannot work, so try to get the | |
398 // system OpenSSL's EVP_PKEY backing this PrivateKey object. | |
399 AndroidEVP_PKEY* sys_pkey = GetOpenSSLSystemHandleForPrivateKey(private_key); | |
400 if (sys_pkey == nullptr) | |
401 return nullptr; | |
402 | |
403 if (sys_pkey->type != ANDROID_EVP_PKEY_RSA) { | |
404 LOG(ERROR) << "Private key has wrong type!"; | |
405 return nullptr; | |
406 } | |
407 | |
408 AndroidRSA* sys_rsa = sys_pkey->pkey.rsa; | |
409 if (sys_rsa->engine) { | |
410 // |private_key| may not have an engine if the PrivateKey did not come | |
411 // from the key store, such as in unit tests. | |
412 if (strcmp(sys_rsa->engine->id, "keystore") == 0) { | |
413 LeakEngine(private_key); | |
414 } else { | |
415 NOTREACHED(); | |
416 } | |
417 } | |
418 | |
419 return CreateRsaPkeyWrapper(private_key, sys_rsa, tracer); | |
420 } | |
421 | |
422 // Custom ECDSA_METHOD that uses the platform APIs. | |
423 // Note that for now, only signing through ECDSA_sign() is really supported. | |
424 // all other method pointers are either stubs returning errors, or no-ops. | |
425 | |
426 const JavaRef<jobject>& EcKeyGetKey(const EC_KEY* ec_key) { | |
427 KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data( | |
428 ec_key, global_boringssl_engine.Get().ec_key_ex_index())); | |
429 return ex_data->private_key; | |
430 } | |
431 | |
432 size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) { | |
433 KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data( | |
434 ec_key, global_boringssl_engine.Get().ec_key_ex_index())); | |
435 return ex_data->cached_size; | |
436 } | |
437 | |
438 int EcdsaMethodSign(const uint8_t* digest, | |
439 size_t digest_len, | |
440 uint8_t* sig, | |
441 unsigned int* sig_len, | |
442 EC_KEY* ec_key) { | |
443 // Retrieve private key JNI reference. | |
444 const JavaRef<jobject>& private_key = EcKeyGetKey(ec_key); | |
445 if (private_key.is_null()) { | |
446 LOG(WARNING) << "Null JNI reference passed to EcdsaMethodSign!"; | |
447 return 0; | |
448 } | |
449 // Sign message with it through JNI. | |
450 std::vector<uint8_t> signature; | |
451 base::StringPiece digest_sp(reinterpret_cast<const char*>(digest), | |
452 digest_len); | |
453 if (!RawSignDigestWithPrivateKey(private_key, digest_sp, &signature)) { | |
454 LOG(WARNING) << "Could not sign message in EcdsaMethodSign!"; | |
455 return 0; | |
456 } | |
457 | |
458 // Note: With ECDSA, the actual signature may be smaller than | |
459 // ECDSA_size(). | |
460 size_t max_expected_size = ECDSA_size(ec_key); | |
461 if (signature.size() > max_expected_size) { | |
462 LOG(ERROR) << "ECDSA Signature size mismatch, actual: " << signature.size() | |
463 << ", expected <= " << max_expected_size; | |
464 return 0; | |
465 } | |
466 | |
467 memcpy(sig, &signature[0], signature.size()); | |
468 *sig_len = signature.size(); | |
469 return 1; | |
470 } | |
471 | |
472 int EcdsaMethodVerify(const uint8_t* digest, | |
473 size_t digest_len, | |
474 const uint8_t* sig, | |
475 size_t sig_len, | |
476 EC_KEY* ec_key) { | |
477 NOTIMPLEMENTED(); | |
478 OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED); | |
479 return 0; | |
480 } | |
481 | |
482 // Setup an EVP_PKEY to wrap an existing platform PrivateKey object. | |
483 // |private_key| is the JNI reference (local or global) to the object. | |
484 // Returns a new EVP_PKEY on success, NULL otherwise. | |
485 // On success, this creates a global JNI reference to the object that | |
486 // is owned by and destroyed with the EVP_PKEY. I.e. the caller shall | |
487 // always free |private_key| after the call. | |
488 crypto::ScopedEVP_PKEY GetEcdsaPkeyWrapper( | |
489 const JavaRef<jobject>& private_key) { | |
490 crypto::OpenSSLErrStackTracer tracer(FROM_HERE); | |
491 crypto::ScopedEC_KEY ec_key( | |
492 EC_KEY_new_method(global_boringssl_engine.Get().engine())); | |
493 | |
494 std::vector<uint8_t> order; | |
495 if (!GetECKeyOrder(private_key, &order)) { | |
496 LOG(ERROR) << "Can't extract order parameter from EC private key"; | |
497 return nullptr; | |
498 } | |
499 | |
500 std::unique_ptr<KeyExData> ex_data(new KeyExData); | |
501 ex_data->private_key.Reset(private_key); | |
502 if (ex_data->private_key.is_null()) { | |
503 LOG(ERROR) << "Can't create global JNI reference"; | |
504 return nullptr; | |
505 } | |
506 ex_data->legacy_rsa = nullptr; | |
507 ex_data->cached_size = VectorBignumSize(order); | |
508 | |
509 EC_KEY_set_ex_data(ec_key.get(), | |
510 global_boringssl_engine.Get().ec_key_ex_index(), | |
511 ex_data.release()); | |
512 | |
513 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); | |
514 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get())) | |
515 return nullptr; | |
516 return pkey; | |
517 } | |
518 | |
519 const ECDSA_METHOD android_ecdsa_method = { | |
520 { | |
521 0 /* references */, 1 /* is_static */ | |
522 } /* common */, | |
523 NULL /* app_data */, | |
524 | |
525 NULL /* init */, | |
526 NULL /* finish */, | |
527 EcdsaMethodGroupOrderSize, | |
528 EcdsaMethodSign, | |
529 EcdsaMethodVerify, | |
530 ECDSA_FLAG_OPAQUE, | |
531 }; | |
532 | |
533 } // namespace | |
534 | |
535 crypto::ScopedEVP_PKEY GetOpenSSLPrivateKeyWrapper( | |
536 const JavaRef<jobject>& private_key) { | |
537 // Create sub key type, depending on private key's algorithm type. | |
538 PrivateKeyType key_type = GetPrivateKeyType(private_key); | |
539 switch (key_type) { | |
540 case PRIVATE_KEY_TYPE_RSA: | |
541 return GetRsaPkeyWrapper(private_key); | |
542 case PRIVATE_KEY_TYPE_ECDSA: | |
543 return GetEcdsaPkeyWrapper(private_key); | |
544 default: | |
545 LOG(WARNING) | |
546 << "GetOpenSSLPrivateKeyWrapper() called with invalid key type"; | |
547 return nullptr; | |
548 } | |
549 } | |
550 | |
551 } // namespace android | |
552 } // namespace net | |
OLD | NEW |