Chromium Code Reviews| 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 |