1 // Copyright 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.
5 package org.chromium.net;
7 import android.util.Log;
9 import java.lang.reflect.Method;
10 import java.security.NoSuchAlgorithmException;
11 import java.security.PrivateKey;
12 import java.security.Signature;
13 import java.security.interfaces.DSAKey;
14 import java.security.interfaces.DSAParams;
15 import java.security.interfaces.DSAPrivateKey;
16 import java.security.interfaces.ECKey;
17 import java.security.interfaces.ECPrivateKey;
18 import java.security.interfaces.RSAKey;
19 import java.security.interfaces.RSAPrivateKey;
20 import java.security.spec.ECParameterSpec;
23 * Simple implementation of the AndroidKeyStore for use with an in-process Java KeyStore.
25 public class DefaultAndroidKeyStore implements AndroidKeyStore {
27 private static final String TAG = "AndroidKeyStoreInProcessImpl";
29 private static class DefaultAndroidPrivateKey implements AndroidPrivateKey {
30 // The actual Java key being wrapped.
31 final PrivateKey mKey;
32 // Key store handling this key.
33 final DefaultAndroidKeyStore mStore;
35 DefaultAndroidPrivateKey(PrivateKey key, DefaultAndroidKeyStore store) {
40 PrivateKey getJavaKey() {
45 public AndroidKeyStore getKeyStore() {
50 public AndroidPrivateKey createKey(PrivateKey javaKey) {
51 return new DefaultAndroidPrivateKey(javaKey, this);
55 public byte[] getRSAKeyModulus(AndroidPrivateKey key) {
56 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
57 if (javaKey instanceof RSAKey) {
58 return ((RSAKey) javaKey).getModulus().toByteArray();
60 Log.w(TAG, "Not a RSAKey instance!");
65 public byte[] getDSAKeyParamQ(AndroidPrivateKey key) {
66 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
67 if (javaKey instanceof DSAKey) {
68 DSAParams params = ((DSAKey) javaKey).getParams();
69 return params.getQ().toByteArray();
71 Log.w(TAG, "Not a DSAKey instance!");
76 public byte[] getECKeyOrder(AndroidPrivateKey key) {
77 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
78 if (javaKey instanceof ECKey) {
79 ECParameterSpec params = ((ECKey) javaKey).getParams();
80 return params.getOrder().toByteArray();
82 Log.w(TAG, "Not an ECKey instance!");
87 public byte[] getPrivateKeyEncodedBytes(AndroidPrivateKey key) {
88 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
89 return javaKey.getEncoded();
93 public byte[] rawSignDigestWithPrivateKey(AndroidPrivateKey key,
95 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
96 // Get the Signature for this key.
97 Signature signature = null;
98 // Hint: Algorithm names come from:
99 // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
101 if (javaKey instanceof RSAPrivateKey) {
102 // IMPORTANT: Due to a platform bug, this will throw NoSuchAlgorithmException
103 // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
104 // See https://android-review.googlesource.com/#/c/40352/
105 signature = Signature.getInstance("NONEwithRSA");
106 } else if (javaKey instanceof DSAPrivateKey) {
107 signature = Signature.getInstance("NONEwithDSA");
108 } else if (javaKey instanceof ECPrivateKey) {
109 signature = Signature.getInstance("NONEwithECDSA");
111 } catch (NoSuchAlgorithmException e) {
115 if (signature == null) {
116 Log.e(TAG, "Unsupported private key algorithm: " + javaKey.getAlgorithm());
122 signature.initSign(javaKey);
123 signature.update(message);
124 return signature.sign();
125 } catch (Exception e) {
126 Log.e(TAG, "Exception while signing message with " + javaKey.getAlgorithm() +
127 " private key: " + e);
133 public int getPrivateKeyType(AndroidPrivateKey key) {
134 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
135 if (javaKey instanceof RSAPrivateKey)
136 return PrivateKeyType.RSA;
137 if (javaKey instanceof DSAPrivateKey)
138 return PrivateKeyType.DSA;
139 if (javaKey instanceof ECPrivateKey)
140 return PrivateKeyType.ECDSA;
142 return PrivateKeyType.INVALID;
146 public long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
147 PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
149 if (javaKey == null) {
150 Log.e(TAG, "key == null");
153 if (!(javaKey instanceof RSAPrivateKey)) {
154 Log.e(TAG, "does not implement RSAPrivateKey");
157 // First, check that this is a proper instance of OpenSSLRSAPrivateKey
158 // or one of its sub-classes.
161 superClass = Class.forName(
162 "org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
163 } catch (Exception e) {
164 // This may happen if the target device has a completely different
165 // implementation of the java.security APIs, compared to vanilla
166 // Android. Highly unlikely, but still possible.
167 Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
170 if (!superClass.isInstance(javaKey)) {
171 // This may happen if the PrivateKey was not created by the "AndroidOpenSSL"
172 // provider, which should be the default. That could happen if an OEM decided
173 // to implement a different default provider. Also highly unlikely.
174 Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" +
175 javaKey.getClass().getCanonicalName());
180 // Use reflection to invoke the 'getOpenSSLKey()' method on
181 // the private key. This returns another Java object that wraps
182 // a native EVP_PKEY. Note that the method is final, so calling
183 // the superclass implementation is ok.
184 Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
185 getKey.setAccessible(true);
186 Object opensslKey = null;
188 opensslKey = getKey.invoke(javaKey);
190 getKey.setAccessible(false);
192 if (opensslKey == null) {
193 // Bail when detecting OEM "enhancement".
194 Log.e(TAG, "getOpenSSLKey() returned null");
198 // Use reflection to invoke the 'getPkeyContext' method on the
199 // result of the getOpenSSLKey(). This is an 32-bit integer
200 // which is the address of an EVP_PKEY object. Note that this
201 // method these days returns a 64-bit long, but since this code
202 // path is used for older Android versions, it may still return
203 // a 32-bit int here. To be on the safe side, we cast the return
204 // value via Number rather than directly to Integer or Long.
205 Method getPkeyContext;
207 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
208 } catch (Exception e) {
209 // Bail here too, something really not working as expected.
210 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
213 getPkeyContext.setAccessible(true);
216 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
218 getPkeyContext.setAccessible(false);
221 // The PrivateKey is probably rotten for some reason.
222 Log.e(TAG, "getPkeyContext() returned null");
226 } catch (Exception e) {
227 Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
233 public void releaseKey(AndroidPrivateKey key) {
234 // no-op for in-process. GC will handle key collection