I'm working on an Android
application that call some API
s over https
. using retrofit2
and okhttp3
. During my development I use a self-signed certificate that I generate in server. I face a lot of problems in calling API
s as I'm using a self-signed certificate, I solve them all but stuck on this exception SSLPeerUnverifiedException
.
Currently I had copy my certificate ServerCertificate.cer
to Download
directory in order to load it and add it to allowed KeyStore
.
I try a lot of solutions from different websites. I try okhttp
CustomTrust and from Android developer website
I write below code depending on Android developer example:
X509TrustManager mTrustManager = null;
private Retrofit getRetrofit(String identity, String serverBaseUrl) {
Retrofit retrofit = null;
try {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.sslSocketFactory(getSSLConfig().getSocketFactory(), mTrustManager)
.addInterceptor(new CustomInterceptor(identity))
.addInterceptor((new okhttp3.logging.HttpLoggingInterceptor())
.setLevel(okhttp3.logging.HttpLoggingInterceptor.Level.BODY))
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(serverBaseUrl)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
} catch (Exception ex) {
}
return retrofit;
}
private SSLContext getSSLConfig() throws Exception {
FileHelper fileHelper = FileHelper.getInstance();
String cerFilePath = "/storage/emulated/0/Download/ServerCertificate.cer";
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new FileInputStream(cerFilePath);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
//Below line print: ca=CN=SS_CEM_5_4
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
mTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
return context;
}
Currently when I call any API
I get following exception:
Exception occurred while calling heartbeat
javax.net.ssl.SSLPeerUnverifiedException: Hostname ss_cem_5_4 not verified:
certificate: sha256/OUxkHCacC0q0+ZQpL/3V1jFgV57CXweub/lSSUXsAZw=
DN: CN=\00S\00S\00_\00C\00E\00M\00_\005\00_\004
subjectAltNames: []
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:330)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:283)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:168)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at co.sedco.sevicesbase.managementproxy.webproxy.CustomInterceptor.intercept(CustomInterceptor.java:39)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254)
at okhttp3.RealCall.execute(RealCall.java:92)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:186)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:92)
at co.sedco.sevicesbase.managementproxy.webproxy.ManagementWebProxy.callHeartbeat(ManagementWebProxy.java:271)
at co.sedco.sevicesbase.heartbeat.HeartbeatManager$CallHeartbeatTimerTask.run(HeartbeatManager.java:91)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
I only managed to make self-signed certificate work by adding HostnameVerifier
to OkHttpClient
and override Verify
function to always return true
, but this solution is not acceptable, I believe that I will encounter a situations where I have to use a self-signed certificate in customer server (Although it is not recommended).
I'm calling Server using Url: https://ss_cem_5_4/Portal/api/GetHeartbeat
I also should mention that I was unable to call server through server name so I modified hosts
file in path '/system/etc/' to add mapping for my server. (I'm working on a rooted device)