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

Commit d496da46 authored by Brian Carlstrom's avatar Brian Carlstrom Committed by Android (Google) Code Review
Browse files

Merge "Implement android.webkit.BrowserFrame.requestClientCert"

parents 599b1029 a1477594
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