2017-03-15 93 views
4

我正在使用com.loopj.android:android-async-http:1.4.9作为我对服务器的请求。它工作正常,直到我的服务器需要SSL/TLS。所以我需要修改我的AsyncHTTPClient以在所有网址中使用HTTPS。安装AsyncHttpClient以使用HTTPS

我检查了这个相似的how to make HTTPS calls using AsyncHttpClient?,但没有提供明确的解决方案。由于库本身的这种警告,接受的解决方案也不安全:

警告!这忽略了每个设备上的SSL证书验证,请谨慎使用 。

因此,我继续检查其他解决方案。我结束了以下推荐:https://developer.android.com/training/articles/security-ssl.html。因此,我有一些与此类似:

// 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(); 

正如你可以在最后一行看到,它提供了从javax.net.ssl包的SSLSocketFactory。然而,AsyncHTTPClient实例需要

asyncHTTPClient.setSSLSocketFactory(cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory)

编辑:

我的服务器使用的是自签名证书。

+0

您是否找到了一个好的解决方案?我有点失落 –

+0

看起来像@ Prera​​k的解决方案下面的作品。我也会添加我自己的版本。谢谢。 – user1506104

回答

1

正如你可以看到heresetSSLFactory需要SSLFactory的对象,所以你可以创建自己的MySSLFactory类。在下面的例子中,我将它重命名为MyCustomSSLFactorySSL Validaton的代码位于X509TrustManager的方法checkServerTrusted中。如果需要,您可以根据需要对其进行修改。现在

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()); 
    } 
} 

你可以通过自定义KeyStore它初始化的MyCustomSSLSocketFactory的对象。

MyCustomSSLFactory socketFactory = new MyCustomSSLFactory(keyStore); 

现在你可以设置套接字工厂为:

asyncHTTPClient.setSSLSocketFactory(socketFactory); 
+0

嘿@Prera​​k Sola,我也是这样做的。它的工作,但***的问题“警告!这省略了每个设备上的SSL证书验证,请谨慎使用“当我们使用'com.loopj.android.http'中的'MySSLSocketFactory'类时,***会保留在这个验证码上。 – user1506104

+0

@ user1506104:检查更新后的答案。 –

+0

嗨@Prera​​k,你能告诉我你的代码和com.loopj.android.http.MySSLSocketFactory之间的区别是什么?如果你想跟随这个链接http://stackoverflow.com/questions/21833804/how-to-make- https-calls-using-asynchttpclient,这将导致'警告!这忽略了每个设备上的SSL证书验证,谨慎使用.' – user1506104

3

我假设你在你的服务器上有一个自签名证书,我没有使用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; 
    } 
+0

是的,我正在使用自签名证书。你的代码与我的相似。我正在寻找一个使用com.loopj.android:android-async-http的答案。感谢您分享您的解决方案。 =) – user1506104

0

你可以使用@ Prera​​k的解决方案。看起来像解决方案的作品。这是我自己的版本。它类似于@Prakrak's,但是更短的构造函数:

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); 
}