Setup AsyncHttpClient pour utiliser HTTPS
j'utilise com.loopj.android:android-async-http:1.4.9
pour ma requête au serveur. Il fonctionnait bien jusqu'à ce que SSL/TLS soit requis dans mon serveur. Je dois donc modifier mon AsyncHTTPClient
pour utiliser HTTPS dans toutes les URLs.
j'ai coché ce similaire comment faire des appels HTTPS en utilisant AsyncHttpClient? mais n'a pas fourni de solution claire au problème. La solution acceptée n'était pas aussi sûre à cause de cet avertissement de la bibliothèque elle-même:
Attention! Cela omet la validation du certificat SSL sur chaque périphérique, utilisez avec prudence.
donc j'ai continué et j'ai vérifié d'autres solutions. J'ai fini par suivre la recommandation de: https://developer.android.com/training/articles/security-ssl.html . Ainsi, j'ai quelque chose de semblable à ceci:
// 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();
comme vous pouvez le voir dans la dernière ligne, il donne un paquet SSLSocketFactory
de javax.net.ssl
. Cependant l'instance AsyncHTTPClient
exige
asyncHTTPClient.setSSLSocketFactory(cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory)
EDIT:
Mon serveur utilise un certificat auto-signé.
3 réponses
Comme vous pouvez le voir ici , setSSLFactory
nécessite un objet de SSLFactory
, de sorte que vous pouvez créer votre propre MySSLFactory
de la classe. Dans l'exemple ci-dessous j'ai renommé MyCustomSSLFactory
. Le code pour SSL Validaton est dans la méthode checkServerTrusted
de X509TrustManager
. Vous pouvez le modifier selon vos besoins si nécessaire.
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());
}
}
Maintenant, vous pouvez initialiser un objet de MyCustomSSLSocketFactory
en passant votre personnalisé KeyStore
.
MyCustomSSLFactory socketFactory = new MyCustomSSLFactory(keyStore);
et maintenant vous pouvez définir l'usine de douilles comme:
asyncHTTPClient.setSSLSocketFactory(socketFactory);
je suppose que vous avez un certificat auto signé sur votre serveur, Je n'ai pas de code qui le fait en utilisant com.loopj.android: android-async-http: 1.4.9, mais je peux vous donner le code qui utilise URLConnection et qui charge le fichier crt (my.serveur.net.crt) from assets folder.:
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;
}
vous pouvez utiliser la solution de @Prerak. On dirait que cette solution fonctionne. C'est ma propre version. Il est similaire à @Prerak, mais un constructeur plus court:
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);
}