Самозаверяющий сертификат и петля для Android
Я пытаюсь использовать loopj для создания асинхронных HTTP
Запросы. Прекрасно работает, за исключением случаев, когда я пытаюсь зайти на сайт https с самозаверяющим сертификатом. я получил
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate.
Я думаю, что параметры ssl по умолчанию могут быть переопределены с помощью setSSLSocketFactory(SSLSocketFactory sslSocketFactory)
метод, но я не уверен, как это сделать, или это может быть не совсем правильно.
Подскажите пожалуйста как можно решить эту проблему?
6 ответов
Вы делаете это почти точно так же, как описано здесь для HttpClient, за исключением чуть более простого - Доверие всем сертификатам с использованием HttpClient поверх HTTPS
Создайте собственный класс:
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
Затем, когда вы создаете свой экземпляр клиента:
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client.setSSLSocketFactory(sf);
}
catch (Exception e) {
}
Вы можете использовать конструктор AsyncHttpClient(логическое исправление fixNoHttpResponseException, int httpPort, int httpsPort). От версии loopj библиотека 1.4.4 и выше. Например
mClient = new AsyncHttpClient(true, 80, 443);
и вы получите предупреждающее сообщение для logcat в подробном журнале.
Beware! Using the fix is insecure, as it doesn't verify SSL certificates.
Более простой способ - использовать встроенный MySSLSocketFactory в loopj, поэтому вам не нужно создавать другой класс
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client.setSSLSocketFactory(sf);
}
catch (Exception e) {}
Как объясняется во многих местах, просто обходить проверку сертификатов неправильно на многих уровнях. Не делай этого!
Вместо этого вы должны создать .bks
файл из вашего сертификата (для этого вам понадобится Bouncy Castle):
keytool -importcert -v -trustcacerts -file "path/to/certfile/certfile.crt" -alias IntermediateCA -keystore "outputname.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path/to/bouncycastle/bcprov-jdk15on-154.jar" -storetype BKS -storepass atleastsix
Следующее место вашего недавно созданного outputname.bks
внутри res/raw
папка.
Создайте вспомогательную функцию (она может быть внутри собственного класса или как угодно):
private static SSLSocketFactory getSocketFactory(Context ctx) {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = ctx.getResources().openRawResource(R.raw.outputname); //name of your keystore file here
try {
// Initialize the keystore with the provided trusted certificates
// Provide the password of the keystore
trusted.load(in, "atleastsix".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); // This can be changed to less stricter verifiers, according to need
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
И последнее, но не менее важное, установите AsyncHttpClient
использовать новую фабрику сокетов:
AsyncHttpClient client = new AsyncHttpClient();
client.setSSLSocketFactory(getSocketFactory(context));
С Https
а также certificate
Я сделал это успешно с помощью двух документов HttpsUrlConnection и Portecle.
Не НУКАЙТЕ все сертификаты SSL. Доверять всем сертификатам - ПЛОХАЯ ПРАКТИКА!!!
- Принимайте только ваш SSL-сертификат.
Посмотрите на мое решение. Некоторое содержание из этого Гист может помочь вам понять, как это сделать.
Обс.: Я использую Android Volley.
https://gist.github.com/ivanlmj/f11fb50d35fa1f2b9698bfb06aedcbcd