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..5e1eb83803def1b02baf2a7acfc9550de5639edf 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,16 +31,81 @@ import org.chromium.base.JNINamespace; |
*/ |
@JNINamespace("android_webview") |
public class AwContentsClientBridge { |
+ static final String TAG = "AwContentsClientBridge"; |
+ |
+ private static final DefaultAndroidKeyStore sLocalKeyStore = |
boliu
2014/04/14 23:56:04
lazy? Make AwBrowserContext own this?
sgurun-gerrit only
2014/04/15 23:40:03
Done.
|
+ new DefaultAndroidKeyStore(); |
private AwContentsClient mClient; |
// The native peer of this object. |
private long mNativeContentsClientBridge; |
+ // Used for mocking this class in tests. |
+ protected AwContentsClientBridge() { } |
+ |
public AwContentsClientBridge(AwContentsClient client) { |
assert client != null; |
mClient = client; |
} |
+ /** |
+ * Callback to communicate clientcertificaterequest back to the AwContentsClientBridge. |
+ * The public methods should be called on UI thread. |
+ */ |
+ public class ClientCertificateRequestCallback { |
+ |
+ // We have the ownership of the native response object. |
+ long mNativePtr; |
boliu
2014/04/14 23:56:04
private?
sgurun-gerrit only
2014/04/15 23:40:03
Done.
|
+ String mHost; |
+ int mPort; |
+ |
+ public ClientCertificateRequestCallback(long nativePtr, String host, int port) { |
+ mNativePtr = nativePtr; |
boliu
2014/04/14 23:56:04
Do we leak if client ignores calblack and this obj
sgurun-gerrit only
2014/04/15 23:40:03
correct, but client will have a lot bigger problem
|
+ mHost = host; |
+ mPort = port; |
+ } |
+ |
+ public void proceed(PrivateKey privateKey, X509Certificate[] chain) { |
+ ThreadUtils.assertOnUiThread(); |
+ AndroidPrivateKey key = sLocalKeyStore.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; |
+ } |
+ ClientCertLookupTable.getInstance().allow(mHost, mPort, key, encodedChain); |
+ provideResponse(key, encodedChain); |
+ } |
+ |
+ public void ignore() { |
+ ThreadUtils.assertOnUiThread(); |
+ provideResponse(null, null); |
+ } |
+ |
+ public void cancel() { |
+ ThreadUtils.assertOnUiThread(); |
+ ClientCertLookupTable.getInstance().deny(mHost, mPort); |
+ provideResponse(null, null); |
+ } |
+ |
+ private void provideResponse(AndroidPrivateKey androidKey, byte[][] certChain) { |
+ nativeProvideClientCertificateResponse(mNativeContentsClientBridge, mNativePtr, |
+ certChain, androidKey); |
+ } |
+ } |
+ |
// Used by the native peer to set/reset a weak ref to the native peer. |
@CalledByNative |
private void setNativeContentsClientBridge(long nativeContentsClientBridge) { |
@@ -66,6 +142,46 @@ public class AwContentsClientBridge { |
nativeProceedSslError(mNativeContentsClientBridge, proceed, id); |
} |
+ // Intentionally not private for testing the native peer of this class. |
+ @CalledByNative |
+ protected boolean selectClientCertificate(final long nativePtr, final String[] keyTypes, |
+ byte[][] encodedPrincipals, final String host, final int port) { |
+ |
boliu
2014/04/14 23:56:04
nit: remove blank line
sgurun-gerrit only
2014/04/15 23:40:03
Done.
|
+ ClientCertLookupTable table = ClientCertLookupTable.getInstance(); |
+ ClientCertLookupTable.Cert cert = table.getCertData(host, port); |
boliu
2014/04/14 23:56:04
This doesn't need to be keyed by keyTypes/encodedP
sgurun-gerrit only
2014/04/15 23:40:03
Encodedprincipals are not that important since the
|
+ if (cert != null) { |
+ // we have taken the ownership of the native object |
boliu
2014/04/14 23:56:04
nit: Capitalize first word and period at end.
sgurun-gerrit only
2014/04/15 23:40:03
Done.
|
+ nativeProvideClientCertificateResponse(mNativeContentsClientBridge, nativePtr, |
+ cert.certChain, cert.privateKey); |
+ return true; |
+ } else if (table.isDenied(host, port)) { |
+ // we have taken the ownership of the native object |
+ nativeProvideClientCertificateResponse(mNativeContentsClientBridge, nativePtr, |
+ null, null); |
+ return true; |
+ } |
+ |
+ // Build the list of principals from encoded versions. |
+ Principal[] principals = null; |
+ if (encodedPrincipals.length > 0) { |
+ principals = new X500Principal[encodedPrincipals.length]; |
+ try { |
+ for (int n = 0; n < encodedPrincipals.length; n++) { |
+ principals[n] = new X500Principal(encodedPrincipals[n]); |
+ } |
+ } catch (Exception e) { |
+ Log.w(TAG, "Exception while decoding issuers list: " + e); |
+ return false; |
+ } |
+ } |
+ |
+ final ClientCertificateRequestCallback callback = |
+ new ClientCertificateRequestCallback(nativePtr, host, port); |
+ // We've taken the ownership of the native ssl request object. |
+ mClient.onReceivedClientCertRequest(callback, keyTypes, principals, host, port); |
+ return true; |
+ } |
+ |
@CalledByNative |
private void handleJsAlert(String url, String message, int id) { |
JsResultHandler handler = new JsResultHandler(this, id); |
@@ -110,6 +226,8 @@ public class AwContentsClientBridge { |
//-------------------------------------------------------------------------------------------- |
private native void nativeProceedSslError(long nativeAwContentsClientBridge, boolean proceed, |
int id); |
+ private native void nativeProvideClientCertificateResponse(long nativeAwContentsClientBridge, |
+ long nativePtr, byte[][] certChain, AndroidPrivateKey androidKey); |
private native void nativeConfirmJsResult(long nativeAwContentsClientBridge, int id, |
String prompt); |