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.chrome.browser; |
| 6 |
| 7 import java.security.cert.CertificateEncodingException; |
| 8 import java.security.cert.X509Certificate; |
| 9 import java.security.Principal; |
| 10 import java.security.PrivateKey; |
| 11 import javax.security.auth.x500.X500Principal; |
| 12 |
| 13 import android.app.Activity; |
| 14 import android.content.Context; |
| 15 import android.os.AsyncTask; |
| 16 import android.security.KeyChain; |
| 17 import android.security.KeyChainAliasCallback; |
| 18 import android.security.KeyChainException; |
| 19 import android.util.Log; |
| 20 |
| 21 import org.chromium.base.ActivityStatus; |
| 22 import org.chromium.base.CalledByNative; |
| 23 import org.chromium.base.JNINamespace; |
| 24 import org.chromium.base.ThreadUtils; |
| 25 |
| 26 @JNINamespace("chrome::android") |
| 27 public class SSLClientCertificateRequest extends AsyncTask<Void, Void, Void> |
| 28 implements KeyChainAliasCallback { |
| 29 |
| 30 static final String TAG = "SSLClientCertificateRequest"; |
| 31 |
| 32 // ClientCertRequest models an asynchronous client certificate request |
| 33 // on the Dalvik side. There is a matching C++ ClientCertRequest |
| 34 // class that has _slightly_ different lifecycles (e.g. if a Tab |
| 35 // is closed while an asynchronous request is still pending, the native |
| 36 // C++ class will be deleted, but the Java one must persist until the |
| 37 // system sends its answer to the activity). |
| 38 // |
| 39 // Each request must be started from the UI thread, and the system |
| 40 // will answer the KeyChain.choosePrivateKeyAlias() call with a |
| 41 // private key alias string, which can be used to call |
| 42 // KeyChain.getCertificateChain() and KeyChain.getPrivateKey(), |
| 43 // however these functions are blocking and can't be called on the |
| 44 // UI thread. |
| 45 // |
| 46 // To solve this, start an AsyncTask when an alias is received. |
| 47 // it will retrieve the certificate chain and private key in the |
| 48 // background, then later send the result back to the UI thread. |
| 49 // |
| 50 private final int mNativePtr; |
| 51 private String mAlias; |
| 52 private byte[][] mEncodedChain; |
| 53 private PrivateKey mPrivateKey; |
| 54 |
| 55 private SSLClientCertificateRequest(int nativePtr) { |
| 56 mNativePtr = nativePtr; |
| 57 mAlias = null; |
| 58 mEncodedChain = null; |
| 59 mPrivateKey = null; |
| 60 } |
| 61 |
| 62 // KeyChainAliasCallback implementation |
| 63 @Override |
| 64 public void alias(String alias) { |
| 65 if (alias == null) { |
| 66 // No certificate was selected. |
| 67 onPostExecute(null); |
| 68 } else { |
| 69 mAlias = alias; |
| 70 // Launch background thread. |
| 71 execute(); |
| 72 } |
| 73 } |
| 74 |
| 75 @Override |
| 76 protected Void doInBackground(Void... params) { |
| 77 // Executed in a background thread, can call blocking APIs. |
| 78 X509Certificate[] chain = null; |
| 79 PrivateKey key = null; |
| 80 Context context = ActivityStatus.getActivity().getApplicationContext(); |
| 81 try { |
| 82 key = KeyChain.getPrivateKey(context, mAlias); |
| 83 chain = KeyChain.getCertificateChain(context, mAlias); |
| 84 } catch (KeyChainException e) { |
| 85 Log.w(TAG, "KeyChainException when looking for '" + mAlias + "' cert
ificate"); |
| 86 return null; |
| 87 } catch (InterruptedException e) { |
| 88 Log.w(TAG, "InterruptedException when looking for '" + mAlias + "'ce
rtificate"); |
| 89 return null; |
| 90 } |
| 91 |
| 92 if (key == null || chain == null || chain.length == 0) { |
| 93 Log.w(TAG, "Empty client certificate chain?"); |
| 94 return null; |
| 95 } |
| 96 |
| 97 // Get the encoded certificate chain. |
| 98 byte[][] encoded_chain = new byte[chain.length][]; |
| 99 try { |
| 100 for (int i = 0; i < chain.length; ++i) { |
| 101 encoded_chain[i] = chain[i].getEncoded(); |
| 102 } |
| 103 } catch (CertificateEncodingException e) { |
| 104 Log.w(TAG, "Could not retrieve encoded certificate chain: " + e); |
| 105 return null; |
| 106 } |
| 107 |
| 108 mEncodedChain = encoded_chain; |
| 109 mPrivateKey = key; |
| 110 return null; |
| 111 } |
| 112 |
| 113 @Override |
| 114 protected void onPostExecute(Void result) { |
| 115 // Back to the UI thread. |
| 116 nativeOnSystemRequestCompletion(mNativePtr, mEncodedChain, mPrivateKey); |
| 117 } |
| 118 |
| 119 |
| 120 /** |
| 121 * Create a new asynchronous request to select a client certificate. |
| 122 * |
| 123 * @param request_id The unique numerical id for the request. |
| 124 * @param key_types The list of supported key exchange types. |
| 125 * @param encoded_principals The list of CA DistinguishedNames. |
| 126 * @param host_name The server host name is available (empty otherwise). |
| 127 * @param port The server port if available (0 otherwise). |
| 128 * @return true on success. |
| 129 */ |
| 130 @CalledByNative |
| 131 static private boolean selectClientCertificate(int nativePtr, |
| 132 String[] key_types, |
| 133 byte[][] encoded_principals, |
| 134 String host_name, |
| 135 int port) { |
| 136 ThreadUtils.assertOnUiThread(); |
| 137 |
| 138 Activity activity = ActivityStatus.getActivity(); |
| 139 if (activity == null) { |
| 140 Log.w(TAG, "No active Chromium main activity!?"); |
| 141 return false; |
| 142 } |
| 143 |
| 144 // Build the list of principals from encoded versions. |
| 145 Principal[] principals = null; |
| 146 if (encoded_principals.length > 0) { |
| 147 principals = new X500Principal[encoded_principals.length]; |
| 148 try { |
| 149 for (int n = 0; n < encoded_principals.length; n++) { |
| 150 principals[n] = new X500Principal(encoded_principals[n]); |
| 151 } |
| 152 } catch (Exception e) { |
| 153 // Bail on error. |
| 154 Log.w(TAG, "Exception while decoding issuers list: " + e); |
| 155 return false; |
| 156 } |
| 157 } |
| 158 |
| 159 // All good, create new request, add it to our list and launch |
| 160 // the certificate selection activity. |
| 161 SSLClientCertificateRequest request = new SSLClientCertificateRequest(na
tivePtr); |
| 162 |
| 163 KeyChain.choosePrivateKeyAlias(activity, request, key_types, |
| 164 principals, host_name, port, null); |
| 165 return true; |
| 166 } |
| 167 |
| 168 // Called to pass request results to native side. |
| 169 private static native void nativeOnSystemRequestCompletion( |
| 170 int requestPtr, |
| 171 byte[][] certChain, |
| 172 PrivateKey privateKey); |
| 173 } |
OLD | NEW |