Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / net / android / java / src / org / chromium / net / DefaultAndroidKeyStore.java
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.
4
5 package org.chromium.net;
6
7 import android.util.Log;
8
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;
21
22 /**
23  * Simple implementation of the AndroidKeyStore for use with an in-process Java KeyStore.
24  */
25 public class DefaultAndroidKeyStore implements AndroidKeyStore {
26
27     private static final String TAG = "AndroidKeyStoreInProcessImpl";
28
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;
34
35         DefaultAndroidPrivateKey(PrivateKey key, DefaultAndroidKeyStore store) {
36             mKey = key;
37             mStore = store;
38         }
39
40         PrivateKey getJavaKey() {
41             return mKey;
42         }
43
44         @Override
45         public AndroidKeyStore getKeyStore() {
46             return mStore;
47         }
48     }
49
50     public AndroidPrivateKey createKey(PrivateKey javaKey) {
51         return new DefaultAndroidPrivateKey(javaKey, this);
52     }
53
54     @Override
55     public byte[] getRSAKeyModulus(AndroidPrivateKey key) {
56         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
57         if (javaKey instanceof RSAKey) {
58             return ((RSAKey) javaKey).getModulus().toByteArray();
59         }
60         Log.w(TAG, "Not a RSAKey instance!");
61         return null;
62     }
63
64     @Override
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();
70         }
71         Log.w(TAG, "Not a DSAKey instance!");
72         return null;
73     }
74
75     @Override
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();
81         }
82         Log.w(TAG, "Not an ECKey instance!");
83         return null;
84     }
85
86    @Override
87     public byte[] getPrivateKeyEncodedBytes(AndroidPrivateKey key) {
88         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
89         return javaKey.getEncoded();
90     }
91
92     @Override
93     public byte[] rawSignDigestWithPrivateKey(AndroidPrivateKey key,
94                                                      byte[] message) {
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
100         try {
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");
110             }
111         } catch (NoSuchAlgorithmException e) {
112             ;
113         }
114
115         if (signature == null) {
116             Log.e(TAG, "Unsupported private key algorithm: " + javaKey.getAlgorithm());
117             return null;
118         }
119
120         // Sign the message.
121         try {
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);
128             return null;
129         }
130     }
131
132     @Override
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;
141         else
142             return PrivateKeyType.INVALID;
143     }
144
145     @Override
146     public long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
147         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
148         // Sanity checks
149         if (javaKey == null) {
150             Log.e(TAG, "key == null");
151             return 0;
152         }
153         if (!(javaKey instanceof RSAPrivateKey)) {
154             Log.e(TAG, "does not implement RSAPrivateKey");
155             return 0;
156         }
157         // First, check that this is a proper instance of OpenSSLRSAPrivateKey
158         // or one of its sub-classes.
159         Class<?> superClass;
160         try {
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);
168             return 0;
169         }
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());
176             return 0;
177         }
178
179         try {
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;
187             try {
188                 opensslKey = getKey.invoke(javaKey);
189             } finally {
190                 getKey.setAccessible(false);
191             }
192             if (opensslKey == null) {
193                 // Bail when detecting OEM "enhancement".
194                 Log.e(TAG, "getOpenSSLKey() returned null");
195                 return 0;
196             }
197
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;
206             try {
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);
211                 return 0;
212             }
213             getPkeyContext.setAccessible(true);
214             long evp_pkey = 0;
215             try {
216                 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
217             } finally {
218                 getPkeyContext.setAccessible(false);
219             }
220             if (evp_pkey == 0) {
221                 // The PrivateKey is probably rotten for some reason.
222                 Log.e(TAG, "getPkeyContext() returned null");
223             }
224             return evp_pkey;
225
226         } catch (Exception e) {
227             Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
228             return 0;
229         }
230     }
231
232     @Override
233     public void releaseKey(AndroidPrivateKey key) {
234         // no-op for in-process. GC will handle key collection
235     }
236 }