How to Bypass SSL Certificate Validation in Android (OkHttp & Retrofit)
🚨 DANGER: Do not use in Production!
Disabling SSL validation makes your app vulnerable to Man-in-the-Middle (MitM) attacks.Google Play will reject your app if you leave this code in your production build. Always wrap this code in if (BuildConfig.DEBUG) checks.
If you are using OkHttp (which Retrofit uses internally), you might encounter CertPathValidatorException when working with local dev servers or proxy tools like Charles or Debuggo. Here are the two ways to solve this: the "Dirty Way" (Code) and the "Clean Way" (XML).
The "Dirty" Way: Unsafe OkHttpClient
The "Dirty" Way: Unsafe OkHttpClient
You can create a custom OkHttpClient that trusts all certificates. This is useful for quick debugging but risky if not handled correctly. (See the OkHttp GitHub for more).
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import okhttp3.OkHttpClient
fun getUnsafeOkHttpClient(): OkHttpClient {
// 🚨 SAFETY CHECK: Only allow in Debug builds
if (!BuildConfig.DEBUG) {
throw RuntimeException("Unsafe client not allowed in production!")
}
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>()
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true } // 🚨 Trust all hostnames
.build()
}
// Usage with Retrofit
val retrofit = Retrofit.Builder()
.baseUrl("https://api.dev.com")
.client(getUnsafeOkHttpClient()) // <--- Inject here
.addConverterFactory(GsonConverterFactory.create())
.build()▶Show Java Version (Legacy)
public static OkHttpClient getUnsafeOkHttpClient() {
try {
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
builder.hostnameVerifier((hostname, session) -> true);
return builder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}The "Clean" Way: Network Security Config
Instead of modifying your code, you can tell Android to trust user-installed certificates only in debug builds. This is the official and recommended way (see Android Docs).
Note: This is required for Android 9 (API 28) and above, where cleartext traffic and user certificates are blocked by default.
SSLContext manually in code (like in the 'Dirty Way' above), Android ignores this XML file.1. Create res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<!-- Trust user added CAs -->
<certificates src="user" />
<certificates src="system" />
</trust-anchors>
</debug-overrides>
</network-security-config>2. Apply in AndroidManifest.xml
Reference the file exactly as @xml/network_security_config.
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>3. Verification
To verify, inspect traffic in Debuggo or Charles Proxy. If you see HTTPS content (JSON) instead of "Unknown", the bypass is working correctly.