Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a1477594 authored by Brian Carlstrom's avatar Brian Carlstrom
Browse files

Implement android.webkit.BrowserFrame.requestClientCert

Following the example of reportSslCertError, implement requestClientCert

ERROR CASE                                              CLIENT CERT CASE
<... From external/webkit ...>                          <... From external/webkit ...>
android.webkit.BrowserFrame.reportSslCertError          android.webkit.BrowserFrame.requestClientCert
CallbackProxy.onReceivedSslError                        CallbackProxy.onReceivedClientCertRequest
WebViewClient.onReceivedSslError                        WebViewClient.onReceivedClientCertRequest
<... See packages/apps/Browser ...>                     <... See packages/apps/Browser ...>
SslErrorHandler.proceed (with SslCertLookupTable)       ClientCertRequestHandler.proceed (with SslClientCertLookupTable)
android.webkit.BrowserFrame.nativeSslCertErrorProceed   android.webkit.BrowserFrame.nativeSslClientCert
<... To external/webkit ...>                            <... To external/webkit ...>

Change-Id: I2ba6007ad9b2ee520a0a6b17f3a767679b1664de
parent 6276814a
Loading
Loading
Loading
Loading
+34 −1
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
import java.nio.charset.Charsets;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1141,7 +1144,7 @@ class BrowserFrame extends Handler {
    }

    /**
     * Called by JNI when the native HTTP(S) stack gets an invalid cert chain.
     * Called by JNI when the native HTTPS stack gets an invalid cert chain.
     *
     * We delegate the request to CallbackProxy, and route its response to
     * {@link #nativeSslCertErrorProceed(int)} or
@@ -1181,6 +1184,32 @@ class BrowserFrame extends Handler {
        }
    }

    /**
     * Called by JNI when the native HTTPS stack gets a client
     * certificate request.
     *
     * We delegate the request to CallbackProxy, and route its response to
     * {@link #nativeSslClientCert(int, X509Certificate)}.
     */
    private void requestClientCert(int handle, byte[] host_and_port_bytes) {
        String host_and_port = new String(host_and_port_bytes, Charsets.UTF_8);
        SslClientCertLookupTable table = SslClientCertLookupTable.getInstance();
        if (table.IsAllowed(host_and_port)) {
            // previously allowed
            nativeSslClientCert(handle,
                                table.PrivateKey(host_and_port),
                                table.CertificateChain(host_and_port));
        } else if (table.IsDenied(host_and_port)) {
            // previously denied
            nativeSslClientCert(handle, null, null);
        } else {
            // previously ignored or new
            mCallbackProxy.onReceivedClientCertRequest(
                    new ClientCertRequestHandler(this, handle, host_and_port, table),
                    host_and_port);
        }
    }

    /**
     * Called by JNI when the native HTTP stack needs to download a file.
     *
@@ -1366,4 +1395,8 @@ class BrowserFrame extends Handler {

    private native void nativeSslCertErrorProceed(int handle);
    private native void nativeSslCertErrorCancel(int handle, int cert_error);

    native void nativeSslClientCert(int handle,
                                    byte[] pkcs8EncodedPrivateKey,
                                    byte[][] asn1DerEncodedCertificateChain);
}
+34 −7
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ class CallbackProxy extends Handler {
    private static final int SET_INSTALLABLE_WEBAPP              = 138;
    private static final int NOTIFY_SEARCHBOX_LISTENERS          = 139;
    private static final int AUTO_LOGIN                          = 140;
    private static final int CLIENT_CERT_REQUEST                 = 141;

    // Message triggered by the client to resume execution
    private static final int NOTIFY                              = 200;
@@ -348,6 +349,16 @@ class CallbackProxy extends Handler {
                }
                break;

            case CLIENT_CERT_REQUEST:
                if (mWebViewClient != null) {
                    HashMap<String, Object> map =
                        (HashMap<String, Object>) msg.obj;
                    mWebViewClient.onReceivedClientCertRequest(mWebView,
                            (ClientCertRequestHandler) map.get("handler"),
                            (String) map.get("host_and_port"));
                }
                break;

            case PROGRESS:
                // Synchronize to ensure mLatestProgress is not modified after
                // setProgress is called and before mProgressUpdatePending is
@@ -998,13 +1009,29 @@ class CallbackProxy extends Handler {
            return;
        }
        Message msg = obtainMessage(SSL_ERROR);
        //, handler);
        HashMap<String, Object> map = new HashMap();
        map.put("handler", handler);
        map.put("error", error);
        msg.obj = map;
        sendMessage(msg);
    }
    /**
     * @hide
     */
    public void onReceivedClientCertRequest(ClientCertRequestHandler handler, String host_and_port) {
        // Do an unsynchronized quick check to avoid posting if no callback has
        // been set.
        if (mWebViewClient == null) {
            handler.cancel();
            return;
        }
        Message msg = obtainMessage(CLIENT_CERT_REQUEST);
        HashMap<String, Object> map = new HashMap();
        map.put("handler", handler);
        map.put("host_and_port", host_and_port);
        msg.obj = map;
        sendMessage(msg);
    }
    /**
     * @hide - hide this because it contains a parameter of type SslCertificate,
     * which is located in a hidden package.
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.webkit;

import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import org.apache.harmony.xnet.provider.jsse.NativeCrypto;

/**
 * ClientCertRequestHandler: class responsible for handling client
 * certificate requests.  This class is passed as a parameter to
 * BrowserCallback.displayClientCertRequestDialog and is meant to
 * receive the user's response.
 *
 * @hide
 */
public final class ClientCertRequestHandler {

    private final BrowserFrame mBrowserFrame;
    private final int mHandle;
    private final String mHostAndPort;
    private final SslClientCertLookupTable mTable;
    ClientCertRequestHandler(BrowserFrame browserFrame,
                             int handle,
                             String host_and_port,
                             SslClientCertLookupTable table) {
        mBrowserFrame = browserFrame;
        mHandle = handle;
        mHostAndPort = host_and_port;
        mTable = table;
    }

    /**
     * Proceed with the specified private key and client certificate chain.
     */
    public void proceed(PrivateKey privateKey, X509Certificate[] chain) {
        byte[] privateKeyBytes = privateKey.getEncoded();
        byte[][] chainBytes;
        try {
            chainBytes = NativeCrypto.encodeCertificates(chain);
        } catch (CertificateEncodingException e) {
            mBrowserFrame.nativeSslClientCert(mHandle, null, null);
            return;
        }
        mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes);
        mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes);
    }

    /**
     * Igore the request for now, the user may be prompted again.
     */
    public void ignore() {
        mBrowserFrame.nativeSslClientCert(mHandle, null, null);
    }

    /**
     * Cancel this request, remember the users negative choice.
     */
    public void cancel() {
        mTable.Deny(mHostAndPort);
        mBrowserFrame.nativeSslClientCert(mHandle, null, null);
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -19,11 +19,11 @@ package android.webkit;
import android.os.Bundle;
import android.net.http.SslError;

/*
/**
 * A simple class to store the wrong certificates that user is aware but
 * chose to proceed.
 */
class SslCertLookupTable {
final class SslCertLookupTable {
    private static SslCertLookupTable sTable;
    private final Bundle table;

+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.webkit;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * A simple class to store client certificates that user has chosen.
 */
final class SslClientCertLookupTable {
    private static SslClientCertLookupTable sTable;
    private final Map<String, byte[]> privateKeys;
    private final Map<String, byte[][]> certificateChains;
    private final Set<String> denied;

    public static synchronized SslClientCertLookupTable getInstance() {
        if (sTable == null) {
            sTable = new SslClientCertLookupTable();
        }
        return sTable;
    }

    private SslClientCertLookupTable() {
        privateKeys = new HashMap<String, byte[]>();
        certificateChains = new HashMap<String, byte[][]>();
        denied = new HashSet<String>();
    }

    public void Allow(String host_and_port, byte[] privateKey, byte[][] chain) {
        privateKeys.put(host_and_port, privateKey);
        certificateChains.put(host_and_port, chain);
        denied.remove(host_and_port);
    }

    public void Deny(String host_and_port) {
        privateKeys.remove(host_and_port);
        certificateChains.remove(host_and_port);
        denied.add(host_and_port);
    }

    public boolean IsAllowed(String host_and_port) {
        return privateKeys.containsKey(host_and_port);
    }

    public boolean IsDenied(String host_and_port) {
        return denied.contains(host_and_port);
    }

    public byte[] PrivateKey(String host_and_port) {
        return privateKeys.get(host_and_port);
    }

    public byte[][] CertificateChain(String host_and_port) {
        return certificateChains.get(host_and_port);
    }
}
Loading