diff --git a/plugin.xml b/plugin.xml
index 21b964e..96d0379 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -65,6 +65,8 @@
+
+
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
index badba56..87d08d0 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
@@ -8,6 +8,7 @@ import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.HttpBodyDecoder;
import com.silkimen.http.HttpRequest;
@@ -32,9 +33,10 @@ abstract class CordovaHttpBase implements Runnable {
protected JSONObject headers;
protected int timeout;
protected boolean followRedirects;
+ protected SSLSocketFactory customSSLSocketFactory;
protected CallbackContext callbackContext;
- public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout, boolean followRedirects,
+ public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
CallbackContext callbackContext) {
this.method = method;
@@ -44,10 +46,12 @@ abstract class CordovaHttpBase implements Runnable {
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
+ this.customSSLSocketFactory = customSSLSocketFactory;
this.callbackContext = callbackContext;
}
- public CordovaHttpBase(String method, String url, JSONObject params, JSONObject headers, int timeout, boolean followRedirects,
+
+ public CordovaHttpBase(String method, String url, JSONObject params, JSONObject headers, int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
CallbackContext callbackContext) {
this.method = method;
@@ -56,6 +60,7 @@ abstract class CordovaHttpBase implements Runnable {
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
+ this.customSSLSocketFactory = customSSLSocketFactory;
this.callbackContext = callbackContext;
}
@@ -116,6 +121,10 @@ abstract class CordovaHttpBase implements Runnable {
request.acceptCharset("UTF-8");
request.uncompress(true);
+ if (this.customSSLSocketFactory != null) {
+ request.setSSLSocketFactory(this.customSSLSocketFactory);
+ }
+
// 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 d17face..60c830e 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
@@ -3,6 +3,8 @@ package com.silkimen.cordovahttp;
import java.io.File;
import java.net.URI;
+import javax.net.ssl.SSLSocketFactory;
+
import com.silkimen.http.HttpRequest;
import org.apache.cordova.CallbackContext;
@@ -13,9 +15,9 @@ class CordovaHttpDownload extends CordovaHttpBase {
private String filePath;
public CordovaHttpDownload(String url, JSONObject params, JSONObject headers, String filePath, int timeout,
- boolean followRedirects, CallbackContext callbackContext) {
+ boolean followRedirects, SSLSocketFactory customSSLSocketFactory, CallbackContext callbackContext) {
- super("GET", url, params, headers, timeout, followRedirects, callbackContext);
+ super("GET", url, params, headers, timeout, followRedirects, customSSLSocketFactory, callbackContext);
this.filePath = filePath;
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java b/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
index 3e9fe82..a40edf1 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
@@ -1,18 +1,20 @@
package com.silkimen.cordovahttp;
+import javax.net.ssl.SSLSocketFactory;
+
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, CallbackContext callbackContext) {
+ int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory, CallbackContext callbackContext) {
- super(method, url, serializer, data, headers, timeout, followRedirects, callbackContext);
+ super(method, url, serializer, data, headers, timeout, followRedirects, customSSLSocketFactory, callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject params, JSONObject headers, int timeout,
- boolean followRedirects, CallbackContext callbackContext) {
+ boolean followRedirects, SSLSocketFactory customSSLSocketFactory, CallbackContext callbackContext) {
- super(method, url, params, headers, timeout, followRedirects, callbackContext);
+ super(method, url, params, headers, timeout, followRedirects, customSSLSocketFactory, callbackContext);
}
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
index 668ac09..8706181 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
@@ -4,38 +4,50 @@ 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.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import com.silkimen.http.TLSSocketFactory;
+import com.silkimen.http.TrustManagersFactory;
+
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import android.util.Log;
import android.content.res.AssetManager;
+import android.util.Log;
public class CordovaHttpPlugin extends CordovaPlugin {
private static final String TAG = "Cordova-Plugin-HTTP";
- private static boolean followRedirects = true;
+ private boolean followRedirects = true;
+ private TrustManagersFactory trustManagersFactory;
+ private SSLSocketFactory customSSLSocketFactory;
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
+ this.trustManagersFactory = new TrustManagersFactory();
+
try {
- // HttpRequest.clearCerts();
- this.pinSSLCertsFromCAStore();
+ this.customSSLSocketFactory = this.createSocketFactory(
+ this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
} catch (Exception e) {
Log.e(TAG, "An error occured while loading system's CA certificates", e);
}
@@ -84,7 +96,7 @@ public class CordovaHttpPlugin extends CordovaPlugin {
int timeout = args.getInt(3) * 1000;
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, params, headers, timeout,
- followRedirects, callbackContext);
+ this.followRedirects, this.customSSLSocketFactory, callbackContext);
cordova.getThreadPool().execute(request);
@@ -101,7 +113,7 @@ public class CordovaHttpPlugin extends CordovaPlugin {
int timeout = args.getInt(4) * 1000;
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
- timeout, followRedirects, callbackContext);
+ timeout, this.followRedirects, this.customSSLSocketFactory, callbackContext);
cordova.getThreadPool().execute(request);
@@ -117,7 +129,7 @@ public class CordovaHttpPlugin extends CordovaPlugin {
int timeout = args.getInt(5) * 1000;
CordovaHttpUpload upload = new CordovaHttpUpload(url, params, headers, filePath, uploadName, timeout,
- followRedirects, callbackContext);
+ this.followRedirects, this.customSSLSocketFactory, callbackContext);
cordova.getThreadPool().execute(upload);
@@ -131,91 +143,101 @@ public class CordovaHttpPlugin extends CordovaPlugin {
String filePath = args.getString(3);
int timeout = args.getInt(4) * 1000;
- CordovaHttpDownload download = new CordovaHttpDownload(url, params, headers, filePath, timeout, followRedirects,
- callbackContext);
+ CordovaHttpDownload download = new CordovaHttpDownload(url, params, headers, filePath, timeout,
+ this.followRedirects, this.customSSLSocketFactory, callbackContext);
cordova.getThreadPool().execute(download);
return true;
}
- private boolean setSSLCertMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
- String mode = args.getString(0);
-
- // HttpRequest.clearCerts();
-
- if (mode.equals("legacy")) {
- // HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_DEFAULT);
- callbackContext.success();
- } else if (mode.equals("nocheck")) {
- // HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_TRUSTALL);
- callbackContext.success();
- } else if (mode.equals("pinned")) {
- try {
- this.loadSSLCertsFromBundle();
- // HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_PINNED);
- callbackContext.success();
- } catch (Exception e) {
- e.printStackTrace();
- callbackContext.error("There was an error setting up ssl pinning");
- }
- } else if (mode.equals("default")) {
- try {
- this.pinSSLCertsFromCAStore();
- callbackContext.success();
- } catch (Exception e) {
- e.printStackTrace();
- callbackContext.error("There was an error loading system's CA certificates");
+ private boolean setSSLCertMode(final JSONArray args, final CallbackContext callbackContext) {
+ try {
+ switch (args.getString(0)) {
+ case "legacy":
+ this.customSSLSocketFactory = null;
+ break;
+ case "nocheck":
+ /* @TODO host name verification */
+ this.customSSLSocketFactory = this.createSocketFactory(this.trustManagersFactory.getNoopTrustManagers());
+ break;
+ case "pinned":
+ this.customSSLSocketFactory = this.createSocketFactory(
+ this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromBundle("www/certificates/")));
+ break;
+ default:
+ this.customSSLSocketFactory = this.createSocketFactory(
+ this.trustManagersFactory.getPinnedTrustManagers(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");
}
return true;
}
private boolean disableRedirect(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
- followRedirects = !args.getBoolean(0);
+ this.followRedirects = !args.getBoolean(0);
callbackContext.success();
return true;
}
- private void pinSSLCertsFromCAStore() throws GeneralSecurityException, IOException {
- this.loadSSLCertsFromKeyStore("AndroidCAStore");
- // HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_PINNED);
- }
+ private ArrayList getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
+ ArrayList certList = new ArrayList();
+ KeyStore keyStore = KeyStore.getInstance(storeType);
+ keyStore.load(null);
- private void loadSSLCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
- KeyStore ks = KeyStore.getInstance(storeType);
- ks.load(null);
- Enumeration aliases = ks.aliases();
+ Enumeration aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
- TrustedCertificateEntry certEntry = (TrustedCertificateEntry) ks.getEntry(alias, null);
+ TrustedCertificateEntry certEntry = (TrustedCertificateEntry) keyStore.getEntry(alias, null);
Certificate cert = certEntry.getTrustedCertificate();
- // HttpRequest.addCert(cert);
+ certList.add(cert);
}
+
+ return certList;
}
- private void loadSSLCertsFromBundle() throws GeneralSecurityException, IOException {
+ private ArrayList getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
AssetManager assetManager = cordova.getActivity().getAssets();
- String[] files = assetManager.list("www/certificates");
- ArrayList cerFiles = new ArrayList();
+ 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) {
- if (files[i].substring(index).equals(".cer")) {
- cerFiles.add("www/certificates/" + files[i]);
- }
+
+ if (index == -1 || !files[i].substring(index).equals(".cer")) {
+ continue;
}
+
+ certList.add(cf.generateCertificate(assetManager.open(path + files[i])));
}
- for (int i = 0; i < cerFiles.size(); i++) {
- InputStream in = cordova.getActivity().getAssets().open(cerFiles.get(i));
- InputStream caInput = new BufferedInputStream(in);
- // HttpRequest.addCert(caInput);
+ return certList;
+ }
+
+ private SSLSocketFactory createSocketFactory(TrustManager[] trustManagers) throws IOException {
+ try {
+ SSLContext context = SSLContext.getInstance("TLS");
+ 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 c6b6b80..fff3390 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
@@ -7,6 +7,8 @@ import com.silkimen.http.HttpRequest;
import java.io.File;
import java.net.URI;
+import javax.net.ssl.SSLSocketFactory;
+
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
@@ -15,9 +17,9 @@ class CordovaHttpUpload extends CordovaHttpBase {
private String uploadName;
public CordovaHttpUpload(String url, JSONObject params, JSONObject headers, String filePath, String uploadName,
- int timeout, boolean followRedirects, CallbackContext callbackContext) {
+ int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory, CallbackContext callbackContext) {
- super("POST", url, params, headers, timeout, followRedirects, callbackContext);
+ super("POST", url, params, headers, timeout, followRedirects, customSSLSocketFactory, callbackContext);
this.filePath = filePath;
this.uploadName = uploadName;
}
diff --git a/src/android/com/silkimen/http/TrustManagersFactory.java b/src/android/com/silkimen/http/TrustManagersFactory.java
index 943abde..ca9fa50 100644
--- a/src/android/com/silkimen/http/TrustManagersFactory.java
+++ b/src/android/com/silkimen/http/TrustManagersFactory.java
@@ -1,9 +1,13 @@
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;
@@ -17,11 +21,11 @@ public class TrustManagersFactory {
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
- // Intentionally left blank
+ // intentionally left blank
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
- // Intentionally left blank
+ // intentionally left blank
}
} };
}
@@ -53,7 +57,8 @@ public class TrustManagersFactory {
} catch (GeneralSecurityException e) {
IOException ioException = new IOException("Security exception configuring SSL trust managers");
ioException.initCause(e);
- throw new HttpRequestException(ioException);
+
+ throw ioException;
}
}
}