Доверие всем сертификатам с помощью okHttp
В целях тестирования я пытаюсь добавить фабрику сокетов в мой клиент okHttp, который доверяет всему, пока установлен прокси. Это было сделано много раз, но в моей реализации доверяющей фабрики сокетов что-то не хватает:
class TrustEveryoneManager implements X509TrustManager {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
OkHttpClient client = new OkHttpClient();
final InetAddress ipAddress = InetAddress.getByName("XX.XXX.XXX.XXX"); // some IP
client.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipAddress, 8888)));
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = new TrustManager[]{new TrustEveryoneManager()};
sslContext.init(null, trustManagers, null);
client.setSslSocketFactory(sslContext.getSocketFactory);
Из моего приложения не отправляются никакие запросы, и никакие исключения не регистрируются, поэтому кажется, что это происходит молча в okHttp. После дальнейшего расследования, кажется, что есть исключение, поглощаемое в okHttp Connection.upgradeToTls()
когда рукопожатие навязывается. Исключение, которое мне дают: javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.
Следующий код производит SSLContext
который работает как шарм при создании SSLSocketFactory, который не выдает никаких исключений:
protected SSLContext getTrustingSslContext() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
final SSLContextBuilder trustingSSLContextBuilder = SSLContexts.custom()
.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true; // Accepts any ssl cert whether valid or not.
}
});
return trustingSSLContextBuilder.build();
}
Проблема в том, что я пытаюсь полностью удалить все зависимости Apache HttpClient из моего приложения. Базовый код с Apache HttpClient для создания SSLContext
кажется достаточно простым, но я явно что-то упускаю, так как не могу настроить SSLContext
чтобы соответствовать этому.
Кто-нибудь сможет создать реализацию SSLContext, которая делает то, что я хотел бы, без использования Apache HttpClient?
5 ответов
На всякий случай, если кто-нибудь попадет сюда, единственное решение, которое сработало для меня, это создание OkHttpClient
как объяснено здесь.
Вот код:
private static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
OkHttpClient okHttpClient = builder.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Я сделал функцию расширения для Котлина. Вставьте его куда хотите и импортируйте при созданииOkHttpClient
.
fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
val naiveTrustManager = object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
}
val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
val trustAllCerts = arrayOf<TrustManager>(naiveTrustManager)
init(null, trustAllCerts, SecureRandom())
}.socketFactory
sslSocketFactory(insecureSocketFactory, naiveTrustManager)
hostnameVerifier(HostnameVerifier { _, _ -> true })
return this
}
используйте это так:
val okHttpClient = OkHttpClient.Builder().apply {
// ...
if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
ignoreAllSSLErrors()
//...
}.build()
Это решение sonxurxo в Котлине, если оно кому-то нужно.
private fun getUnsafeOkHttpClient(): OkHttpClient {
// Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
}
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
})
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
return OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }.build()
}
Обновите okhttp3.0, функция getAcceptedIssuers() должна возвращать пустой массив вместо нуля
Следующий метод устарел
sslSocketFactory(SSLSocketFactory sslSocketFactory)
Рассмотрите возможность обновления до
sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
SSLSocketFactory не предоставляет свой X509TrustManager, который является полем, необходимым OkHttp для построения чистой цепочки сертификатов. Этот метод вместо этого должен использовать отражение, чтобы извлечь менеджер доверия. Приложения должны предпочитать вызывать sslSocketFactory(SSLSocketFactory, X509TrustManager), что позволяет избежать такого отражения.
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslContext.getSocketFactory(),
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
});
Это решение Scala, если оно кому-то нужно
def anUnsafeOkHttpClient(): OkHttpClient = {
val manager: TrustManager =
new X509TrustManager() {
override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String) = {}
override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String) = {}
override def getAcceptedIssuers = Seq.empty[X509Certificate].toArray
}
val trustAllCertificates = Seq(manager).toArray
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCertificates, new java.security.SecureRandom())
val sslSocketFactory = sslContext.getSocketFactory()
val okBuilder = new OkHttpClient.Builder()
okBuilder.sslSocketFactory(sslSocketFactory, trustAllCertificates(0).asInstanceOf[X509TrustManager])
okBuilder.hostnameVerifier(new NoopHostnameVerifier)
okBuilder.build()
}
Никогда не пытайтесь переопределить проверку сертификата в коде! Если вам необходимо выполнить тестирование, используйте внутренний / тестовый CA и установите корневой сертификат CA на устройстве или эмуляторе. Вы можете использовать BurpSuite или Charles Proxy, если вы не знаете, как настроить CA.