Android

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found

javax.net.ssl.SSLHandshakeException: java.security.cert.Исключение CertPathValidatorException: привязка доверия для пути сертификации не найдена

Я использую Retrofit для доступа к моему REST API. Однако, когда я помещаю свой API за ssl и получаю к нему доступ с помощью http://myhost/myapi, я получаю эту ошибку:

Нужно ли мне делать что-то дополнительное теперь, когда мой API поддерживает SSL?

Вот как я подключаюсь:

private final String API = "https://myhost/myapi";

private final RestAdapter REST_ADAPTER = new RestAdapter.Builder()
.setServer(API)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();

01-10 09:49:55.621 2076-2100/com.myapp.mobile D/Retrofit﹕ javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:401)
at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:90)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:48)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:287)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:222)
at $Proxy12.signin(Native Method)
at com.myapp.loginactivity$3.doInBackground(LoginActivity.java:143)
at com.myapp.loginactivity$3.doInBackground(LoginActivity.java:136)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:282)
at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:202)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:595)
at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:398)
            at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
            at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
            at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
            at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
            at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
            at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
            at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
            at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
            at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:90)
            at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:48)
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:287)
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:222)
            at $Proxy12.signin(Native Method)
            at com.myapp.LoginActivity$3.doInBackground(LoginActivity.java:143)
            at com.myapp.LoginActivity$3.doInBackground(LoginActivity.java:136)
            at android.os.AsyncTask$2.call(AsyncTask.java:287)
            at java.util.concurrent.FutureTask.run(FutureTask.java:234)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
            at java.lang.Thread.run(Thread.java:841)
Переведено автоматически
Ответ 1

Причина, по которой это происходит, заключается в том, что JVM / Dalvik не уверены в сертификатах CA в системе или в хранилищах пользовательских сертификатов.

Чтобы исправить это с помощью Retrofit, если вы используете okhttp, с другим клиентом это очень похоже.
Вам нужно сделать:

A). Создайте хранилище сертификатов, содержащее открытый ключ CA. Для этого вам нужно запустить следующий скрипт для * nix. Вам необходимо установить openssl на свой компьютер и загрузить с https://www.bouncycastle.org / jar bcprov-jdk16-1.46.jar. Загружайте эту версию, а не другую, версия 1.5x несовместима с Android 4.0.4.

#!/bin/bash

if [ -z $1 ]; then
echo "Usage: cert2Android<CA cert PEM file>"
exit 1
fi

CACERT=$1
BCJAR=bcprov-jdk16-1.46.jar

TRUSTSTORE=mytruststore.bks
ALIAS=`openssl x509 -inform PEM -subject_hash -noout -in $CACERT`

if [ -f $TRUSTSTORE ]; then
rm $TRUSTSTORE || exit 1
fi

echo "Adding certificate to $TRUSTSTORE..."
keytool -import -v -trustcacerts -alias $ALIAS \
-file $CACERT \
-keystore $TRUSTSTORE -storetype BKS \
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath $BCJAR \
-storepass secret

echo ""
echo "Added '$CACERT' with alias '$ALIAS' to $TRUSTSTORE..."

B). Скопируйте файл truststore mytruststore.bks в res / raw вашего проекта.
местоположение truststore

C). Установка SSLContext соединения:

.............
okHttpClient = new OkHttpClient();
try {
KeyStore ksTrust = KeyStore.getInstance("BKS");
InputStream instream = context.getResources().openRawResource(R.raw.mytruststore);
ksTrust.load(instream, "secret".toCharArray());

// TrustManager decides which certificate authorities to use.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ksTrust);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
e.printStackTrace();
}
.................
Ответ 2

Это может произойти по нескольким причинам, в том числе:


  1. Центр сертификации, выдавший сертификат сервера, был неизвестен

  2. Сертификат сервера не был подписан центром сертификации, но был подписан самостоятельно

  3. В конфигурации сервера отсутствует промежуточный центр сертификации

пожалуйста, проверьте эту ссылку для решения: https://developer.android.com/training/articles/security-ssl.html#CommonProblems

Ответ 3

В принципе, у вас есть четыре потенциальных решения для исправления исключения "javax.net.ssl.SSLHandshakeException: " на Android


  1. Доверяйте всем сертификатам. Не делайте этого, если вы действительно не знаете, что делаете.

  2. Создайте пользовательский SSLSocketFactory, который доверяет только вашему сертификату. Это работает до тех пор, пока вы точно знаете, к каким серверам собираетесь подключаться, но как только вам потребуется подключиться к новому серверу с другим SSL-сертификатом, вам потребуется обновить ваше приложение.

  3. Создайте файл хранилища ключей, содержащий "основной список" сертификатов Android, затем добавьте свой собственный. Если срок действия какого-либо из этих сертификатов истекает в будущем, вы несете ответственность за их обновление в своем приложении. Я не могу придумать причину для этого.

  4. Создайте пользовательский SSLSocketFactory, который использует встроенное хранилище ключей сертификата, но использует альтернативное хранилище ключей для всего, что не удается проверить по умолчанию. Это хорошо объяснено в нажмите здесь

Кроме того, я хочу подробнее остановиться на пункте № 1. Мы можем выборочно пропустить некоторый домен, используя manifest network config в качестве объяснения:


  1. Создайте файл "network_security_config.xml" в папке xml в папке res со следующим содержимым.


       <network-security-config xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <domain-config>
    <domain includeSubdomains="true">191.1.1.0</domain>
    <domain includeSubdomains="true">your_domain</domain>
    <trust-anchors>
    <certificates src="system" />
    <certificates src="user" />
    </trust-anchors>
    </domain-config>
    </network-security-config>


  2. Добавьте "network_security_config.xml" в тег приложения в манифесте как:


    android:networkSecurityConfig="@xml/network_security_config"



Вот и все .. готово !!. Вы успешно пропустили SSL-сертификат.

Ответ 4

Исправление для Android и выше: У меня была похожая проблема, и я попытался решить ее, выполнив шаги, описанные в https://developer.android.com/training/articles/security-config

Но изменения конфигурации без какой-либо сложной логики кода будут работать только на Android версии 24 и выше.

Исправлено для всех версий, включая версию < N: Поэтому для Android ниже N (версия 24) решение заключается в изменении кода, как упоминалось выше. Если вы используете OkHttp, то соблюдайте customTrust: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java

java android ssl retrofit