[英]Setup AsyncHttpClient to use HTTPS
I am using com.loopj.android:android-async-http:1.4.9
for my request to server. 我正在使用
com.loopj.android:android-async-http:1.4.9
来获取我对服务器的请求。 It was working fine until I SSL/TLS is required in my server. 它工作正常,直到我的服务器中需要SSL / TLS。 So I need to modify my
AsyncHTTPClient
to use HTTPS in all URLs. 所以我需要修改我的
AsyncHTTPClient
以在所有URL中使用HTTPS。
I checked this similar how to make HTTPS calls using AsyncHttpClient? 我检查了类似如何使用AsyncHttpClient进行HTTPS调用? but did not provide clear solution to the problem.
但没有提供明确的解决方案。 The accepted solution was not secure as well because of this warning from the library itself:
由于图书馆本身的这一警告,所接受的解决方案也不安全:
Warning!
警告! This omits SSL certificate validation on every device, use with caution.
这会在每台设备上省略SSL证书验证,请谨慎使用。
So I went on and check other solutions. 所以我继续检查其他解决方案。 I ended up following the recommendation from: https://developer.android.com/training/articles/security-ssl.html .
我最终遵循以下建议: https : //developer.android.com/training/articles/security-ssl.html 。 Thus, I have something similar to this:
因此,我有类似的东西:
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Get SocketFactory from our SSLContext
//
// !!!PROBLEM IS HERE!!!
//
javax.net.ssl.SSLSocketFactory socketFactory = context.getSocketFactory();
As you can see in the last line, it gives an SSLSocketFactory
from javax.net.ssl
package. 正如您在最后一行中看到的,它从
javax.net.ssl
包中提供了一个SSLSocketFactory
。 However the AsyncHTTPClient
instance requires 但是,
AsyncHTTPClient
实例需要
asyncHTTPClient.setSSLSocketFactory(cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory)
EDIT: 编辑:
My server is using a self-signed certificate. 我的服务器正在使用自签名证书。
I assume you have a self signed certificate on your server, I dont have a code that does it using com.loopj.android:android-async-http:1.4.9, but I can give you code that uses URLConnection and which loads crt file (my.server.net.crt) from assets folder.: 我假设你的服务器上有自签名证书,我没有使用com.loopj.android:android-async-http:1.4.9的代码,但是我可以给你使用URLConnection并加载crt的代码资产文件夹中的文件(my.server.net.crt):
public static HttpsURLConnection connectSelfSignedHttps(Context ctx, String surl) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
//
InputStream caInput = new BufferedInputStream(ctx.getApplicationContext().getAssets().open("my.server.net.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
//System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL(surl);
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
return urlConnection;
}
As you can see here , setSSLFactory
requires an object of SSLFactory
, so you can create your own MySSLFactory
class. 正如你可以看到这里 ,
setSSLFactory
需要的对象SSLFactory
,这样你就可以创建自己的MySSLFactory
类。 In the below example I have renamed it to MyCustomSSLFactory
. 在下面的示例中,我将其重命名为
MyCustomSSLFactory
。 The code for SSL Validaton is in the method checkServerTrusted
of X509TrustManager
. SSL Validaton的代码位于
X509TrustManager
checkServerTrusted
方法中。 You can modify it as per your need if needed. 如果需要,您可以根据需要进行修改。
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import cz.msebera.android.httpclient.HttpVersion;
import cz.msebera.android.httpclient.conn.ClientConnectionManager;
import cz.msebera.android.httpclient.conn.scheme.PlainSocketFactory;
import cz.msebera.android.httpclient.conn.scheme.Scheme;
import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry;
import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory;
import cz.msebera.android.httpclient.impl.client.DefaultHttpClient;
import cz.msebera.android.httpclient.impl.conn.tsccm.ThreadSafeClientConnManager;
import cz.msebera.android.httpclient.params.BasicHttpParams;
import cz.msebera.android.httpclient.params.HttpParams;
import cz.msebera.android.httpclient.params.HttpProtocolParams;
import cz.msebera.android.httpclient.protocol.HTTP;
/**
* Created by prerak on 15/03/2017.
*/
public class MyCustomSSLFactory extends SSLSocketFactory {
final SSLContext sslContext = SSLContext.getInstance("TLS");
/**
* Creates a new SSL Socket Factory with the given KeyStore.
*
* @param truststore A KeyStore to create the SSL Socket Factory in context of
* @throws NoSuchAlgorithmException NoSuchAlgorithmException
* @throws KeyManagementException KeyManagementException
* @throws KeyStoreException KeyStoreException
* @throws UnrecoverableKeyException UnrecoverableKeyException
*/
public MyCustomSSLFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
chain[0].checkValidity();
} catch (Exception e) {
throw new CertificateException("Certificate not valid or trusted.");
}
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{tm}, null);
}
/**
* Gets a KeyStore containing the Certificate
*
* @param cert InputStream of the Certificate
* @return KeyStore
*/
public static KeyStore getKeystoreOfCA(InputStream cert) {
// Load CAs from an InputStream
InputStream caInput = null;
Certificate ca = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
caInput = new BufferedInputStream(cert);
ca = cf.generateCertificate(caInput);
} catch (CertificateException e1) {
e1.printStackTrace();
} finally {
try {
if (caInput != null) {
caInput.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
} catch (Exception e) {
e.printStackTrace();
}
return keyStore;
}
/**
* Gets a Default KeyStore
*
* @return KeyStore
*/
public static KeyStore getKeystore() {
KeyStore trustStore = null;
try {
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
} catch (Throwable t) {
t.printStackTrace();
}
return trustStore;
}
/**
* Returns a SSlSocketFactory which trusts all certificates
*
* @return SSLSocketFactory
*/
public static SSLSocketFactory getFixedSocketFactory() {
SSLSocketFactory socketFactory;
try {
socketFactory = new MyCustomSSLFactory(getKeystore());
socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
} catch (Throwable t) {
t.printStackTrace();
socketFactory = SSLSocketFactory.getSocketFactory();
}
return socketFactory;
}
/**
* Gets a DefaultHttpClient which trusts a set of certificates specified by the KeyStore
*
* @param keyStore custom provided KeyStore instance
* @return DefaultHttpClient
*/
public static DefaultHttpClient getNewHttpClient(KeyStore keyStore) {
try {
SSLSocketFactory sf = new MyCustomSSLFactory(keyStore);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
/**
* Makes HttpsURLConnection trusts a set of certificates specified by the KeyStore
*/
public void fixHttpsURLConnection() {
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
}
}
Now you can initialise an object of MyCustomSSLSocketFactory
by passing your custom KeyStore
to it. 现在,您可以通过将自定义
KeyStore
传递给它来初始化MyCustomSSLSocketFactory
的对象。
MyCustomSSLFactory socketFactory = new MyCustomSSLFactory(keyStore);
And now you can set the socket factory as: 现在您可以将套接字工厂设置为:
asyncHTTPClient.setSSLSocketFactory(socketFactory);
you can use @Prerak's solution. 你可以使用@ Prerak的解决方案。 Looks like that solution works.
看起来该解决方案有效。 This is my own version.
这是我自己的版本。 It is similar to @Prerak's but a shorter constructor:
它与@ Prerak类似,但是构造函数更短:
public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(truststore);
// Create an SSLContext that uses our TrustManager
sslContext.init(null, tmf.getTrustManagers(), null);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.