Сертификат не работает с OkHttp на Android

Используя com.squareup.okhttp:okhttp:2.4.0 с com.squareup.retrofit:retrofit:1.9.0 в приложении для Android, пытайтесь установить связь с сервером REST API через HTTPS, который использует самоподписанный сертификат.

У сервера keystore есть закрытый ключ и 2 сертификата, сервер и корневой сертификат. openssl s_client вывод -

Certificate chain
 0 s:/C=...OU=Dev/CN=example.com
   i:/C=... My CA/[email protected]
 1 s:/C=... My CA/[email protected]
   i:/C=... My CA/[email protected]

В Android-приложении OkHttp инициализируется сигнатурой корневого сертификата SHA1 -

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();

Но при попытке отправить запрос сбой исключается -

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)

При попытке sslSocket.startHandshake() он выбрасывается при com.squareup.okhttp.internal.http.SocketConnector, даже до CertificatePinner используется для проверки полученных сертификатов.

Я убедился, что сервер правильно установил сертификаты, используя openssl и curl --cacert root.pem.

Итак, почему OkHttp генерирует исключение, прежде чем пытаться проверить, соответствуют ли сертификаты?

Ответы

Ответ 1

OkHttp не поддерживает самоподписанные сертификаты.

При использовании сертификата, подписанного известным ЦС, рукопожатие успешно завершается, а затем CertificatePinner гарантирует, что цепочка сертификатов содержит хотя бы одну из предоставленных подписей. Если ни один не появляется, он выдает исключение, останавливая запрос.

Таким образом, можно использовать сертификат, подписанный известным ЦС, и подключить один из сертификатов, чтобы убедиться, что мы разговариваем с правильным сервером.

Ответ 2

OkHttp поддерживает самоподписанные сертификаты.

Пожалуйста, проверьте этот ответ, чтобы узнать, как создать SslSocket, который доверяет только вашему сертификату.