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