14

Using com.squareup.okhttp:okhttp:2.4.0 with com.squareup.retrofit:retrofit:1.9.0 on an Android app, trying to communicate with an server REST API over HTTPS, that uses a self signed certificate.

Server keystore has a private key and 2 certificates, the server's and a root certificate. openssl s_client output -

Certificate chain
 0 s:/C=...OU=Dev/CN=example.com
   i:/C=... My CA/emailAddress=info@example.com
 1 s:/C=... My CA/emailAddress=info@example.com
   i:/C=... My CA/emailAddress=info@example.com

At the Android app, OkHttp is initialised with the root certificate's SHA1 signature -

CertificatePinner certificatePinner = new CertificatePinner.Builder()
        .add("example.com", "sha1/5d...3b=")
        .build();

OkHttpClient client = new OkHttpClient();
client.setCertificatePinner(certificatePinner);

RestAdapter restAdapter = new RestAdapter.Builder()
        .setEndpoint("https://example.com")
        .setClient(new OkClient(client))
        .build();

But when trying to send a request fails with exception -

retrofit.RetrofitError: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:395)
        at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
        at java.lang.reflect.Proxy.invoke(Proxy.java:397)
        at $Proxy1.report(Unknown Source)
        ...
        at android.os.AsyncTask$2.call(AsyncTask.java:288)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)
 Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:306)
        at com.squareup.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:103)
        at com.squareup.okhttp.Connection.connect(Connection.java:143)
        at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:185)
        at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
        at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:341)

It's thrown at com.squareup.okhttp.internal.http.SocketConnector when trying sslSocket.startHandshake(), even before CertificatePinner is used to check the received certificates.

I've made sure server has certificates installed correctly using openssl and with curl --cacert root.pem.

So why does OkHttp throw an exception before even trying to check if provided certificates are OK?

Kof
  • 23,893
  • 9
  • 56
  • 81

2 Answers2

6

OkHttp does not support self signed certificates.

When using a certificate which is signed by a known CA, handshakes succeeds and then CertificatePinner makes sure the certificate chain contains at least one of the provided signatures. If none appear, it will throw an exception, stopping the request.

So it is possible to use a cert signed by a known CA and pin one of the certificates to make sure we're talking to the right server.

Kof
  • 23,893
  • 9
  • 56
  • 81
  • 2
    Yup. Certificate pinning doesn't replace the existing certificate verification; it complements it. You need to configure your client to trust your certificate; there are plenty of examples in the tests! – Jesse Wilson Aug 31 '15 at 03:40
  • @JesseWilson and @Kof: can I ask a question, does `a known CA` mean a CA that is installed succesfully in Android phone (I can see it in `Trusted credentials - User`? Thanks – BNK Jun 10 '16 at 02:11
  • Probably, it's a CA that the device has its signature installed in its trusted CA database. – Kof Jun 12 '16 at 11:38
  • how can we know if our CA is trusted? – Mr. Jay Sep 28 '21 at 01:47
  • @Mr.Jay in browsers they have a list of trusted CAs, list can probably be looked up on the internet, if it's your CA (for self signing) you can trust yourself, trust me. – Kof Oct 18 '21 at 07:51
4

OkHttp does support self-signed certificates.

Please check this answer to learn how to create a SslSocket that trusts only your certificate.

Community
  • 1
  • 1
Paulo Avelar
  • 2,140
  • 1
  • 17
  • 31
  • 2
    Another place to see example code is in OkHttp's recipe repo: [CustomTrust.java](https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java). It provides more generic working code. – Anonsage Jan 21 '16 at 01:39