mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-01-31 00:00:03 +08:00
WIP: started re-implementing SSL cert modes
This commit is contained in:
@@ -65,6 +65,8 @@
|
||||
<source-file src="src/android/com/silkimen/http/HttpBodyDecoder.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/JsonUtils.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TrustManagersFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:3.10.0"/>
|
||||
</platform>
|
||||
<platform name="browser">
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Certificate> getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
|
||||
ArrayList<Certificate> certList = new ArrayList<Certificate>();
|
||||
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<String> aliases = ks.aliases();
|
||||
Enumeration<String> 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<Certificate> getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] files = assetManager.list("www/certificates");
|
||||
ArrayList<String> cerFiles = new ArrayList<String>();
|
||||
String[] files = assetManager.list(path);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
ArrayList<Certificate> certList = new ArrayList<Certificate>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user