OkHttp trust certificate
In my Android app, I need to perform some requests to the server using OkHttp library. I have an ssl certificate with four parts:
- AddTrustExternalCARoot.crt
- COMODORSAAddTrustCA.crt
- COMODORSADomainValidationSecureServerCA.crt
- www_mydomain_com.crt
I have imported all the parts in portecle 1.9, then set the keystore password, and exported the .bks certificate.
Then, inserted mycert.bks into the res/raw folder of my app project. Now I try to connect to the server via https using the following code:
OkHttpClient client = new OkHttpClient();
try{
client.setSslSocketFactory(getPinnedCertSslSocketFactory(context));
RequestBody formBody = new FormEncodingBuilder()
.add("params", "xxx")
.build();
Request request = new Request.Builder()
.url("https:\\mydomain.com")
.post(formBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
return response.body().string();;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private SSLSocketFactory getPinnedCertSslSocketFactory(Context context) {
try {
KeyStore trusted = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.mycert);
trusted.load(in, "mypass".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trusted);
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (Exception e) {
Log.e("MyApp", e.getMessage(), e);
}
return null;
}
But I get an exception, this is the logcat:
10-11 18:22:51.930 13604-5341/com.aaa.android.client W/System.err: javax.net.ssl.SSLPeerUnverifiedException: Hostname myserver.net not verified:
10-11 18:22:51.930 13604-5341/com.aaa.android.client W/System.err: certificate: sha1/"mysha1string"
10-11 18:22:51.930 13604-5341/com.aaa.android.client W/System.err: DN: CN=www.aaa.it,OU=PositiveSSL,OU=Domain Control Validated
10-11 18:22:51.930 13604-5341/com.aaa.android.client W/System.err: subjectAltNames: [www.aaa.it, aaa.it]
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Connection.connectTls(Connection.java:244)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Connection.connectSocket(Connection.java:199)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Connection.connect(Connection.java:172)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:367)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:328)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:245)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Call.getResponse(Call.java:267)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:224)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:195)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.squareup.okhttp.Call.execute(Call.java:79)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.aaa.android.client.Login$BackgroundTask.doInBackground(Login.java:294)
10-11 18:22:51.940 13604-5341/com.aaa.android.client W/System.err: at com.aaa.android.client.Login$BackgroundTask.doInBackground(Login.java:277)
10-11 18:22:51.950 13604-5341/com.aaa.android.client W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:288)
10-11 18:22:51.950 13604-5341/com.aaa.android.client W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
10-11 18:22:51.950 13604-5341/com.aaa.android.client W/System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
10-11 18:22:51.950 13604-5341/com.aaa.android.client W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
10-11 18:22:51.950 13604-5341/com.aaa.android.client W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
10-11 18:22:51.950 13604-5341/com.aaa.android.client W/System.err: at java.lang.Thread.run(Thread.java:818)
Where is the problem?
Update: I tried a lot with the library and found this:
- If I make a request to https:\link.com, the OkHttp library automatically trusts all certificates.
- If I just want to trust my certificate, I have to do the solution posted above.
- If I don't want to accept all hosts, but don't use my certificate, I can use the hostnameVerifier suggested by BNK.
But why is my problem solvable? Because I was stupid, I made a request to my vps url (vpsxxx.net/directory.php) instead of my domain (mydomain.it/directory.php).
Apparently the SSL certificate is for my domain but not my vps.
I hope this is useful to someone.
PS: sorry for my english! :D
Let's assume your server's application is hosted inside the server with a server certificate "Issued to"
, "localhost"
eg. Then verify
you can verify in the inner method "localhost"
.
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify("localhost", session);
}
};
You can read more information at the following links:
-
If the URL's hostname does not match the peer's identifying hostname, it is used during the handshake.
Frequently Asked Questions about Hostname Verification
One reason this happens is due to server misconfiguration. The server configured certificate does not have a subject or subject alternative name field that matches the server you are trying to connect to...
You can then use the hostname validator in your application by calling client.setHostnameVerifier(hostnameVerifier);
. Hope this helps!
P/S: There is another temporary workaround return true;
inside the verify
method, but it is not recommended.