Reputation: 2131
For testing purposes, I'm trying to add a socket factory to my okHttp client that trusts everything while a proxy is set. This has been done many times over, but my implementation of a trusting socket factory seems to be missing something:
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);
No requests are being sent out of my app and no exceptions are getting logged so it seems that it's failing silently within okHttp. Upon further investigation, it seems that there is an Exception being swallowed up in okHttp's Connection.upgradeToTls()
when the handshake is being forced. The exception I'm being given is: javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.
The following code produces an SSLContext
which works like a charm in creating an SSLSocketFactory that doesn't throw any exceptions:
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();
}
The issue is that I'm trying to remove all Apache HttpClient dependencies from my app completely. The underlying code with Apache HttpClient to produce the SSLContext
seems straightforward enough, but I'm obviously missing something as I cannot configure my SSLContext
to match this.
Would anyone be able to produce an SSLContext implementation which does what I'd like without using Apache HttpClient?
Upvotes: 156
Views: 162251
Reputation: 5718
Just in case anyone falls here, the (only) solution that worked for me is creating the OkHttpClient
like explained here.
Here is the code:
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);
}
}
Upvotes: 320
Reputation: 1560
I made an extension function for Kotlin. Paste it where ever you like and import it while creating 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
}
use it like this:
val okHttpClient = OkHttpClient.Builder().apply {
// ...
if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
ignoreAllSSLErrors()
//...
}.build()
Upvotes: 50
Reputation: 191
Update OkHttp 3.0, the getAcceptedIssuers()
function must return an empty array instead of null
.
Upvotes: 16
Reputation: 518
This is the Scala solution if anyone needs it
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()
}
Upvotes: 4
Reputation: 600
SSLSocketFactory does not expose its X509TrustManager, which is a field that OkHttp needs to build a clean certificate chain. This method instead must use reflection to extract the trust manager. Applications should prefer to call sslSocketFactory(SSLSocketFactory, X509TrustManager), which avoids such reflection.
Source: OkHttp documentation
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[]{};
}
});
Upvotes: 13
Reputation: 423
This is sonxurxo's solution in Kotlin, if anyone needs it.
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()
}
Upvotes: 24
Reputation: 372
Following method is deprecated
sslSocketFactory(SSLSocketFactory sslSocketFactory)
Consider updating it to
sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
Upvotes: 15
Reputation: 33
You should never look to override certificate validation in code! If you need to do testing, use an internal/test CA and install the CA root certificate on the device or emulator. You can use BurpSuite or Charles Proxy if you don't know how to setup a CA.
Upvotes: -16