Как добавить доверенный сертификат в OkHttp - PullRequest
1 голос
/ 16 октября 2019

Мне нужно доверять сертификату одного конкретного сайта с помощью клиента OkHttp. Я нашел решение здесь: https://jebware.com/blog/?p=340

Этот код хорошо работает с сервером, которому я хотел доверять. Единственная проблема в том, что никакие другие серверы теперь не являются доверенными! : D Так что, очевидно, не очень идеальный ...

Я просто хочу, чтобы OkHttp сохранил поведение по умолчанию, а просто добавьте мой собственный доверенный сертификат.

Текущее поведение: OkHttp не может подключиться к любому серверу, использующему HTTPS, кроме myaccount.esbecars.com

Ожидаемое поведение: OkHttp может подключаться ко всем доверенным серверам по умолчанию (я не не хочу слепо доверять всем серверам) включая myaccount.esbecars.com

Я буду благодарен заВаш совет!

Я использую этот код:

SSLContext sslContext;
TrustManager[] trustManagers;
try {
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null, null);
    InputStream certInputStream = context.getAssets().open("esb_ireland.pem");
    BufferedInputStream bis = new BufferedInputStream(certInputStream);
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    while (bis.available() > 0) {
        Certificate cert = certificateFactory.generateCertificate(bis);
        keyStore.setCertificateEntry("myaccount.esbecars.com", cert);
    }
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(keyStore);
    trustManagers = trustManagerFactory.getTrustManagers();
    sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagers, null);

    instance = new OkHttpClient.Builder()
            .cache(myCache)
            .connectTimeout(FrontendConstants.NETWORK_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
            .readTimeout(FrontendConstants.NETWORK_READ_TIMEOUT, TimeUnit.MILLISECONDS)
            .addInterceptor(new ObedientCacheInterceptor())
            .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
            .build();
} catch (Exception e) {
    e.printStackTrace(); //TODO replace with real exception handling tailored to your needs
}

Исключение, которое я получаю при попытке подключиться ко всем серверам, кроме myaccount.esbecars.com:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:361)
        at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:336)
        at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:300)
        at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:185)
        at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224)
        at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:107)
        at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:87)
        at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
        at com.sanctusmedia.android.WattsUp.network.OkHttpSingleton$ObedientCacheInterceptor.intercept(OkHttpSingleton.java:121)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:221)
        at okhttp3.RealCall.execute(RealCall.java:81)

1 Ответ

1 голос
/ 16 октября 2019

Первый вариант - добавить в локальный файл хранилища ключей, хранящийся в каталоге JRE.

Но я также использовал настраиваемый менеджер доверия, который объединяет сертификаты https://github.com/yschimke/okurl/blob/07e79795e14b9163bcf4342f39c23020f51ecf64/src/main/kotlin/com/baulsupp/okurl/security/MergedX509TrustManager.kt

https://github.com/yschimke/okurl/blob/0520489d697d49b179010e468a87ef0749ff95be/src/main/kotlin/com/baulsupp/okurl/security/CertificateUtils.kt

package com.baulsupp.okurl.security

import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager

class MergedX509TrustManager(private val managers: List<X509TrustManager>) : X509TrustManager {

  override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
    throw UnsupportedOperationException()
  }

  override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
    val exceptions = mutableListOf<CertificateException>()

    for (tm in managers) {
      try {
        tm.checkServerTrusted(chain, authType)
        return
      } catch (e: CertificateException) {
        exceptions.add(e)
      }
    }

    throw bestException(exceptions)
  }

  fun bestException(exceptions: List<CertificateException>): CertificateException {
    if (exceptions.isNotEmpty()) {
      // last is probably system keystore
      throw exceptions[exceptions.size - 1]
    } else {
      throw CertificateException("no X509TrustManager to check")
    }
  }

  override fun getAcceptedIssuers(): Array<X509Certificate> {
    val certificates = mutableListOf<X509Certificate>()

    for (tm in managers) {
      certificates.addAll(tm.acceptedIssuers.toList())
    }

    return certificates.toTypedArray()
  }
}
...