| 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.Context; | 9 import android.content.Context; |
| 10 import android.content.DialogInterface; |
| 11 import android.content.DialogInterface.OnClickListener; |
| 9 import android.os.AsyncTask; | 12 import android.os.AsyncTask; |
| 10 import android.security.KeyChain; | 13 import android.security.KeyChain; |
| 11 import android.security.KeyChainAliasCallback; | 14 import android.security.KeyChainAliasCallback; |
| 12 import android.security.KeyChainException; | 15 import android.security.KeyChainException; |
| 16 import android.support.v7.app.AlertDialog; |
| 13 import android.util.Log; | 17 import android.util.Log; |
| 14 | 18 |
| 15 import org.chromium.base.ThreadUtils; | 19 import org.chromium.base.ThreadUtils; |
| 20 import org.chromium.base.VisibleForTesting; |
| 16 import org.chromium.base.annotations.CalledByNative; | 21 import org.chromium.base.annotations.CalledByNative; |
| 17 import org.chromium.base.annotations.JNINamespace; | 22 import org.chromium.base.annotations.JNINamespace; |
| 23 import org.chromium.chrome.R; |
| 18 import org.chromium.chrome.browser.smartcard.PKCS11AuthenticationManager; | 24 import org.chromium.chrome.browser.smartcard.PKCS11AuthenticationManager; |
| 19 import org.chromium.net.AndroidPrivateKey; | 25 import org.chromium.net.AndroidPrivateKey; |
| 20 import org.chromium.net.DefaultAndroidKeyStore; | 26 import org.chromium.net.DefaultAndroidKeyStore; |
| 21 import org.chromium.ui.base.WindowAndroid; | 27 import org.chromium.ui.base.WindowAndroid; |
| 22 | 28 |
| 23 import java.security.Principal; | 29 import java.security.Principal; |
| 24 import java.security.cert.CertificateEncodingException; | 30 import java.security.cert.CertificateEncodingException; |
| 25 import java.security.cert.X509Certificate; | 31 import java.security.cert.X509Certificate; |
| 26 | 32 |
| 27 import javax.security.auth.x500.X500Principal; | 33 import javax.security.auth.x500.X500Principal; |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 }); | 212 }); |
| 207 } else { | 213 } else { |
| 208 new CertAsyncTaskKeyChain(mContext, mNativePtr, alias).e
xecute(); | 214 new CertAsyncTaskKeyChain(mContext, mNativePtr, alias).e
xecute(); |
| 209 } | 215 } |
| 210 } | 216 } |
| 211 }); | 217 }); |
| 212 } | 218 } |
| 213 } | 219 } |
| 214 | 220 |
| 215 /** | 221 /** |
| 222 * Wrapper class for the static KeyChain#choosePrivateKeyAlias method to fac
ilitate testing. |
| 223 */ |
| 224 @VisibleForTesting |
| 225 static class KeyChainCertSelectionWrapper { |
| 226 private final Activity mActivity; |
| 227 private final KeyChainAliasCallback mCallback; |
| 228 private final String[] mKeyTypes; |
| 229 private final Principal[] mPrincipalsForCallback; |
| 230 private final String mHostName; |
| 231 private final int mPort; |
| 232 private final String mAlias; |
| 233 |
| 234 public KeyChainCertSelectionWrapper(Activity activity, KeyChainAliasCall
back callback, |
| 235 String[] keyTypes, Principal[] principalsForCallback, String hos
tName, int port, |
| 236 String alias) { |
| 237 mActivity = activity; |
| 238 mCallback = callback; |
| 239 mKeyTypes = keyTypes; |
| 240 mPrincipalsForCallback = principalsForCallback; |
| 241 mHostName = hostName; |
| 242 mPort = port; |
| 243 mAlias = alias; |
| 244 } |
| 245 |
| 246 /** |
| 247 * Calls KeyChain#choosePrivateKeyAlias with the provided arguments. |
| 248 */ |
| 249 public void choosePrivateKeyAlias() throws ActivityNotFoundException { |
| 250 KeyChain.choosePrivateKeyAlias(mActivity, mCallback, mKeyTypes, mPri
ncipalsForCallback, |
| 251 mHostName, mPort, mAlias); |
| 252 } |
| 253 } |
| 254 |
| 255 /** |
| 256 * Dialog that explains to the user that client certificates aren't supporte
d on their operating |
| 257 * system. Separated out into its own class to allow Robolectric unit testin
g of |
| 258 * maybeShowCertSelection without depending on Chrome resources. |
| 259 */ |
| 260 @VisibleForTesting |
| 261 static class CertSelectionFailureDialog { |
| 262 private final Activity mActivity; |
| 263 |
| 264 public CertSelectionFailureDialog(Activity activity) { |
| 265 mActivity = activity; |
| 266 } |
| 267 |
| 268 /** |
| 269 * Builds and shows the dialog. |
| 270 */ |
| 271 public void show() { |
| 272 final AlertDialog.Builder builder = |
| 273 new AlertDialog.Builder(mActivity, R.style.AlertDialogTheme)
; |
| 274 builder.setTitle(R.string.client_cert_unsupported_title) |
| 275 .setMessage(R.string.client_cert_unsupported_message) |
| 276 .setNegativeButton(R.string.close, |
| 277 new OnClickListener() { |
| 278 public void onClick(DialogInterface dialog, int
which) { |
| 279 // Do nothing |
| 280 } |
| 281 }); |
| 282 builder.show(); |
| 283 } |
| 284 } |
| 285 |
| 286 /** |
| 216 * Create a new asynchronous request to select a client certificate. | 287 * Create a new asynchronous request to select a client certificate. |
| 217 * | 288 * |
| 218 * @param nativePtr The native object responsible for this request. | 289 * @param nativePtr The native object responsible for this request. |
| 219 * @param window A WindowAndroid instance. | 290 * @param window A WindowAndroid instance. |
| 220 * @param keyTypes The list of supported key exchange types. | 291 * @param keyTypes The list of supported key exchange types. |
| 221 * @param encodedPrincipals The list of CA DistinguishedNames. | 292 * @param encodedPrincipals The list of CA DistinguishedNames. |
| 222 * @param hostName The server host name is available (empty otherwi
se). | 293 * @param hostName The server host name is available (empty otherwi
se). |
| 223 * @param port The server port if available (0 otherwise). | 294 * @param port The server port if available (0 otherwise). |
| 224 * @return true on success. | 295 * @return true on success. |
| 225 * Note that nativeOnSystemRequestComplete will be called iff this method re
turns true. | 296 * Note that nativeOnSystemRequestComplete will be called iff this method re
turns true. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 252 | 323 |
| 253 final Principal[] principalsForCallback = principals; | 324 final Principal[] principalsForCallback = principals; |
| 254 // Certificate for client authentication can be obtained either from the
system store of | 325 // Certificate for client authentication can be obtained either from the
system store of |
| 255 // from a smart card (if available). | 326 // from a smart card (if available). |
| 256 Runnable useSystemStore = new Runnable() { | 327 Runnable useSystemStore = new Runnable() { |
| 257 @Override | 328 @Override |
| 258 public void run() { | 329 public void run() { |
| 259 KeyChainCertSelectionCallback callback = | 330 KeyChainCertSelectionCallback callback = |
| 260 new KeyChainCertSelectionCallback(activity.getApplicatio
nContext(), | 331 new KeyChainCertSelectionCallback(activity.getApplicatio
nContext(), |
| 261 nativePtr); | 332 nativePtr); |
| 262 KeyChain.choosePrivateKeyAlias(activity, callback, keyTypes, pri
ncipalsForCallback, | 333 KeyChainCertSelectionWrapper keyChain = new KeyChainCertSelectio
nWrapper(activity, |
| 263 hostName, port, null); | 334 callback, keyTypes, principalsForCallback, hostName, por
t, null); |
| 335 maybeShowCertSelection(keyChain, callback, |
| 336 new CertSelectionFailureDialog(activity)); |
| 264 } | 337 } |
| 265 }; | 338 }; |
| 266 | 339 |
| 267 final Context appContext = activity.getApplicationContext(); | 340 final Context appContext = activity.getApplicationContext(); |
| 268 final PKCS11AuthenticationManager smartCardAuthManager = | 341 final PKCS11AuthenticationManager smartCardAuthManager = |
| 269 ((ChromeApplication) appContext).getPKCS11AuthenticationManager(
); | 342 ((ChromeApplication) appContext).getPKCS11AuthenticationManager(
); |
| 270 if (smartCardAuthManager.isPKCS11AuthEnabled()) { | 343 if (smartCardAuthManager.isPKCS11AuthEnabled()) { |
| 271 // Smart card support is available, prompt the user whether to use i
t or Android system | 344 // Smart card support is available, prompt the user whether to use i
t or Android system |
| 272 // store. | 345 // store. |
| 273 Runnable useSmartCard = new Runnable() { | 346 Runnable useSmartCard = new Runnable() { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 290 selectionDialog.show(activity.getFragmentManager(), null); | 363 selectionDialog.show(activity.getFragmentManager(), null); |
| 291 } else { | 364 } else { |
| 292 // Smart card support is not available, use the system store uncondi
tionally. | 365 // Smart card support is not available, use the system store uncondi
tionally. |
| 293 useSystemStore.run(); | 366 useSystemStore.run(); |
| 294 } | 367 } |
| 295 | 368 |
| 296 // We've taken ownership of the native ssl request object. | 369 // We've taken ownership of the native ssl request object. |
| 297 return true; | 370 return true; |
| 298 } | 371 } |
| 299 | 372 |
| 373 /** |
| 374 * Attempt to show the certificate selection dialog and shows the provided |
| 375 * CertSelectionFailureDialog if the platform's cert selection activity can'
t be found. |
| 376 */ |
| 377 @VisibleForTesting |
| 378 static void maybeShowCertSelection(KeyChainCertSelectionWrapper keyChain, |
| 379 KeyChainAliasCallback callback, CertSelectionFailureDialog failureDi
alog) { |
| 380 try { |
| 381 keyChain.choosePrivateKeyAlias(); |
| 382 } catch (ActivityNotFoundException e) { |
| 383 // This exception can be hit when a platform is missing the activity
to select |
| 384 // a client certificate. It gets handled here to avoid a crash. |
| 385 // Complete the callback without selecting a certificate. |
| 386 callback.alias(null); |
| 387 // Show a dialog letting the user know that the system does not supp
ort |
| 388 // client certificate selection. |
| 389 failureDialog.show(); |
| 390 } |
| 391 } |
| 392 |
| 300 public static void notifyClientCertificatesChangedOnIOThread() { | 393 public static void notifyClientCertificatesChangedOnIOThread() { |
| 301 Log.d(TAG, "ClientCertificatesChanged!"); | 394 Log.d(TAG, "ClientCertificatesChanged!"); |
| 302 nativeNotifyClientCertificatesChangedOnIOThread(); | 395 nativeNotifyClientCertificatesChangedOnIOThread(); |
| 303 } | 396 } |
| 304 | 397 |
| 305 private static native void nativeNotifyClientCertificatesChangedOnIOThread()
; | 398 private static native void nativeNotifyClientCertificatesChangedOnIOThread()
; |
| 306 | 399 |
| 307 // Called to pass request results to native side. | 400 // Called to pass request results to native side. |
| 308 private static native void nativeOnSystemRequestCompletion( | 401 private static native void nativeOnSystemRequestCompletion( |
| 309 long requestPtr, byte[][] certChain, AndroidPrivateKey androidKey); | 402 long requestPtr, byte[][] certChain, AndroidPrivateKey androidKey); |
| 310 } | 403 } |
| OLD | NEW |