| Index: android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
|
| diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
|
| index 66e4f1ec9522047bf983c2461f4f653f47352712..6ebfe8a2e48e77d2feca1935ec0e0452e3927968 100644
|
| --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
|
| +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
|
| @@ -6,10 +6,21 @@ package org.chromium.android_webview;
|
|
|
| import android.net.http.SslCertificate;
|
| import android.net.http.SslError;
|
| +import android.util.Log;
|
| import android.webkit.ValueCallback;
|
|
|
| import org.chromium.base.CalledByNative;
|
| import org.chromium.base.JNINamespace;
|
| +import org.chromium.base.ThreadUtils;
|
| +import org.chromium.net.AndroidPrivateKey;
|
| +import org.chromium.net.DefaultAndroidKeyStore;
|
| +
|
| +import java.security.Principal;
|
| +import java.security.PrivateKey;
|
| +import java.security.cert.CertificateEncodingException;
|
| +import java.security.cert.X509Certificate;
|
| +
|
| +import javax.security.auth.x500.X500Principal;
|
|
|
| /**
|
| * This class handles the JNI communication logic for the the AwContentsClient class.
|
| @@ -20,14 +31,99 @@ import org.chromium.base.JNINamespace;
|
| */
|
| @JNINamespace("android_webview")
|
| public class AwContentsClientBridge {
|
| + static final String TAG = "AwContentsClientBridge";
|
|
|
| private AwContentsClient mClient;
|
| // The native peer of this object.
|
| private long mNativeContentsClientBridge;
|
|
|
| - public AwContentsClientBridge(AwContentsClient client) {
|
| + private DefaultAndroidKeyStore mLocalKeyStore;
|
| +
|
| + private ClientCertLookupTable mLookupTable;
|
| +
|
| + // Used for mocking this class in tests.
|
| + protected AwContentsClientBridge(DefaultAndroidKeyStore keyStore,
|
| + ClientCertLookupTable table) {
|
| + mLocalKeyStore = keyStore;
|
| + mLookupTable = table;
|
| + }
|
| +
|
| + public AwContentsClientBridge(AwContentsClient client, DefaultAndroidKeyStore keyStore,
|
| + ClientCertLookupTable table) {
|
| assert client != null;
|
| mClient = client;
|
| + mLocalKeyStore = keyStore;
|
| + mLookupTable = table;
|
| + }
|
| +
|
| + /**
|
| + * Callback to communicate clientcertificaterequest back to the AwContentsClientBridge.
|
| + * The public methods should be called on UI thread.
|
| + * A request can not be proceeded, ignored or canceled more than once. Doing this
|
| + * is a programming error and causes an exception.
|
| + */
|
| + public class ClientCertificateRequestCallback {
|
| +
|
| + private int mId;
|
| + private String mHost;
|
| + private int mPort;
|
| + private boolean mIsCalled;
|
| +
|
| + public ClientCertificateRequestCallback(int id, String host, int port) {
|
| + mId = id;
|
| + mHost = host;
|
| + mPort = port;
|
| + }
|
| +
|
| + public void proceed(PrivateKey privateKey, X509Certificate[] chain) {
|
| + ThreadUtils.assertOnUiThread();
|
| + checkIfCalled();
|
| +
|
| + AndroidPrivateKey key = mLocalKeyStore.createKey(privateKey);
|
| +
|
| + if (key == null || chain == null || chain.length == 0) {
|
| + Log.w(TAG, "Empty client certificate chain?");
|
| + provideResponse(null, null);
|
| + return;
|
| + }
|
| + // Encode the certificate chain.
|
| + byte[][] encodedChain = new byte[chain.length][];
|
| + try {
|
| + for (int i = 0; i < chain.length; ++i) {
|
| + encodedChain[i] = chain[i].getEncoded();
|
| + }
|
| + } catch (CertificateEncodingException e) {
|
| + Log.w(TAG, "Could not retrieve encoded certificate chain: " + e);
|
| + provideResponse(null, null);
|
| + return;
|
| + }
|
| + mLookupTable.allow(mHost, mPort, key, encodedChain);
|
| + provideResponse(key, encodedChain);
|
| + }
|
| +
|
| + public void ignore() {
|
| + ThreadUtils.assertOnUiThread();
|
| + checkIfCalled();
|
| + provideResponse(null, null);
|
| + }
|
| +
|
| + public void cancel() {
|
| + ThreadUtils.assertOnUiThread();
|
| + mLookupTable.deny(mHost, mPort);
|
| + provideResponse(null, null);
|
| + }
|
| +
|
| + public void checkIfCalled() {
|
| + if (mIsCalled) {
|
| + throw new IllegalStateException("The callback was already called.");
|
| + }
|
| + mIsCalled = true;
|
| + }
|
| +
|
| + private void provideResponse(AndroidPrivateKey androidKey, byte[][] certChain) {
|
| + nativeProvideClientCertificateResponse(mNativeContentsClientBridge, mId,
|
| + certChain, androidKey);
|
| + }
|
| }
|
|
|
| // Used by the native peer to set/reset a weak ref to the native peer.
|
| @@ -66,6 +162,43 @@ public class AwContentsClientBridge {
|
| nativeProceedSslError(mNativeContentsClientBridge, proceed, id);
|
| }
|
|
|
| + // Intentionally not private for testing the native peer of this class.
|
| + @CalledByNative
|
| + protected void selectClientCertificate(final int id, final String[] keyTypes,
|
| + byte[][] encodedPrincipals, final String host, final int port) {
|
| + ClientCertLookupTable.Cert cert = mLookupTable.getCertData(host, port);
|
| + if (mLookupTable.isDenied(host, port)) {
|
| + nativeProvideClientCertificateResponse(mNativeContentsClientBridge, id,
|
| + null, null);
|
| + return;
|
| + }
|
| + if (cert != null) {
|
| + nativeProvideClientCertificateResponse(mNativeContentsClientBridge, id,
|
| + cert.certChain, cert.privateKey);
|
| + return;
|
| + }
|
| + // Build the list of principals from encoded versions.
|
| + Principal[] principals = null;
|
| + if (encodedPrincipals.length > 0) {
|
| + principals = new X500Principal[encodedPrincipals.length];
|
| + for (int n = 0; n < encodedPrincipals.length; n++) {
|
| + try {
|
| + principals[n] = new X500Principal(encodedPrincipals[n]);
|
| + } catch (IllegalArgumentException e) {
|
| + Log.w(TAG, "Exception while decoding issuers list: " + e);
|
| + nativeProvideClientCertificateResponse(mNativeContentsClientBridge, id,
|
| + null, null);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + }
|
| +
|
| + final ClientCertificateRequestCallback callback =
|
| + new ClientCertificateRequestCallback(id, host, port);
|
| + mClient.onReceivedClientCertRequest(callback, keyTypes, principals, host, port);
|
| + }
|
| +
|
| @CalledByNative
|
| private void handleJsAlert(String url, String message, int id) {
|
| JsResultHandler handler = new JsResultHandler(this, id);
|
| @@ -110,6 +243,8 @@ public class AwContentsClientBridge {
|
| //--------------------------------------------------------------------------------------------
|
| private native void nativeProceedSslError(long nativeAwContentsClientBridge, boolean proceed,
|
| int id);
|
| + private native void nativeProvideClientCertificateResponse(long nativeAwContentsClientBridge,
|
| + int id, byte[][] certChain, AndroidPrivateKey androidKey);
|
|
|
| private native void nativeConfirmJsResult(long nativeAwContentsClientBridge, int id,
|
| String prompt);
|
|
|