2017-03-15 128 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); 
}