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 package org.chromium.net; | |
6 | |
7 import android.util.Log; | |
8 | |
9 import java.lang.reflect.InvocationTargetException; | |
10 import java.lang.reflect.Method; | |
11 import java.security.interfaces.DSAPrivateKey; | |
12 import java.security.interfaces.ECPrivateKey; | |
13 import java.security.interfaces.RSAPrivateKey; | |
14 import java.security.NoSuchAlgorithmException; | |
15 import java.security.PrivateKey; | |
16 import java.security.Signature; | |
17 | |
18 import org.chromium.base.CalledByNative; | |
19 import org.chromium.base.JNINamespace; | |
20 import org.chromium.net.PrivateKeyType;; | |
21 | |
22 @JNINamespace("net::android") | |
23 public class AndroidKeyStore { | |
24 | |
25 private static final String TAG = AndroidKeyStore.class.getName(); | |
26 | |
27 //////////////////////////////////////////////////////////////////// | |
28 // | |
29 // Message signing support. | |
30 // | |
31 | |
32 /** | |
33 * Called from native code to sign a given message with a given | |
34 * PrivateKey object. This is used to implement SSL client certificate | |
35 * support through OpenSSL. Thus, it must match the behaviour of the | |
36 * OpenSSL RSA_sign() / DSA_sign() / ECDSA_sign() functions. | |
37 * @param privateKey The PrivateKey handle. | |
38 * @param message The message to sign. | |
agl
2013/01/30 14:28:53
I'm assuming here that |message| has already been
digit1
2013/01/30 16:55:01
Yes, what the function receives is the actual hash
| |
39 * @return signature as a byte buffer. | |
40 * | |
41 * Important: Due to a platform bug, this function will always fail on | |
42 * Android < 4.2 for RSA PrivateKey objects. See the | |
43 * getOpenSSLHandleForPrivateKey() below for work-around. | |
44 */ | |
45 @CalledByNative | |
46 public static byte[] signWithPrivateKey(PrivateKey privateKey, | |
Ryan Sleevi
2013/01/31 03:09:53
perhaps name this "rawSignWithPrivateKey", since i
digit1
2013/01/31 17:44:30
Done.
| |
47 byte[] message) { | |
48 // Get the Signature for this key. | |
49 Signature signature = null; | |
50 // Hint: Algorithm names come from: | |
51 // http://docs.oracle.com/javase/6/docs/technotes/guides/security/Standa rdNames.html | |
52 try { | |
53 if (privateKey instanceof RSAPrivateKey) { | |
54 // IMPORTANT: Due to what looks like a platform bug, this will | |
55 // throw NoSuchAlgorithmException on Android 4.0.x and 4.1.x. Fi xed in 4.2 | |
56 // and higher. See https://android-review.googlesource.com/#/c/4 0352/ | |
57 signature = Signature.getInstance("NONEwithRSA"); | |
58 } else if (privateKey instanceof DSAPrivateKey) { | |
59 signature = Signature.getInstance("NONEwithDSA"); | |
60 } else if (privateKey instanceof ECPrivateKey) { | |
61 signature = Signature.getInstance("NONEwithECDSA"); | |
62 } | |
63 } catch (NoSuchAlgorithmException e) { | |
64 ; | |
65 } | |
66 | |
67 if (signature == null) { | |
68 Log.e(TAG, "Unsupported private key algorithm: " + privateKey.getAlg orithm()); | |
69 return null; | |
70 } | |
71 | |
72 // Sign the message. | |
73 try { | |
74 signature.initSign(privateKey); | |
75 signature.update(message); | |
76 return signature.sign(); | |
77 } catch (Exception e) { | |
78 Log.e(TAG, "Exception while signing message with " + privateKey.getA lgorithm() + | |
79 " private key: " + e); | |
80 return null; | |
81 } | |
82 } | |
83 | |
84 /** | |
85 * Called from native code to return the type of a given PrivateKey | |
86 * object. This is an integer that maps to one of the values defined | |
87 * by org.chromium.net.PrivateKeyType, which is itself | |
88 * auto-generated from net/android/private_key_type_list.h | |
89 * @param privateKey The PrivateKey handle | |
90 * @return key type, or PrivateKeyType.INVALID if unknown. | |
91 */ | |
92 @CalledByNative | |
93 public static int getPrivateKeyType(PrivateKey privateKey) { | |
94 if (privateKey instanceof RSAPrivateKey) | |
95 return PrivateKeyType.RSA; | |
96 if (privateKey instanceof DSAPrivateKey) | |
97 return PrivateKeyType.DSA; | |
98 if (privateKey instanceof ECPrivateKey) | |
99 return PrivateKeyType.ECDSA; | |
100 else | |
101 return PrivateKeyType.INVALID; | |
102 } | |
103 | |
104 /** | |
105 * Called from native code to return the system EVP_PKEY handle | |
106 * corresponding to a given PrivateKey object, obtained through | |
107 * reflection (a.k.a. "Evil Hack"). | |
108 * | |
109 * This shall only be used for RSA private keys on Android 4.0 and | |
110 * 4.1 in order to work around the platform bug described in the | |
111 * signWithPrivateKey() comment above (namely that the NONEwithRSA | |
112 * signature is not available before Android 4.2). | |
113 * | |
114 * This assumes that the target device uses a vanilla AOSP | |
115 * implementation of its java.security classes, which is also | |
116 * based on OpenSSL (fortunately, no OEM has apperently changed to | |
117 * a different implementation, according to the Android team). | |
118 * | |
119 * Note that the object returned was created with the platform version | |
120 * of OpenSSL, and _not_ the one that comes with Chromium. Whether the | |
121 * object can be used safely with the Chromium OpenSSL library depends | |
122 * on differences between their actual ABI / implementation details. | |
123 * | |
124 * To better understand what's going on below, please refer to the | |
125 * following source files in the Android 4.0 and 4.1 source trees: | |
126 * libcore/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLR SAPrivateKey.java | |
127 * libcore/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_Native Crypto.cpp | |
128 * | |
129 * @param privateKey The PrivateKey handle. | |
130 * @return The EVP_PKEY handle, as a 32-bit integer (0 if not available) | |
131 */ | |
132 @CalledByNative | |
133 public static int getOpenSSLHandleForPrivateKey(PrivateKey privateKey) { | |
134 // Sanity checks | |
135 if (privateKey == null) { | |
136 Log.e(TAG, "privateKey == null"); | |
137 return 0; | |
138 } | |
139 if (!(privateKey instanceof RSAPrivateKey)) { | |
140 Log.e(TAG, "does not implement RSAPrivateKey"); | |
141 return 0; | |
142 } | |
143 // First, check that this is a proper instance of OpenSSLRSAPrivateKey | |
144 // or one of its sub-classes. | |
145 Class<?> superClass; | |
146 try { | |
147 superClass = Class.forName( | |
148 "org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey" ); | |
149 } catch (Exception e) { | |
150 // This may happen if the target device has a completely different | |
151 // implementation of the java.security APIs, compared to vanilla | |
152 // Android. Highly unlikely, but still possible. | |
153 Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e); | |
154 return 0; | |
155 } | |
156 if (!superClass.isInstance(privateKey)) { | |
157 // This may happen if the PrivateKey was not created by the "Android OpenSSL" | |
158 // provider, which should be the default. That could happen if an OE M decided | |
159 // to implement a different default provider. Also highly unlikely. | |
160 Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" + | |
161 privateKey.getClass().getCanonicalName()); | |
162 return 0; | |
163 } | |
164 | |
165 try { | |
166 // Use reflection to invoke the 'getOpenSSLKey()' method on | |
167 // the private key. This returns another Java object that wraps | |
168 // a native EVP_PKEY. Note that the method is final, so calling | |
169 // the superclass implementation is ok. | |
170 Method getKey = superClass.getDeclaredMethod("getOpenSSLKey"); | |
171 getKey.setAccessible(true); | |
172 Object opensslKey = null; | |
173 try { | |
174 opensslKey = getKey.invoke(privateKey); | |
175 } finally { | |
176 getKey.setAccessible(false); | |
177 } | |
178 if (opensslKey == null) { | |
179 // Bail when detecting OEM "enhancement". | |
180 Log.e(TAG, "getOpenSSLKey() returned null"); | |
181 return 0; | |
182 } | |
183 | |
184 // Use reflection to invoke the 'getPkeyContext' method on the | |
185 // result of the getOpenSSLKey(). This is an integer which | |
agl
2013/01/30 14:28:53
"which value"? Maybe "This is an integer who's val
digit1
2013/01/30 16:55:01
thanks, I'll fix this to "whose value".
| |
186 // value is the address of the wrapper EVP_PKEY object. | |
187 Method getPkeyContext; | |
188 try { | |
189 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPke yContext"); | |
190 } catch (Exception e) { | |
191 // Bail here too, something really not working as expected. | |
192 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e); | |
193 return 0; | |
194 } | |
195 getPkeyContext.setAccessible(true); | |
196 int evp_pkey = 0; | |
197 try { | |
198 evp_pkey = (Integer) getPkeyContext.invoke(opensslKey); | |
199 } finally { | |
200 getPkeyContext.setAccessible(false); | |
201 } | |
202 if (evp_pkey == 0) { | |
203 // The PrivateKey is probably rotten for some reason. | |
204 Log.e(TAG, "getPkeyContext() returned null"); | |
205 } | |
206 return evp_pkey; | |
Ryan Sleevi
2013/01/31 03:09:53
I am nervous about the potential for ABI issues he
digit1
2013/01/31 17:44:30
I'm concerned about it too. I think longer term, i
| |
207 | |
208 } catch (Exception e) { | |
209 Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handl e: " + e); | |
210 return 0; | |
211 } | |
212 } | |
213 } | |
OLD | NEW |