Issue
I've been trying for days to get this working. I'm trying to connect to my server over https with a self signed certificate. I don't think there is any pages or examples that I haven't read by now.
What I have done:
- Created bks keystore by following this tutorial: http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
It uses openssl s_client -connect domain.com:443
to get the certificate from the server. Then creates a bks keystore using bouncy castle.
Reading created keystore from raw folder adding it to sslfactory and and then to OkHttpClient. Like this:
public ApiService() { mClient = new OkHttpClient(); mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); mClient.setCache(getCache()); mClient.setCertificatePinner(getPinnedCerts()); mClient.setSslSocketFactory(getSSL()); } protected SSLSocketFactory getSSL() { try { KeyStore trusted = KeyStore.getInstance("BKS"); InputStream in = Beadict.getAppContext().getResources().openRawResource(R.raw.mytruststore); trusted.load(in, "pwd".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) { e.printStackTrace(); } return null; } public CertificatePinner getPinnedCerts() { return new CertificatePinner.Builder() .add("domain.com", "sha1/theSha=") .build(); }
This for some reason this always generates a
SSLPeerUnverifiedException
with or without the keystore. And with or without theCertificatePinner
.javax.net.ssl.SSLPeerUnverifiedException: Hostname domain.com not verified: 0 W/System.err﹕ certificate: sha1/theSha= W/System.err﹕ DN: 1.2.840.113549.1.9.1=#1610696e666f40626561646963742e636f6d,CN=http://domain.com,OU=development,O=domain,L=Valencia,ST=Valencia,C=ES W/System.err﹕ subjectAltNames: [] W/System.err﹕ at com.squareup.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:124) W/System.err﹕ at com.squareup.okhttp.Connection.connect(Connection.java:143) W/System.err﹕ at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:185) W/System.err﹕ at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128) W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:341) W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:330) W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:248) W/System.err﹕ at com.squareup.okhttp.Call.getResponse(Call.java:273) W/System.err﹕ at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230) W/System.err﹕ at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201) W/System.err﹕ at com.squareup.okhttp.Call.execute(Call.java:81) ...
What am I doing wrong?
Solution
I finally got this working with a mix of multiple answers.
First, the certificates was made wrongly, not sure how. But by creating them using the script in this answer made them work. What was needed was a server certificate and a key. Then the client needed another certificate.
To use the certificate in android I converted the .pem file to a .crt file like this:
openssl x509 -outform der -in client.pem -out client.crt
In android I added the certificate to my OkHttp client like the following:
public ApiService() {
mClient = new OkHttpClient();
mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
mClient.setCache(getCache());
mClient.setSslSocketFactory(getSSL());
}
protected SSLSocketFactory getSSL() {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream cert = getAppContext().getResources().openRawResource(R.raw.client);
Certificate ca = cf.generateCertificate(cert);
cert.close();
// creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
return new AdditionalKeyStore(keyStore);
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
The last part with new AdditionalKeyStore()
is taken from this very well written answer. Which adds a fallback keystore.
I hope this might help anyone else! This is the simplest way to get HTTPS working with a self-signed certificate that I have found. Other ways include having a BouncyCastle keystore which seems excessive to me.
Answered By - just_user
Answer Checked By - Gilberto Lyons (JavaFixing Admin)