Доверять / белый список сертификата в OkHttp (не найден доверенный якорь для пути сертификации)

У меня проблема с устройствами Android 4, которые получают следующее исключение при подключении к серверу:

java.security.cert.CertPathValidatorException: доверенная привязка для пути сертификации не найдена. по адресу com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:410) по адресу okhttp3.internal.connection.RealConnection.connectTls(SourceFile:319) по адресу okhttp3.internal.connection.RealConnection (присоединение к источнику) okhttp3.internal.connection.RealConnection.connect(SourceFile:168) по адресу okhttp3.internal.connection.StreamAllocation.findConnection(SourceFile:257) по адресу okhttp3.internal.connection.StreamAllocation.findHealthyConnection(источник: 135. точка доступа:135).StreamAllocation.newStream(SourceFile:114) в файле okhttp3.internal.connection.ConnectInterceptor.intercept(SourceFile:42) в файле okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:147) в файле okhttp3.internal.http.Rex. SourceFile:121) по адресу okhttp3.internal.cache.CacheInterceptor.intercept(SourceFile:93) по адресу okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:147) по адресу okhttp3.internal.http.RealInterceptorChain.proceed) в источнике (okhttp3.internal.http. BridgeInterceptor.intercept (SourceFile: 93) в файле okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:147) в файле okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(SourceFile:126) в файле okhttp3.inha.Interh.: 147) в okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:121) в okhttp3.RealCall.getResponseWithInterceptorChain(SourceFile:254) в okhttp3.RealCall.execute(SourceFile:92)

Сертификат сервера от Cloudflare, я проверил его с помощью нескольких инструментов, таких как https://www.digicert.com/help/ и, похоже, все в порядке.

Но по некоторым причинам я не понимаю, что это начало терпеть неудачу в версиях Android 4.

Пробовал решение доверять всем сертификатам [ССЫЛКА], и это работает, но это, очевидно, имеет проблемы с безопасностью, такие как подвергание вашего приложения атаке "человек посередине"

final TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        }
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
okHttpBuilder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);

Как я могу реализовать TrustManager с поведением по умолчанию, но в белый список входит только сертификат сервера.

Спасибо

Изменить: следуя примеру в OkHttp @ CustomTrust (спасибо CommonsWare)

Использовал команду:

openssl s_client -showcerts -servername www.serverdomain.com -connect www.serverdomain.com:443

В цепочке сертификатов дали мне два сертификата в формате:

-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----

Заменили URL и сертификаты в примере на полученные, но он все еще не работает, есть идеи?

1 ответ

Вам нужно сохранить ваш сертификат в сырой папке, а затем:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream instream = context.getResources().openRawResource(R.raw.gtux_cert);
Certificate ca;
try {
    ca = cf.generateCertificate(instream);
} finally {
    caInput.close();
}

KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(););
tmf.init(kStore);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

okHttpClient.setSslSocketFactory(context.getSocketFactory());

Больше информации здесь: Безопасность SSL

Другие вопросы по тегам