diff --git a/plugin.xml b/plugin.xml
index dcdad4d..127bef2 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -59,19 +59,21 @@
+
-
+
+
+
-
@@ -84,4 +86,4 @@
-
\ No newline at end of file
+
diff --git a/src/android/com/silkimen/cordovahttp/CordovaClientAuth.java b/src/android/com/silkimen/cordovahttp/CordovaClientAuth.java
new file mode 100644
index 0000000..f75accd
--- /dev/null
+++ b/src/android/com/silkimen/cordovahttp/CordovaClientAuth.java
@@ -0,0 +1,75 @@
+package com.silkimen.cordovahttp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.util.Log;
+
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.KeyManager;
+
+import org.apache.cordova.CallbackContext;
+
+import com.silkimen.http.KeyChainKeyManager;
+import com.silkimen.http.TLSConfiguration;
+
+class CordovaClientAuth implements Runnable, KeyChainAliasCallback {
+ private static final String TAG = "Cordova-Plugin-HTTP";
+
+ private String mode;
+ private Activity activity;
+ private Context context;
+ private TLSConfiguration tlsConfiguration;
+ private CallbackContext callbackContext;
+
+ public CordovaClientAuth(final String mode, final Activity activity, final Context context,
+ final TLSConfiguration configContainer, final CallbackContext callbackContext) {
+
+ this.mode = mode;
+ this.activity = activity;
+ this.tlsConfiguration = configContainer;
+ this.context = context;
+ this.callbackContext = callbackContext;
+ }
+
+ @Override
+ public void run() {
+ switch (this.mode) {
+ case "systemstore":
+ KeyChain.choosePrivateKeyAlias(this.activity, this, null, null, null, -1, null);
+ break;
+ case "bundle":
+ // @todo use pfx in bundle
+ this.callbackContext.error("Not implemented, yet");
+ break;
+ default:
+ this.tlsConfiguration.setKeyManagers(null);
+ this.callbackContext.success();
+ break;
+ }
+ }
+
+ @Override
+ public void alias(final String alias) {
+ try {
+ if (alias == null) {
+ throw new Exception("Couldn't get a consent for private key access");
+ }
+
+ PrivateKey key = KeyChain.getPrivateKey(this.context, alias);
+ X509Certificate[] chain = KeyChain.getCertificateChain(this.context, alias);
+ KeyManager keyManager = new KeyChainKeyManager(alias, key, chain);
+
+ this.tlsConfiguration.setKeyManagers(new KeyManager[] { keyManager });
+
+ this.callbackContext.success();
+ } catch (Exception e) {
+ Log.e(TAG, "Couldn't load private key and certificate pair for authentication", e);
+ this.callbackContext.error("Couldn't load private key and certificate pair for authentication");
+ }
+ }
+}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
index af2bcfa..27e8a67 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
@@ -1,5 +1,6 @@
package com.silkimen.cordovahttp;
+import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.net.SocketTimeoutException;
@@ -7,15 +8,14 @@ import java.net.UnknownHostException;
import java.nio.ByteBuffer;
-import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.HttpBodyDecoder;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.HttpRequest.HttpRequestException;
import com.silkimen.http.JsonUtils;
import com.silkimen.http.OkConnectionFactory;
+import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
@@ -34,13 +34,11 @@ abstract class CordovaHttpBase implements Runnable {
protected JSONObject headers;
protected int timeout;
protected boolean followRedirects;
- protected SSLSocketFactory customSSLSocketFactory;
- protected HostnameVerifier customHostnameVerifier;
+ protected TLSConfiguration tlsConfiguration;
protected CallbackContext callbackContext;
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
- boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
- CallbackContext callbackContext) {
+ boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
this.method = method;
this.url = url;
@@ -49,22 +47,19 @@ abstract class CordovaHttpBase implements Runnable {
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
- this.customSSLSocketFactory = customSSLSocketFactory;
- this.customHostnameVerifier = customHostnameVerifier;
+ this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
- public CordovaHttpBase(String method, String url, JSONObject headers, int timeout,
- boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
- CallbackContext callbackContext) {
+ public CordovaHttpBase(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
+ TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
this.method = method;
this.url = url;
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
- this.customSSLSocketFactory = customSSLSocketFactory;
- this.customHostnameVerifier = customHostnameVerifier;
+ this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
@@ -116,20 +111,18 @@ abstract class CordovaHttpBase implements Runnable {
return new HttpRequest(this.url, this.method);
}
- protected void prepareRequest(HttpRequest request) throws JSONException {
+ protected void prepareRequest(HttpRequest request) throws JSONException, IOException {
request.followRedirects(this.followRedirects);
request.readTimeout(this.timeout);
request.acceptCharset("UTF-8");
request.uncompress(true);
request.setConnectionFactory(new OkConnectionFactory());
- if (this.customHostnameVerifier != null) {
- request.setHostnameVerifier(this.customHostnameVerifier);
+ if (this.tlsConfiguration.getHostnameVerifier() != null) {
+ request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
}
- if (this.customSSLSocketFactory != null) {
- request.setSSLSocketFactory(this.customSSLSocketFactory);
- }
+ request.setSSLSocketFactory(this.tlsConfiguration.getTLSSocketFactory());
// setup content type before applying headers, so user can override it
this.setContentType(request);
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
index 425faa9..3b30bfe 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
@@ -7,6 +7,7 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.HttpRequest;
+import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.file.FileUtils;
@@ -15,12 +16,10 @@ import org.json.JSONObject;
class CordovaHttpDownload extends CordovaHttpBase {
private String filePath;
- public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout,
- boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
- CallbackContext callbackContext) {
+ public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
+ TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
- super("GET", url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
- callbackContext);
+ super("GET", url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
this.filePath = filePath;
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java b/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
index 98c3569..2a9a34c 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
@@ -3,23 +3,21 @@ package com.silkimen.cordovahttp;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
+import com.silkimen.http.TLSConfiguration;
+
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
class CordovaHttpOperation extends CordovaHttpBase {
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
- int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
- HostnameVerifier customHostnameVerifier, CallbackContext callbackContext) {
+ int timeout, boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
- super(method, url, serializer, data, headers, timeout, followRedirects, customSSLSocketFactory,
- customHostnameVerifier, callbackContext);
+ super(method, url, serializer, data, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
- SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
- CallbackContext callbackContext) {
+ TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
- super(method, url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
- callbackContext);
+ super(method, url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
}
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
index ccf5745..47e848d 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
@@ -1,27 +1,8 @@
package com.silkimen.cordovahttp;
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import java.security.cert.CertificateFactory;
-import java.security.GeneralSecurityException;
import java.security.KeyStore;
-import java.security.KeyStore.TrustedCertificateEntry;
-import java.security.SecureRandom;
-import java.security.cert.Certificate;
-import java.util.ArrayList;
-import java.util.Enumeration;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-
-import com.silkimen.http.HostnameVerifierFactory;
-import com.silkimen.http.TLSSocketFactory;
-import com.silkimen.http.TrustManagersFactory;
+import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
@@ -31,26 +12,32 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import android.content.res.AssetManager;
import android.util.Log;
+import javax.net.ssl.TrustManagerFactory;
+
public class CordovaHttpPlugin extends CordovaPlugin {
private static final String TAG = "Cordova-Plugin-HTTP";
- private final TrustManagersFactory trustManagersFactory = new TrustManagersFactory();
- private final HostnameVerifierFactory hostnameVerifierFactory = new HostnameVerifierFactory();
-
private boolean followRedirects = true;
- private SSLSocketFactory customSSLSocketFactory;
- private HostnameVerifier customHostnameVerifier;
+ private TLSConfiguration tlsConfiguration;
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
+ this.tlsConfiguration = new TLSConfiguration();
+
try {
- this.customSSLSocketFactory = this.createSocketFactory(
- this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
+ KeyStore store = KeyStore.getInstance("AndroidCAStore");
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+
+ store.load(null);
+ tmf.init(store);
+
+ this.tlsConfiguration.setHostnameVerifier(null);
+ this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
} catch (Exception e) {
Log.e(TAG, "An error occured while loading system's CA certificates", e);
}
@@ -83,6 +70,8 @@ public class CordovaHttpPlugin extends CordovaPlugin {
return this.downloadFile(args, callbackContext);
case "setSSLCertMode":
return this.setSSLCertMode(args, callbackContext);
+ case "setClientAuthMode":
+ return this.setClientAuthMode(args, callbackContext);
case "disableRedirect":
return this.disableRedirect(args, callbackContext);
default:
@@ -98,7 +87,7 @@ public class CordovaHttpPlugin extends CordovaPlugin {
int timeout = args.getInt(2) * 1000;
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout,
- this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
+ this.followRedirects, this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(request);
@@ -115,7 +104,7 @@ public class CordovaHttpPlugin extends CordovaPlugin {
int timeout = args.getInt(4) * 1000;
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
- timeout, this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
+ timeout, this.followRedirects, this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(request);
@@ -129,8 +118,8 @@ public class CordovaHttpPlugin extends CordovaPlugin {
String uploadName = args.getString(3);
int timeout = args.getInt(4) * 1000;
- CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePath, uploadName, timeout,
- this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
+ CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePath, uploadName, timeout, this.followRedirects,
+ this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(upload);
@@ -143,42 +132,28 @@ public class CordovaHttpPlugin extends CordovaPlugin {
String filePath = args.getString(2);
int timeout = args.getInt(3) * 1000;
- CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout,
- this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
+ CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, this.followRedirects,
+ this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(download);
return true;
}
- private boolean setSSLCertMode(final JSONArray args, final CallbackContext callbackContext) {
- try {
- switch (args.getString(0)) {
- case "legacy":
- this.customHostnameVerifier = null;
- this.customSSLSocketFactory = null;
- break;
- case "nocheck":
- this.customHostnameVerifier = this.hostnameVerifierFactory.getNoOpVerifier();
- this.customSSLSocketFactory = this.createSocketFactory(this.trustManagersFactory.getNoopTrustManagers());
- break;
- case "pinned":
- this.customHostnameVerifier = null;
- this.customSSLSocketFactory = this.createSocketFactory(
- this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromBundle("www/certificates")));
- break;
- default:
- this.customHostnameVerifier = null;
- this.customSSLSocketFactory = this.createSocketFactory(
- this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
- break;
- }
+ private boolean setSSLCertMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
+ CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
+ this.tlsConfiguration, callbackContext);
- callbackContext.success();
- } catch (Exception e) {
- Log.e(TAG, "An error occured while configuring SSL cert mode", e);
- callbackContext.error("An error occured while configuring SSL cert mode");
- }
+ cordova.getThreadPool().execute(runnable);
+
+ return true;
+ }
+
+ private boolean setClientAuthMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
+ CordovaClientAuth runnable = new CordovaClientAuth(args.getString(0), this.cordova.getActivity(),
+ this.cordova.getContext(), this.tlsConfiguration, callbackContext);
+
+ cordova.getThreadPool().execute(runnable);
return true;
}
@@ -190,59 +165,4 @@ public class CordovaHttpPlugin extends CordovaPlugin {
return true;
}
-
- private ArrayList getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
- ArrayList certList = new ArrayList();
- KeyStore keyStore = KeyStore.getInstance(storeType);
- keyStore.load(null);
-
- Enumeration aliases = keyStore.aliases();
-
- while (aliases.hasMoreElements()) {
- String alias = aliases.nextElement();
- TrustedCertificateEntry certEntry = (TrustedCertificateEntry) keyStore.getEntry(alias, null);
- Certificate cert = certEntry.getTrustedCertificate();
- certList.add(cert);
- }
-
- return certList;
- }
-
- private ArrayList getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
- AssetManager assetManager = cordova.getActivity().getAssets();
- String[] files = assetManager.list(path);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- ArrayList certList = new ArrayList();
-
- for (int i = 0; i < files.length; i++) {
- int index = files[i].lastIndexOf('.');
-
- if (index == -1 || !files[i].substring(index).equals(".cer")) {
- continue;
- }
-
- certList.add(cf.generateCertificate(assetManager.open(path + "/" + files[i])));
- }
-
- return certList;
- }
-
- private SSLSocketFactory createSocketFactory(TrustManager[] trustManagers) throws IOException {
- try {
- SSLContext context = SSLContext.getInstance("TLS");
-
- /* @TODO implement custom KeyManager */
- context.init(null, trustManagers, new SecureRandom());
-
- if (android.os.Build.VERSION.SDK_INT < 20) {
- return new TLSSocketFactory(context);
- } else {
- return context.getSocketFactory();
- }
- } catch (GeneralSecurityException e) {
- IOException ioException = new IOException("Security exception occured while configuring SSL context");
- ioException.initCause(e);
- throw ioException;
- }
- }
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
index 77449dc..623e025 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
@@ -10,6 +10,8 @@ import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
+import com.silkimen.http.TLSConfiguration;
+
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
@@ -17,12 +19,10 @@ class CordovaHttpUpload extends CordovaHttpBase {
private String filePath;
private String uploadName;
- public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName,
- int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
- HostnameVerifier customHostnameVerifier, CallbackContext callbackContext) {
+ public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName, int timeout,
+ boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
- super("POST", url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
- callbackContext);
+ super("POST", url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
this.filePath = filePath;
this.uploadName = uploadName;
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaServerTrust.java b/src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
new file mode 100644
index 0000000..cfd4b49
--- /dev/null
+++ b/src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
@@ -0,0 +1,129 @@
+package com.silkimen.cordovahttp;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import com.silkimen.http.TLSConfiguration;
+
+import org.apache.cordova.CallbackContext;
+
+import android.app.Activity;
+import android.util.Log;
+import android.content.res.AssetManager;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+class CordovaServerTrust implements Runnable {
+ private static final String TAG = "Cordova-Plugin-HTTP";
+
+ private final TrustManager[] noOpTrustManagers;
+ private final HostnameVerifier noOpVerifier;
+
+ private String mode;
+ private Activity activity;
+ private TLSConfiguration tlsConfiguration;
+ private CallbackContext callbackContext;
+
+ public CordovaServerTrust(final String mode, final Activity activity, final TLSConfiguration configContainer,
+ final CallbackContext callbackContext) {
+
+ this.mode = mode;
+ this.activity = activity;
+ this.tlsConfiguration = configContainer;
+ this.callbackContext = callbackContext;
+
+ this.noOpTrustManagers = new TrustManager[] { new X509TrustManager() {
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType) {
+ // intentionally left blank
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType) {
+ // intentionally left blank
+ }
+ } };
+
+ this.noOpVerifier = new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+ }
+
+ @Override
+ public void run() {
+ try {
+ switch (this.mode) {
+ case "legacy":
+ this.tlsConfiguration.setHostnameVerifier(null);
+ this.tlsConfiguration.setTrustManagers(null);
+ break;
+ case "nocheck":
+ this.tlsConfiguration.setHostnameVerifier(this.noOpVerifier);
+ this.tlsConfiguration.setTrustManagers(this.noOpTrustManagers);
+ break;
+ case "pinned":
+ this.tlsConfiguration.setHostnameVerifier(null);
+ this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromBundle("www/certificates")));
+ break;
+ default:
+ this.tlsConfiguration.setHostnameVerifier(null);
+ this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
+ break;
+ }
+
+ callbackContext.success();
+ } catch (Exception e) {
+ Log.e(TAG, "An error occured while configuring SSL cert mode", e);
+ callbackContext.error("An error occured while configuring SSL cert mode");
+ }
+ }
+
+ private TrustManager[] getTrustManagers(KeyStore store) throws GeneralSecurityException {
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+ tmf.init(store);
+
+ return tmf.getTrustManagers();
+ }
+
+ private KeyStore getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
+ AssetManager assetManager = this.activity.getAssets();
+ String[] files = assetManager.list(path);
+
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+
+ keyStore.load(null, null);
+
+ for (int i = 0; i < files.length; i++) {
+ int index = files[i].lastIndexOf('.');
+
+ if (index == -1 || !files[i].substring(index).equals(".cer")) {
+ continue;
+ }
+
+ keyStore.setCertificateEntry("CA" + i, cf.generateCertificate(assetManager.open(path + "/" + files[i])));
+ }
+
+ return keyStore;
+ }
+
+ private KeyStore getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
+ KeyStore store = KeyStore.getInstance(storeType);
+ store.load(null);
+
+ return store;
+ }
+}
diff --git a/src/android/com/silkimen/http/HostnameVerifierFactory.java b/src/android/com/silkimen/http/HostnameVerifierFactory.java
deleted file mode 100644
index f0cf16c..0000000
--- a/src/android/com/silkimen/http/HostnameVerifierFactory.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.silkimen.http;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSession;
-
-public class HostnameVerifierFactory {
- private final HostnameVerifier noOpVerifier;
-
- public HostnameVerifierFactory() {
- this.noOpVerifier = new HostnameVerifier() {
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
- };
- }
-
- public HostnameVerifier getNoOpVerifier() {
- return this.noOpVerifier;
- }
-}
diff --git a/src/android/com/silkimen/http/KeyChainKeyManager.java b/src/android/com/silkimen/http/KeyChainKeyManager.java
new file mode 100644
index 0000000..ecdaa38
--- /dev/null
+++ b/src/android/com/silkimen/http/KeyChainKeyManager.java
@@ -0,0 +1,57 @@
+package com.silkimen.http;
+
+import android.content.Context;
+import android.security.KeyChain;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509ExtendedKeyManager;
+
+public class KeyChainKeyManager extends X509ExtendedKeyManager {
+ private final String alias;
+ private final X509Certificate[] chain;
+ private final PrivateKey key;
+
+ public KeyChainKeyManager(String alias, PrivateKey key, X509Certificate[] chain) {
+ this.alias = alias;
+ this.key = key;
+ this.chain = chain;
+ }
+
+ @Override
+ public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
+ return this.alias;
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ return chain;
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ return key;
+ }
+
+ @Override
+ public final String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final String[] getClientAliases(String keyType, Principal[] issuers) {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final String[] getServerAliases(String keyType, Principal[] issuers) {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/android/com/silkimen/http/TLSConfiguration.java b/src/android/com/silkimen/http/TLSConfiguration.java
new file mode 100644
index 0000000..33c8634
--- /dev/null
+++ b/src/android/com/silkimen/http/TLSConfiguration.java
@@ -0,0 +1,69 @@
+package com.silkimen.http;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import com.silkimen.http.TLSSocketFactory;
+
+public class TLSConfiguration {
+ private TrustManager[] trustManagers;
+ private KeyManager[] keyManagers;
+ private HostnameVerifier hostnameVerifier;
+
+ private SSLSocketFactory socketFactory;
+
+ public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
+ this.hostnameVerifier = hostnameVerifier;
+ }
+
+ public void setKeyManagers(KeyManager[] keyManagers) {
+ this.keyManagers = keyManagers;
+ this.socketFactory = null;
+ }
+
+ public void setTrustManagers(TrustManager[] trustManagers) {
+ this.trustManagers = trustManagers;
+ this.socketFactory = null;
+ }
+
+ public HostnameVerifier getHostnameVerifier() {
+ return this.hostnameVerifier;
+ }
+
+ public SSLSocketFactory getTLSSocketFactory() throws IOException {
+ if (this.socketFactory != null) {
+ return this.socketFactory;
+ }
+
+ try {
+ SSLContext context = SSLContext.getInstance("TLS");
+
+ context.init(this.keyManagers, this.trustManagers, new SecureRandom());
+
+ if (android.os.Build.VERSION.SDK_INT < 20) {
+ this.socketFactory = new TLSSocketFactory(context);
+ } else {
+ this.socketFactory = context.getSocketFactory();
+ }
+
+ return this.socketFactory;
+ } catch (GeneralSecurityException e) {
+ IOException ioException = new IOException("Security exception occured while configuring TLS context");
+ ioException.initCause(e);
+ throw ioException;
+ }
+ }
+}
diff --git a/src/android/com/silkimen/http/TrustManagersFactory.java b/src/android/com/silkimen/http/TrustManagersFactory.java
deleted file mode 100644
index ca9fa50..0000000
--- a/src/android/com/silkimen/http/TrustManagersFactory.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.silkimen.http;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-
-public class TrustManagersFactory {
- private final TrustManager[] noOpTrustManager;
-
- public TrustManagersFactory() {
- this.noOpTrustManager = new TrustManager[] { new X509TrustManager() {
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
-
- public void checkClientTrusted(X509Certificate[] chain, String authType) {
- // intentionally left blank
- }
-
- public void checkServerTrusted(X509Certificate[] chain, String authType) {
- // intentionally left blank
- }
- } };
- }
-
- public TrustManager[] getNoopTrustManagers() {
- return this.noOpTrustManager;
- }
-
- public TrustManager[] getPinnedTrustManagers(ArrayList pinnedCerts) throws IOException {
- if (pinnedCerts == null || pinnedCerts.size() == 0) {
- throw new IOException("You must add at least 1 certificate in order to pin to certificates");
- }
-
- try {
- String keyStoreType = KeyStore.getDefaultType();
- KeyStore keyStore = KeyStore.getInstance(keyStoreType);
- keyStore.load(null, null);
-
- for (int i = 0; i < pinnedCerts.size(); i++) {
- keyStore.setCertificateEntry("CA" + i, pinnedCerts.get(i));
- }
-
- // Create a TrustManager that trusts the CAs in our KeyStore
- String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
- TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
- tmf.init(keyStore);
-
- return tmf.getTrustManagers();
- } catch (GeneralSecurityException e) {
- IOException ioException = new IOException("Security exception configuring SSL trust managers");
- ioException.initCause(e);
-
- throw ioException;
- }
- }
-}
diff --git a/www/helpers.js b/www/helpers.js
index 06e82ee..14313b5 100644
--- a/www/helpers.js
+++ b/www/helpers.js
@@ -1,6 +1,7 @@
module.exports = function init(cookieHandler, messages) {
var validSerializers = ['urlencoded', 'json', 'utf8'];
var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
+ var validClientAuthModes = ['none', 'systemstore', 'bundle'];
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download'];
return {
@@ -8,6 +9,7 @@ module.exports = function init(cookieHandler, messages) {
getTypeOf: getTypeOf,
checkSerializer: checkSerializer,
checkSSLCertMode: checkSSLCertMode,
+ checkClientAuthMode: checkClientAuthMode,
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
injectCookieHandler: injectCookieHandler,
@@ -82,6 +84,10 @@ module.exports = function init(cookieHandler, messages) {
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
}
+ function checkClientAuthMode(mode) {
+ return checkForValidStringValue(validClientAuthModes, mode, messages.INVALID_CLIENT_AUTH_MODE);
+ }
+
function checkForBlacklistedHeaderKey(key) {
if (key.toLowerCase() === 'cookie') {
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
diff --git a/www/messages.js b/www/messages.js
index da37826..25a76d0 100644
--- a/www/messages.js
+++ b/www/messages.js
@@ -6,6 +6,7 @@ module.exports = {
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
+ INVALID_CLIENT_AUTH_MODE: 'advanced-http: invalid client certificate authentication mode, supported modes are:',
INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings'
diff --git a/www/public-interface.js b/www/public-interface.js
index 38fe2f2..0ee9058 100644
--- a/www/public-interface.js
+++ b/www/public-interface.js
@@ -13,6 +13,7 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
getRequestTimeout: getRequestTimeout,
setRequestTimeout: setRequestTimeout,
setSSLCertMode: setSSLCertMode,
+ setClientAuthMode: setClientAuthMode,
disableRedirect: disableRedirect,
sendRequest: sendRequest,
post: post,
@@ -92,6 +93,10 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
return exec(success, failure, 'CordovaHttpPlugin', 'setSSLCertMode', [helpers.checkSSLCertMode(mode)]);
}
+ function setClientAuthMode(mode, success, failure) {
+ return exec(success, failure, 'CordovaHttpPlugin', 'setClientAuthMode', [helpers.checkClientAuthMode(mode)]);
+ }
+
function disableRedirect(disable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [!!disable]);
}