From 623b394c830b8a83b5c2f16624d8013b6f851cd9 Mon Sep 17 00:00:00 2001 From: Marcus Pridham Date: Fri, 16 Jan 2015 14:48:19 -0500 Subject: [PATCH] CB-8328 Allow plugins to handle certificate challenges (close #150) This is a new API for Lollipop --- .../cordova/CordovaClientCertRequest.java | 96 +++++++++++++++++++ .../src/org/apache/cordova/CordovaPlugin.java | 14 +++ .../apache/cordova/CordovaWebViewClient.java | 25 ++++- .../cordova/ICordovaClientCertRequest.java | 66 +++++++++++++ .../src/org/apache/cordova/PluginManager.java | 19 ++++ 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 framework/src/org/apache/cordova/CordovaClientCertRequest.java create mode 100644 framework/src/org/apache/cordova/ICordovaClientCertRequest.java diff --git a/framework/src/org/apache/cordova/CordovaClientCertRequest.java b/framework/src/org/apache/cordova/CordovaClientCertRequest.java new file mode 100644 index 00000000..5dd0ecae --- /dev/null +++ b/framework/src/org/apache/cordova/CordovaClientCertRequest.java @@ -0,0 +1,96 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 org.apache.cordova; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import android.webkit.ClientCertRequest; + +/** + * Implementation of the ICordovaClientCertRequest for Android WebView. + */ +public class CordovaClientCertRequest implements ICordovaClientCertRequest { + + private final ClientCertRequest request; + + public CordovaClientCertRequest(ClientCertRequest request) { + this.request = request; + } + + /** + * Cancel this request + */ + public void cancel() + { + request.cancel(); + } + + /* + * Returns the host name of the server requesting the certificate. + */ + public String getHost() + { + return request.getHost(); + } + + /* + * Returns the acceptable types of asymmetric keys (can be null). + */ + public String[] getKeyTypes() + { + return request.getKeyTypes(); + } + + /* + * Returns the port number of the server requesting the certificate. + */ + public int getPort() + { + return request.getPort(); + } + + /* + * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). + */ + public Principal[] getPrincipals() + { + return request.getPrincipals(); + } + + /* + * Ignore the request for now. Do not remember user's choice. + */ + public void ignore() + { + request.ignore(); + } + + /* + * Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests. + * + * @param privateKey The privateKey + * @param chain The certificate chain + */ + public void proceed(PrivateKey privateKey, X509Certificate[] chain) + { + request.proceed(privateKey, chain); + } +} diff --git a/framework/src/org/apache/cordova/CordovaPlugin.java b/framework/src/org/apache/cordova/CordovaPlugin.java index 0367fc63..1748407a 100644 --- a/framework/src/org/apache/cordova/CordovaPlugin.java +++ b/framework/src/org/apache/cordova/CordovaPlugin.java @@ -214,4 +214,18 @@ public class CordovaPlugin { public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) { return false; } + + /** + * Called when he system received an SSL client certificate request. Plugin can use + * the supplied ClientCertRequest to process this certificate challenge. + * + * @param view The WebView that is initiating the callback + * @param request The client certificate request + * + * @return Returns True if plugin will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) { + return false; + } } diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 2106c501..218cd81e 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -21,7 +21,6 @@ package org.apache.cordova; import java.util.Hashtable; import org.apache.cordova.CordovaInterface; - import org.apache.cordova.LOG; import org.json.JSONException; import org.json.JSONObject; @@ -33,6 +32,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.net.http.SslError; import android.view.View; +import android.webkit.ClientCertRequest; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; import android.webkit.WebView; @@ -132,6 +132,29 @@ public class CordovaWebViewClient extends WebViewClient { // By default handle 401 like we'd normally do! super.onReceivedHttpAuthRequest(view, handler, host, realm); } + + /** + * On received client cert request. + * The method forwards the request to any running plugins before using the default implementation. + * + * @param view + * @param request + */ + @Override + @TargetApi(21) + public void onReceivedClientCertRequest (WebView view, ClientCertRequest request) + { + + // Check if there is some plugin which can resolve this certificate request + PluginManager pluginManager = this.appView.pluginManager; + if (pluginManager != null && pluginManager.onReceivedClientCertRequest(this.appView, new CordovaClientCertRequest(request))) { + this.appView.loadUrlTimeout++; + return; + } + + // By default pass to WebViewClient + super.onReceivedClientCertRequest(view, request); + } /** * Notify the host application that a page has started loading. diff --git a/framework/src/org/apache/cordova/ICordovaClientCertRequest.java b/framework/src/org/apache/cordova/ICordovaClientCertRequest.java new file mode 100644 index 00000000..455d2f9d --- /dev/null +++ b/framework/src/org/apache/cordova/ICordovaClientCertRequest.java @@ -0,0 +1,66 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 org.apache.cordova; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * Specifies interface for handling certificate requests. + */ +public interface ICordovaClientCertRequest { + /** + * Cancel this request + */ + public void cancel(); + + /* + * Returns the host name of the server requesting the certificate. + */ + public String getHost(); + + /* + * Returns the acceptable types of asymmetric keys (can be null). + */ + public String[] getKeyTypes(); + + /* + * Returns the port number of the server requesting the certificate. + */ + public int getPort(); + + /* + * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). + */ + public Principal[] getPrincipals(); + + /* + * Ignore the request for now. Do not remember user's choice. + */ + public void ignore(); + + /* + * Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests. + * + * @param privateKey The privateKey + * @param chain The certificate chain + */ + public void proceed(PrivateKey privateKey, X509Certificate[] chain); +} \ No newline at end of file diff --git a/framework/src/org/apache/cordova/PluginManager.java b/framework/src/org/apache/cordova/PluginManager.java index 5bfd65bd..2fb6b2b8 100755 --- a/framework/src/org/apache/cordova/PluginManager.java +++ b/framework/src/org/apache/cordova/PluginManager.java @@ -262,6 +262,25 @@ public class PluginManager { } return false; } + + /** + * Called when he system received an SSL client certificate request. Plugin can use + * the supplied ClientCertRequest to process this certificate challenge. + * + * @param view The WebView that is initiating the callback + * @param request The client certificate request + * + * @return Returns True if plugin will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.onReceivedClientCertRequest(view, request)) { + return true; + } + } + return false; + } /** * Called when the activity will start interacting with the user.