OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser; | 5 package org.chromium.chrome.browser; |
6 | 6 |
7 import android.app.Activity; | 7 import android.app.Activity; |
8 import android.content.ActivityNotFoundException; | 8 import android.content.ActivityNotFoundException; |
9 import android.content.Context; | 9 import android.content.Context; |
10 import android.content.DialogInterface; | 10 import android.content.DialogInterface; |
11 import android.content.DialogInterface.OnClickListener; | 11 import android.content.DialogInterface.OnClickListener; |
12 import android.os.AsyncTask; | 12 import android.os.AsyncTask; |
13 import android.security.KeyChain; | 13 import android.security.KeyChain; |
14 import android.security.KeyChainAliasCallback; | 14 import android.security.KeyChainAliasCallback; |
15 import android.security.KeyChainException; | 15 import android.security.KeyChainException; |
16 import android.support.v7.app.AlertDialog; | 16 import android.support.v7.app.AlertDialog; |
17 import android.util.Log; | 17 import android.util.Log; |
18 | 18 |
19 import org.chromium.base.ThreadUtils; | 19 import org.chromium.base.ThreadUtils; |
20 import org.chromium.base.VisibleForTesting; | 20 import org.chromium.base.VisibleForTesting; |
21 import org.chromium.base.annotations.CalledByNative; | 21 import org.chromium.base.annotations.CalledByNative; |
22 import org.chromium.base.annotations.JNINamespace; | 22 import org.chromium.base.annotations.JNINamespace; |
23 import org.chromium.chrome.R; | 23 import org.chromium.chrome.R; |
24 import org.chromium.chrome.browser.smartcard.PKCS11AuthenticationManager; | |
25 import org.chromium.net.AndroidPrivateKey; | 24 import org.chromium.net.AndroidPrivateKey; |
26 import org.chromium.net.DefaultAndroidKeyStore; | 25 import org.chromium.net.DefaultAndroidKeyStore; |
27 import org.chromium.ui.base.WindowAndroid; | 26 import org.chromium.ui.base.WindowAndroid; |
28 | 27 |
29 import java.security.Principal; | 28 import java.security.Principal; |
30 import java.security.cert.CertificateEncodingException; | 29 import java.security.cert.CertificateEncodingException; |
31 import java.security.cert.X509Certificate; | 30 import java.security.cert.X509Certificate; |
32 | 31 |
33 import javax.security.auth.x500.X500Principal; | 32 import javax.security.auth.x500.X500Principal; |
34 | 33 |
35 /** | 34 /** |
36 * Handles selection of client certificate on the Java side. This class is respo nsible for selection | 35 * Handles selection of client certificate on the Java side. This class is respo nsible for selection |
37 * of the client certificate to be used for authentication and retrieval of the private key and full | 36 * of the client certificate to be used for authentication and retrieval of the private key and full |
38 * certificate chain. | 37 * certificate chain. |
39 * | 38 * |
40 * The entry point is selectClientCertificate() and it will be called on the UI thread. Then the | 39 * The entry point is selectClientCertificate() and it will be called on the UI thread. Then the |
41 * class will construct and run an appropriate CertAsyncTask, that will run in b ackground, and | 40 * class will construct and run an appropriate CertAsyncTask, that will run in b ackground, and |
42 * finally pass the results back to the UI thread, which will return to the nati ve code. | 41 * finally pass the results back to the UI thread, which will return to the nati ve code. |
43 */ | 42 */ |
44 @JNINamespace("chrome::android") | 43 @JNINamespace("chrome::android") |
45 public class SSLClientCertificateRequest { | 44 public class SSLClientCertificateRequest { |
46 static final String TAG = "SSLClientCertificateRequest"; | 45 static final String TAG = "SSLClientCertificateRequest"; |
47 | 46 |
48 private static final DefaultAndroidKeyStore sLocalKeyStore = | 47 private static final DefaultAndroidKeyStore sLocalKeyStore = |
49 new DefaultAndroidKeyStore(); | 48 new DefaultAndroidKeyStore(); |
Yaron
2015/11/25 16:24:31
Any reason not to remove the AndroidKeyStore inter
davidben
2015/11/25 17:48:14
+1
I believe this is the CL that introduced it: h
Changwan Ryu
2015/11/26 02:52:11
No need to keep this. Thanks for the pointer. Done
| |
50 | 49 |
51 /** | 50 /** |
52 * Common implementation for anynchronous task of handling the certificate r equest. This | 51 * Implementation for anynchronous task of handling the certificate request. This |
53 * AsyncTask uses the abstract methods to retrieve the authentication materi al from a | 52 * AsyncTask retrieve the authentication material from the system key store. |
54 * generalized key store. The key store is accessed in background, as the AP Is being exercised | 53 * The key store is accessed in background, as the APIs being exercised |
55 * may be blocking. The results are posted back to native on the UI thread. | 54 * may be blocking. The results are posted back to native on the UI thread. |
56 */ | 55 */ |
57 abstract static class CertAsyncTask extends AsyncTask<Void, Void, Void> { | 56 private static class CertAsyncTaskKeyChain extends AsyncTask<Void, Void, Voi d> { |
58 // These fields will store the results computed in doInBackground so tha t they can be posted | 57 // These fields will store the results computed in doInBackground so tha t they can be posted |
59 // back in onPostExecute. | 58 // back in onPostExecute. |
60 private byte[][] mEncodedChain; | 59 private byte[][] mEncodedChain; |
61 private AndroidPrivateKey mAndroidPrivateKey; | 60 private AndroidPrivateKey mAndroidPrivateKey; |
62 | 61 |
63 // Pointer to the native certificate request needed to return the result s. | 62 // Pointer to the native certificate request needed to return the result s. |
64 private final long mNativePtr; | 63 private final long mNativePtr; |
65 | 64 |
66 CertAsyncTask(long nativePtr) { | 65 final Context mContext; |
66 final String mAlias; | |
67 | |
68 CertAsyncTaskKeyChain(Context context, long nativePtr, String alias) { | |
67 mNativePtr = nativePtr; | 69 mNativePtr = nativePtr; |
70 mContext = context; | |
71 assert alias != null; | |
72 mAlias = alias; | |
68 } | 73 } |
69 | 74 |
70 // These overriden methods will be used to access the key store. | |
71 abstract String getAlias(); | |
72 abstract AndroidPrivateKey getPrivateKey(String alias); | |
73 abstract X509Certificate[] getCertificateChain(String alias); | |
74 | |
75 @Override | 75 @Override |
76 protected Void doInBackground(Void... params) { | 76 protected Void doInBackground(Void... params) { |
77 String alias = getAlias(); | 77 String alias = getAlias(); |
78 if (alias == null) return null; | 78 if (alias == null) return null; |
79 | 79 |
80 AndroidPrivateKey key = getPrivateKey(alias); | 80 AndroidPrivateKey key = getPrivateKey(alias); |
81 X509Certificate[] chain = getCertificateChain(alias); | 81 X509Certificate[] chain = getCertificateChain(alias); |
82 | 82 |
83 if (key == null || chain == null || chain.length == 0) { | 83 if (key == null || chain == null || chain.length == 0) { |
84 Log.w(TAG, "Empty client certificate chain?"); | 84 Log.w(TAG, "Empty client certificate chain?"); |
(...skipping 14 matching lines...) Expand all Loading... | |
99 mEncodedChain = encodedChain; | 99 mEncodedChain = encodedChain; |
100 mAndroidPrivateKey = key; | 100 mAndroidPrivateKey = key; |
101 return null; | 101 return null; |
102 } | 102 } |
103 | 103 |
104 @Override | 104 @Override |
105 protected void onPostExecute(Void result) { | 105 protected void onPostExecute(Void result) { |
106 ThreadUtils.assertOnUiThread(); | 106 ThreadUtils.assertOnUiThread(); |
107 nativeOnSystemRequestCompletion(mNativePtr, mEncodedChain, mAndroidP rivateKey); | 107 nativeOnSystemRequestCompletion(mNativePtr, mEncodedChain, mAndroidP rivateKey); |
108 } | 108 } |
109 } | |
110 | 109 |
111 /** Implementation of CertAsyncTask for the system KeyChain API. */ | 110 private String getAlias() { |
112 private static class CertAsyncTaskKeyChain extends CertAsyncTask { | |
113 final Context mContext; | |
114 final String mAlias; | |
115 | |
116 CertAsyncTaskKeyChain(Context context, long nativePtr, String alias) { | |
117 super(nativePtr); | |
118 mContext = context; | |
119 assert alias != null; | |
120 mAlias = alias; | |
121 } | |
122 | |
123 @Override | |
124 String getAlias() { | |
125 return mAlias; | 111 return mAlias; |
126 } | 112 } |
127 | 113 |
128 @Override | 114 private AndroidPrivateKey getPrivateKey(String alias) { |
129 AndroidPrivateKey getPrivateKey(String alias) { | |
130 try { | 115 try { |
131 return sLocalKeyStore.createKey(KeyChain.getPrivateKey(mContext, alias)); | 116 return sLocalKeyStore.createKey(KeyChain.getPrivateKey(mContext, alias)); |
132 } catch (KeyChainException e) { | 117 } catch (KeyChainException e) { |
133 Log.w(TAG, "KeyChainException when looking for '" + alias + "' c ertificate"); | 118 Log.w(TAG, "KeyChainException when looking for '" + alias + "' c ertificate"); |
134 return null; | 119 return null; |
135 } catch (InterruptedException e) { | 120 } catch (InterruptedException e) { |
136 Log.w(TAG, "InterruptedException when looking for '" + alias + " 'certificate"); | 121 Log.w(TAG, "InterruptedException when looking for '" + alias + " 'certificate"); |
137 return null; | 122 return null; |
138 } | 123 } |
139 } | 124 } |
140 | 125 |
141 @Override | 126 private X509Certificate[] getCertificateChain(String alias) { |
142 X509Certificate[] getCertificateChain(String alias) { | |
143 try { | 127 try { |
144 return KeyChain.getCertificateChain(mContext, alias); | 128 return KeyChain.getCertificateChain(mContext, alias); |
145 } catch (KeyChainException e) { | 129 } catch (KeyChainException e) { |
146 Log.w(TAG, "KeyChainException when looking for '" + alias + "' c ertificate"); | 130 Log.w(TAG, "KeyChainException when looking for '" + alias + "' c ertificate"); |
147 return null; | 131 return null; |
148 } catch (InterruptedException e) { | 132 } catch (InterruptedException e) { |
149 Log.w(TAG, "InterruptedException when looking for '" + alias + " 'certificate"); | 133 Log.w(TAG, "InterruptedException when looking for '" + alias + " 'certificate"); |
150 return null; | 134 return null; |
151 } | 135 } |
152 } | 136 } |
153 } | 137 } |
154 | 138 |
155 /** Implementation of CertAsyncTask for use with a PKCS11-backed KeyStore. * / | |
156 private static class CertAsyncTaskPKCS11 extends CertAsyncTask { | |
157 private final PKCS11AuthenticationManager mPKCS11AuthManager; | |
158 private final String mHostName; | |
159 private final int mPort; | |
160 | |
161 CertAsyncTaskPKCS11(long nativePtr, String hostName, int port, | |
162 PKCS11AuthenticationManager pkcs11CardAuthManager) { | |
163 super(nativePtr); | |
164 mHostName = hostName; | |
165 mPort = port; | |
166 mPKCS11AuthManager = pkcs11CardAuthManager; | |
167 } | |
168 | |
169 @Override | |
170 String getAlias() { | |
171 return mPKCS11AuthManager.getClientCertificateAlias(mHostName, mPort ); | |
172 } | |
173 | |
174 @Override | |
175 AndroidPrivateKey getPrivateKey(String alias) { | |
176 return mPKCS11AuthManager.getPrivateKey(alias); | |
177 } | |
178 | |
179 @Override | |
180 X509Certificate[] getCertificateChain(String alias) { | |
181 return mPKCS11AuthManager.getCertificateChain(alias); | |
182 } | |
183 } | |
184 | |
185 /** | 139 /** |
186 * The system KeyChain API will call us back on the alias() method, passing the alias of the | 140 * The system KeyChain API will call us back on the alias() method, passing the alias of the |
187 * certificate selected by the user. | 141 * certificate selected by the user. |
188 */ | 142 */ |
189 private static class KeyChainCertSelectionCallback implements KeyChainAliasC allback { | 143 private static class KeyChainCertSelectionCallback implements KeyChainAliasC allback { |
190 private final long mNativePtr; | 144 private final long mNativePtr; |
191 private final Context mContext; | 145 private final Context mContext; |
192 | 146 |
193 KeyChainCertSelectionCallback(Context context, long nativePtr) { | 147 KeyChainCertSelectionCallback(Context context, long nativePtr) { |
194 mContext = context; | 148 mContext = context; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
268 /** | 222 /** |
269 * Builds and shows the dialog. | 223 * Builds and shows the dialog. |
270 */ | 224 */ |
271 public void show() { | 225 public void show() { |
272 final AlertDialog.Builder builder = | 226 final AlertDialog.Builder builder = |
273 new AlertDialog.Builder(mActivity, R.style.AlertDialogTheme) ; | 227 new AlertDialog.Builder(mActivity, R.style.AlertDialogTheme) ; |
274 builder.setTitle(R.string.client_cert_unsupported_title) | 228 builder.setTitle(R.string.client_cert_unsupported_title) |
275 .setMessage(R.string.client_cert_unsupported_message) | 229 .setMessage(R.string.client_cert_unsupported_message) |
276 .setNegativeButton(R.string.close, | 230 .setNegativeButton(R.string.close, |
277 new OnClickListener() { | 231 new OnClickListener() { |
232 @Override | |
278 public void onClick(DialogInterface dialog, int which) { | 233 public void onClick(DialogInterface dialog, int which) { |
279 // Do nothing | 234 // Do nothing |
280 } | 235 } |
281 }); | 236 }); |
282 builder.show(); | 237 builder.show(); |
283 } | 238 } |
284 } | 239 } |
285 | 240 |
286 /** | 241 /** |
287 * Create a new asynchronous request to select a client certificate. | 242 * Create a new asynchronous request to select a client certificate. |
(...skipping 26 matching lines...) Expand all Loading... | |
314 try { | 269 try { |
315 for (int n = 0; n < encodedPrincipals.length; n++) { | 270 for (int n = 0; n < encodedPrincipals.length; n++) { |
316 principals[n] = new X500Principal(encodedPrincipals[n]); | 271 principals[n] = new X500Principal(encodedPrincipals[n]); |
317 } | 272 } |
318 } catch (Exception e) { | 273 } catch (Exception e) { |
319 Log.w(TAG, "Exception while decoding issuers list: " + e); | 274 Log.w(TAG, "Exception while decoding issuers list: " + e); |
320 return false; | 275 return false; |
321 } | 276 } |
322 } | 277 } |
323 | 278 |
324 final Principal[] principalsForCallback = principals; | 279 KeyChainCertSelectionCallback callback = |
325 // Certificate for client authentication can be obtained either from the system store of | 280 new KeyChainCertSelectionCallback(activity.getApplicationContext (), |
326 // from a smart card (if available). | 281 nativePtr); |
327 Runnable useSystemStore = new Runnable() { | 282 KeyChainCertSelectionWrapper keyChain = new KeyChainCertSelectionWrapper (activity, |
328 @Override | 283 callback, keyTypes, principals, hostName, port, null); |
329 public void run() { | 284 maybeShowCertSelection(keyChain, callback, |
330 KeyChainCertSelectionCallback callback = | 285 new CertSelectionFailureDialog(activity)); |
331 new KeyChainCertSelectionCallback(activity.getApplicatio nContext(), | |
332 nativePtr); | |
333 KeyChainCertSelectionWrapper keyChain = new KeyChainCertSelectio nWrapper(activity, | |
334 callback, keyTypes, principalsForCallback, hostName, por t, null); | |
335 maybeShowCertSelection(keyChain, callback, | |
336 new CertSelectionFailureDialog(activity)); | |
337 } | |
338 }; | |
339 | |
340 final Context appContext = activity.getApplicationContext(); | |
341 final PKCS11AuthenticationManager smartCardAuthManager = | |
342 ((ChromeApplication) appContext).getPKCS11AuthenticationManager( ); | |
343 if (smartCardAuthManager.isPKCS11AuthEnabled()) { | |
344 // Smart card support is available, prompt the user whether to use i t or Android system | |
345 // store. | |
346 Runnable useSmartCard = new Runnable() { | |
347 @Override | |
348 public void run() { | |
349 new CertAsyncTaskPKCS11(nativePtr, hostName, port, | |
350 smartCardAuthManager).execute(); | |
351 } | |
352 }; | |
353 Runnable cancelRunnable = new Runnable() { | |
354 @Override | |
355 public void run() { | |
356 // We took ownership of the request, need to delete it. | |
357 nativeOnSystemRequestCompletion(nativePtr, null, null); | |
358 } | |
359 }; | |
360 | |
361 KeyStoreSelectionDialog selectionDialog = new KeyStoreSelectionDialo g( | |
Yaron
2015/11/25 16:24:31
You can remove this class too and the strings adde
Changwan Ryu
2015/11/26 02:52:11
Done.
| |
362 useSystemStore, useSmartCard, cancelRunnable); | |
363 selectionDialog.show(activity.getFragmentManager(), null); | |
364 } else { | |
365 // Smart card support is not available, use the system store uncondi tionally. | |
366 useSystemStore.run(); | |
367 } | |
368 | 286 |
369 // We've taken ownership of the native ssl request object. | 287 // We've taken ownership of the native ssl request object. |
370 return true; | 288 return true; |
371 } | 289 } |
372 | 290 |
373 /** | 291 /** |
374 * Attempt to show the certificate selection dialog and shows the provided | 292 * Attempt to show the certificate selection dialog and shows the provided |
375 * CertSelectionFailureDialog if the platform's cert selection activity can' t be found. | 293 * CertSelectionFailureDialog if the platform's cert selection activity can' t be found. |
376 */ | 294 */ |
377 @VisibleForTesting | 295 @VisibleForTesting |
(...skipping 16 matching lines...) Expand all Loading... | |
394 Log.d(TAG, "ClientCertificatesChanged!"); | 312 Log.d(TAG, "ClientCertificatesChanged!"); |
395 nativeNotifyClientCertificatesChangedOnIOThread(); | 313 nativeNotifyClientCertificatesChangedOnIOThread(); |
396 } | 314 } |
397 | 315 |
398 private static native void nativeNotifyClientCertificatesChangedOnIOThread() ; | 316 private static native void nativeNotifyClientCertificatesChangedOnIOThread() ; |
399 | 317 |
400 // Called to pass request results to native side. | 318 // Called to pass request results to native side. |
401 private static native void nativeOnSystemRequestCompletion( | 319 private static native void nativeOnSystemRequestCompletion( |
402 long requestPtr, byte[][] certChain, AndroidPrivateKey androidKey); | 320 long requestPtr, byte[][] certChain, AndroidPrivateKey androidKey); |
403 } | 321 } |
OLD | NEW |