简体   繁体   English

设置AsyncHttpClient以使用HTTPS

[英]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. 你可以使用@ Prera​​k的解决方案。 Looks like that solution works. 看起来该解决方案有效。 This is my own version. 这是我自己的版本。 It is similar to @Prerak's but a shorter constructor: 它与@ Prera​​k类似,但是构造函数更短:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM