Android 使用 okHttp 信任所有证书

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25509296/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 09:30:57  来源:igfitidea点击:

Trusting all certificates with okHttp

androidsslokhttpandroid-networking

提问by seato

For testing purposes, I'm trying to add a socket factory to my okHttp client that trusts everything while a proxy is set. This has been done many times over, but my implementation of a trusting socket factory seems to be missing something:

出于测试目的,我正在尝试向我的 okHttp 客户端添加一个套接字工厂,该工厂在设置代理时信任所有内容。这已经做过很多次了,但是我对信任套接字工厂的实现似乎缺少一些东西:

class TrustEveryoneManager implements 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 null;
    }
}
OkHttpClient client = new OkHttpClient();

final InetAddress ipAddress = InetAddress.getByName("XX.XXX.XXX.XXX"); // some IP
client.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipAddress, 8888)));

SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = new TrustManager[]{new TrustEveryoneManager()};
sslContext.init(null, trustManagers, null);
client.setSslSocketFactory(sslContext.getSocketFactory);

No requests are being sent out of my app and no exceptions are getting logged so it seems that it's failing silently within okHttp. Upon further investigation, it seems that there is an Exception being swallowed up in okHttp's Connection.upgradeToTls()when the handshake is being forced. The exception I'm being given is: javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

我的应用程序没有发出任何请求,也没有记录任何异常,因此它似乎在 okHttp 中默默地失败了。经过进一步调查,似乎Connection.upgradeToTls()在强制握手时,okHttp's中有一个异常被吞噬。我得到的例外是:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

The following code produces an SSLContextwhich works like a charm in creating an SSLSocketFactory that doesn't throw any exceptions:

下面的代码生成一个SSLContext,它在创建一个不抛出任何异常的 SSLSocketFactory 时就像一个魅力:

protected SSLContext getTrustingSslContext() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
    final SSLContextBuilder trustingSSLContextBuilder = SSLContexts.custom()
            .loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true; // Accepts any ssl cert whether valid or not.
                }
            });
    return trustingSSLContextBuilder.build();
}

The issue is that I'm trying to remove all Apache HttpClient dependencies from my app completely. The underlying code with Apache HttpClient to produce the SSLContextseems straightforward enough, but I'm obviously missing something as I cannot configure my SSLContextto match this.

问题是我试图从我的应用程序中完全删除所有 Apache HttpClient 依赖项。使用 Apache HttpClient 生成的底层代码SSLContext似乎很简单,但我显然缺少一些东西,因为我无法配置我的SSLContext来匹配它。

Would anyone be able to produce an SSLContext implementation which does what I'd like without using Apache HttpClient?

有没有人能够在不使用 Apache HttpClient 的情况下生成一个 SSLContext 实现来做我想做的事情?

回答by sonxurxo

Just in case anyone falls here, the (only) solution that worked for me is creating the OkHttpClientlike explained here.

以防万一有人跌倒,对我有用的(唯一)解决方案是创建此处OkHttpClient解释的类似内容。

Here is the code:

这是代码:

private static OkHttpClient getUnsafeOkHttpClient() {
  try {
    // Create a trust manager that does not validate certificate chains
    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[]{};
          }
        }
    };

    // Install the all-trusting trust manager
    final SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
    // Create an ssl socket factory with our all-trusting manager
    final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
    builder.hostnameVerifier(new HostnameVerifier() {
      @Override
      public boolean verify(String hostname, SSLSession session) {
        return true;
      }
    });

    OkHttpClient okHttpClient = builder.build();
    return okHttpClient;
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

回答by Jakub N

Following method is deprecated

以下方法已弃用

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Consider updating it to

考虑将其更新为

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)

回答by Ervin Zhang

Update OkHttp 3.0, the getAcceptedIssuers()function must return an empty arrayinstead of null.

更新 OkHttp 3.0,该getAcceptedIssuers()函数必须返回一个空数组而不是null.

回答by Tharindu Bandara

SSLSocketFactory does not expose its X509TrustManager, which is a field that OkHttp needs to build a clean certificate chain. This method instead must use reflection to extract the trust manager. Applications should prefer to call sslSocketFactory(SSLSocketFactory, X509TrustManager), which avoids such reflection.

SSLSocketFactory 不会暴露其 X509TrustManager,这是 OkHttp 构建干净证书链所需的字段。此方法必须使用反射来提取信任管理器。应用程序应该更喜欢调用 sslSocketFactory(SSLSocketFactory, X509TrustManager),这样可以避免这种反射。

Source: OkHttp documentation

来源:OkHttp 文档

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.sslSocketFactory(sslContext.getSocketFactory(),
    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[]{};
        }
    });

回答by doodla

This is sonxurxo's solution in Kotlin, if anyone needs it.

如果有人需要,这是在 Kotlin 中的 Sonxurxo 解决方案。

private fun getUnsafeOkHttpClient(): OkHttpClient {
    // Create a trust manager that does not validate certificate chains
    val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
        override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
    })

    // Install the all-trusting trust manager
    val sslContext = SSLContext.getInstance("SSL")
    sslContext.init(null, trustAllCerts, java.security.SecureRandom())
    // Create an ssl socket factory with our all-trusting manager
    val sslSocketFactory = sslContext.socketFactory

    return OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
        .hostnameVerifier { _, _ -> true }.build()
}

回答by George Shalvashvili

I made an extension function for Kotlin. Paste it where ever you like and import it while creating OkHttpClient.

我为 Kotlin 做了一个扩展函数。将其粘贴到您喜欢的任何位置并在创建OkHttpClient.

fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
    val naiveTrustManager = object : X509TrustManager {
        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
        override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
        override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
    }

    val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
        val trustAllCerts = arrayOf<TrustManager>(naiveTrustManager)
        init(null, trustAllCerts, SecureRandom())
    }.socketFactory

    sslSocketFactory(insecureSocketFactory, naiveTrustManager)
    hostnameVerifier(HostnameVerifier { _, _ -> true })
    return this
}

use it like this:

像这样使用它:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()

回答by netta

This is the Scala solution if anyone needs it

如果有人需要,这是 Scala 解决方案

def anUnsafeOkHttpClient(): OkHttpClient = {
val manager: TrustManager =
  new X509TrustManager() {
    override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def getAcceptedIssuers = Seq.empty[X509Certificate].toArray
  }
val trustAllCertificates =  Seq(manager).toArray

val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCertificates, new java.security.SecureRandom())
val sslSocketFactory = sslContext.getSocketFactory()
val okBuilder = new OkHttpClient.Builder()
okBuilder.sslSocketFactory(sslSocketFactory, trustAllCertificates(0).asInstanceOf[X509TrustManager])
okBuilder.hostnameVerifier(new NoopHostnameVerifier)
okBuilder.build()

}

}

回答by Tony Trummer

You should never look to override certificate validation in code! If you need to do testing, use an internal/test CA and install the CA root certificate on the device or emulator. You can use BurpSuite or Charles Proxy if you don't know how to setup a CA.

您永远不应该在代码中覆盖证书验证!如果您需要进行测试,请使用内部/测试 CA 并在设备或模拟器上安装 CA 根证书。如果您不知道如何设置 CA,您可以使用 BurpSuite 或 Charles Proxy。