Recently, I worked on a remote-networking implementation for an Android eCommerce application, and everything went smoothly. I used the Retrofit networking library and the base URL was http://api.infovistar.in without SSL (an HTTP scheme).
The moment the MVP was ready to be beta tested, we acquired a domain name and enabled SSL on the server. Consequently, the base URL scheme became HTTPS (example: https://api.infovistar.in).
The change to a secured URL in the app caused chaos in the entire app. All the endpoints were not connecting to the server successfully. There was a problem with the network handshake between the client and server.
HTTP FAILED: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Solution 1:
In doing some research, I found out the Android system didn’t trust the server certificate. There are several possible reasons for this:
- The Certificate Authority (CA) that issued the server certificate was unknown.
- The server certificate wasn’t signed by a CA but was self-signed.
- The server configuration is missing an intermediate CA.
In my case, this issue occurred because the server certificate was self-signed.
The Android documentation describes a simple way to set it up to trust self-signed certificates, which I will outline in three steps.
Step 1
Add the .crt or .pem file to the raw folder.
The digital certificate will be obtained from the server or you can request it from the backend engineer.
Step 2
Create an XML network security configuration file (network_security_config.xml) as follows:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.infovistar.in</domain>
<trust-anchors>
<certificates src="@raw/certificate" />
</trust-anchors>
</domain-config>
</network-security-config>
Step 3
Provide the application’s network configuration settings in the Manifest.xml file.
Solution 2:
In your Retrofit request, you need to modify your OKHttpClient.Builder object so that your request will create a certificate that can be trusted by your server. Only then can your server allow access to your request.
public static Retrofit getUnsafeApiClient(Context ctx) {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getUnsafeOkHttpClient().build())
.build();
}
public static OkHttpClient.Builder getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@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]);
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
These two solutions should allow you to connect seamlessly with the backend.
Leave a Reply