代码之家  ›  专栏  ›  技术社区  ›  Yuri Schimke

连接到Android N或更早版本的Let’s Encrypt主机的CertPathValidatorException

  •  0
  • Yuri Schimke  · 技术社区  · 4 年前

    [编辑:如果您是来参加2021年1月的Let's Encrypt到期活动的,请先阅读本文https://letsencrypt.org/2020/12/21/extending-android-compatibility.html]

    正在连接到 https://valid-isrgrootx1.letsencrypt.org/ 在Android M或更早版本上通过OkHttp连接失败,而在N或更高版本上连接正常。

        OkHttpClient client = new OkHttpClient();
    
        try {
          Request request = new Request.Builder()
                  .url("https://valid-isrgrootx1.letsencrypt.org/robots.txt")
                  .build();
          try (Response response = client.newCall(request).execute()) {
            assertTrue(response.code() == 200 || response.code() == 404);
            assertEquals(Protocol.HTTP_2, response.protocol());
          }
        } catch (SSLHandshakeException sslhe) {
          sslhe.printStackTrace();
        }
    
    javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:322)
    at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:320)
    at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:284)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:169)
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:258)
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:127)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:257)
    at okhttp3.RealCall.execute(RealCall.java:93)
    at okhttp.regression.LetsEncryptTest.sendRequest(LetsEncryptTest.java:133)
    at okhttp.regression.LetsEncryptTest.getFailsWithoutAdditionalCert(LetsEncryptTest.java:52)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:154)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
    at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)
    Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
    at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:318)
    at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:219)
    at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:114)
    at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:550)
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:318)
    ... 50 more
    Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
    ... 56 more
    
    1 回复  |  直到 4 年前
        1
  •  20
  •   Yuri Schimke    4 年前

    [编辑:如果您是来参加2021年1月的Let's Encrypt到期活动的,请先阅读本文https://letsencrypt.org/2020/12/21/extending-android-compatibility.html]

    作为一般建议,如果你需要向旧的Android设备添加根CA,这个例子展示了这是如何工作的。

    该问题与Let’s encrypt的ISRG根证书在2021年到期有关。此(测试)服务器正在使用替换证书,该证书仅在Android N(7.1.1)及更高版本上受支持。

    以下代码将在将来对lets-encrypt使用的根证书起作用。它建立在 okhttp-tls .

    注意:这些建议都不适用于CertificateInner,如果您选择也锁定证书,请与您的内部安全团队讨论您的策略。

        boolean androidNorEarlier = Build.VERSION.SDK_INT <= 25;
    
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
    
        if (androidNorEarlier) {
          // TODO: download fresh from https://letsencrypt.org/certs/isrgrootx1.pem
          String isgCert =
                  "-----BEGIN CERTIFICATE-----\n" +
                  "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" +
                  "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" +
                  "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" +
                  "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" +
                  "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" +
                  "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" +
                  "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" +
                  "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" +
                  "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" +
                  "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" +
                  "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" +
                  "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" +
                  "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" +
                  "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" +
                  "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" +
                  "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" +
                  "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" +
                  "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" +
                  "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" +
                  "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" +
                  "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" +
                  "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" +
                  "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" +
                  "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" +
                  "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" +
                  "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" +
                  "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" +
                  "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" +
                  "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" +
                  "-----END CERTIFICATE-----";
    
          CertificateFactory cf = CertificateFactory.getInstance("X.509");
          Certificate isgCertificate = cf.generateCertificate(new ByteArrayInputStream(isgCert.getBytes("UTF-8")));
    
          HandshakeCertificates certificates = new HandshakeCertificates.Builder()
                  .addTrustedCertificate((X509Certificate) isgCertificate)
                  // Uncomment to allow connection to any site generally, but could possibly cause
                  // noticeable memory pressure in Android apps.
    //              .addPlatformTrustedCertificates()
                  .build();
    
          builder.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager());
        }
    
        OkHttpClient client = builder.build();
    
        Request request = new Request.Builder()
                .url("https://valid-isrgrootx1.letsencrypt.org/robots.txt")
                .build();
        try (Response response = client.newCall(request).execute()) {
          assertTrue(response.code() == 200 || response.code() == 404);
          assertEquals(Protocol.HTTP_2, response.protocol());
        }
    

    新的主机证书由根签名 ISRG Root X1 CA。

     ./cft --host valid-isrgrootx1.letsencrypt.org           
    CN:     valid-isrgrootx1.letsencrypt.org
    Pin:    sha256/489aa1610850a89c720217b9d9dbdc7f80918119f32b88c2dd3bcfaf1de29079
    SAN:    valid-isrgrootx1.letsencrypt.org
    Key Usage: DigitalSignature, KeyEncipherment
    Ext Key Usage: serverAuth, clientAuth
    Authority Info Access:
        ocsp: http://ocsp.int-x3.letsencrypt.org
        caIssuers: http://cert.int-x3.letsencrypt.org/
    Valid:  2020-10-14T15:00:50Z..2021-01-12T15:00:50Z (1 months)
    CA: false
    
    CN:     Let's Encrypt Authority X3
    Pin:    sha256/60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18
    SAN:    <N/A>
    Key Usage: DigitalSignature, KeyCertSign, CRLSign
    Authority Info Access:
        ocsp: http://ocsp.root-x1.letsencrypt.org/
        caIssuers: http://cert.root-x1.letsencrypt.org/
    Valid:  2016-10-06T15:43:55Z..2021-10-06T15:43:55Z (10 months)
    CA: true Max Intermediate: 0
    
    CN:     ISRG Root X1 (signed by locally-trusted root)
    Pin:    sha256/0b9fa5a59eed715c26c1020c711b4f6ec42d58b0015e14337a39dad301c5afc3
    SAN:    <N/A>
    Key Usage: KeyCertSign, CRLSign
    Valid:  2015-06-04T11:04:38Z..2035-06-04T11:04:38Z (14 years)
    CA: true
    
    Strict Transport Security: max-age=604800
    
    OCSP status: GOOD
    

    现有证书由以下人员签署 DST Root CA X3 2021年9月到期。

    $  ./cft --host letsencrypt.org 
    CN:     lencr.org
    Pin:    sha256/b93116ebda5e22efe089e7710b221557eb80a2e13c60a58687c0ce0369afd68a
    SAN:    lencr.org, letsencrypt.org, www.lencr.org, www.letsencrypt.org
    Key Usage: DigitalSignature, KeyEncipherment
    Ext Key Usage: serverAuth, clientAuth
    Authority Info Access:
        ocsp: http://ocsp.int-x3.letsencrypt.org
        caIssuers: http://cert.int-x3.letsencrypt.org/
    Valid:  2020-11-03T21:00:55Z..2021-02-01T21:00:55Z (2 months)
    CA: false
    
    CN:     Let's Encrypt Authority X3
    Pin:    sha256/60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18
    SAN:    <N/A>
    Key Usage: DigitalSignature, KeyCertSign, CRLSign
    Authority Info Access:
        ocsp: http://isrg.trustid.ocsp.identrust.com
        caIssuers: http://apps.identrust.com/roots/dstrootcax3.p7c
    Valid:  2016-03-17T16:40:46Z..2021-03-17T16:40:46Z (4 months)
    CA: true Max Intermediate: 0
    
    CN:     DST Root CA X3 (signed by locally-trusted root)
    Pin:    sha256/563b3caf8cfef34c2335caf560a7a95906e8488462eb75ac59784830df9e5b2b
    SAN:    <N/A>
    Key Usage: KeyCertSign, CRLSign
    Valid:  2000-09-30T21:12:19Z..2021-09-30T14:01:15Z (10 months)
    CA: true
    
    Strict Transport Security: max-age=31536000
    
    OCSP status: GOOD
    

    可用修复程序

    姓名 版本 API水平 ISRG根X1 网络
    安全
    config
    OkHttp 3.12
    +修复
    无官方代号 1. 1.
    1.1 2.
    纸杯蛋糕 1.5 3.
    甜甜圈 1.6 4.
    Eclair 2.0 2.1 5 7
    佛罗约 2.2 2.2.3 8.
    姜饼 2.3 2.3.7 9 10 十、
    蜂窝 3.0 3.2.6 11 13 十、
    冰淇淋三明治 4.0 4.0.4 14 15 十、
    果冻豆 4.1 4.3.1 16 18 十、
    奇巧 4.4 4.4.4 19 20 十、
    棒棒糖 5.0 5.1.1 21 22 十、
    棉花糖 6.0 6.0.1 23 十、
    牛轧糖 7.0 7.1.2 24 25 7.1.1+ 十、 十、
    奥利奥 8.0 8.1 26 27 十、 十、 十、
    馅饼 9 28 十、 十、 十、
    安卓10 10 29 十、 十、 十、
    安卓11 11 30 十、 十、 十、
        2
  •  6
  •   Yuri Schimke    4 年前

    在Android Nougat(7)上,您可以添加/更新 android:networkSecurityConfig 在AndroidManifest.xml中指向本地证书。

    https://www.danieldent.com/blog/android-apps-lets-encrypt-dst-root-expiry/

    <network-security-config>
        <base-config cleartextTrafficPermitted="false">
            <trust-anchors>
                <certificates src="@raw/isrg_root_x2" />
                <certificates src="@raw/isrg_root_x1" />
                <certificates src="system" />
            </trust-anchors>
        </base-config>
    </network-security-config>
    
        3
  •  4
  •   Yuri Schimke    4 年前

    对于2021年1月的Let’s Encrypt到期事件,不再需要这样做。

    阅读 https://letsencrypt.org/2020/12/21/extending-android-compatibility.html 在申请之前。

        4
  •  3
  •   Fradow    4 年前

    基于@Yuri Schimke的代码的替代答案,该代码适用于OkHttp 3.1.2、Volley和HttpsUrlConnection。

    因为那些没有 HandshakeCertificates 做繁重工作的班级,你需要自己做:

    public TrustManagerFactory getTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException {
        //Note: hardcode it, because the device might not even have the certificate to download it over https
        String isgCert =
                "-----BEGIN CERTIFICATE-----\n" +
                        "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" +
                        "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" +
                        "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" +
                        "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" +
                        "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" +
                        "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" +
                        "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" +
                        "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" +
                        "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" +
                        "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" +
                        "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" +
                        "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" +
                        "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" +
                        "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" +
                        "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" +
                        "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" +
                        "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" +
                        "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" +
                        "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" +
                        "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" +
                        "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" +
                        "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" +
                        "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" +
                        "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" +
                        "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" +
                        "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" +
                        "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" +
                        "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" +
                        "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" +
                        "-----END CERTIFICATE-----";
    
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        Certificate isgCertificate = cf.generateCertificate(new ByteArrayInputStream(isgCert.getBytes(StandardCharsets.UTF_8)));
    
        // Create a KeyStore containing our trusted CAs
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);
        keyStore.setCertificateEntry("isrg_root", isgCertificate);
    
        //Default TrustManager to get device trusted CA
        TrustManagerFactory defaultTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        defaultTmf.init((KeyStore) null);
    
        X509TrustManager trustManager = (X509TrustManager) defaultTmf.getTrustManagers()[0];
        int number = 0;
        for(Certificate cert : trustManager.getAcceptedIssuers()) {
            keyStore.setCertificateEntry(Integer.toString(number), cert);
            number++;
        }
    
        // Create a TrustManager that trusts the CAs in our KeyStore
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);
        return tmf;
    }
    

    然后你可以为OkHttp配置它:

    public static OkHttpClient getHttpClient() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
    
        if (Build.VERSION.SDK_INT <= 25) {
            TrustManagerFactory tmf = getTrustManagerFactory();
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);
            builder.sslSocketFactory(context.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0]);
        }
        return builder.build();
    }
    

    对于Volley:

    public RequestQueue configureRequestQueue(Context context) {
        if (Build.VERSION.SDK_INT <= 25) {
            try {
                TrustManagerFactory tmf = NetworkUtility.getTrustManagerFactory();
                // Create an SSLContext that uses our TrustManager
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, tmf.getTrustManagers(), null);
    
                HurlStack httpStack = new HurlStack(null, sslContext.getSocketFactory());
                return Volley.newRequestQueue(context, httpStack);
            } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
                e.printStackTrace();
            }
        }
        return Volley.newRequestQueue(context);
    }
    

    对于HttpsURL连接:

    public void configureHttpsUrlConnection() {
        if (Build.VERSION.SDK_INT <= 25) {
            try {
                TrustManagerFactory tmf = getTrustManagerFactory();
                SSLContext context = SSLContext.getInstance("TLS");
                context.init(null, tmf.getTrustManagers(), null);
                HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
            } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | IOException | CertificateException e) {
                e.printStackTrace();
            }
        }
    }
    
        5
  •  2
  •   Aitor Viana    4 年前

    对于Glide,只需注册OkHttp ModelLoaderFactory 对于 GlideUrl 模型类。

    @GlideModule
    class GlobalGlideModule : AppGlideModule() {
        override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
                try {
    
                    val isrgRootX1 = ... // X509Certificate
    
                    val handshakeCertificates = HandshakeCertificates.Builder()
                        .addTrustedCertificate(isrgRootX1)
                        .addPlatformTrustedCertificates()
                        .build()
    
                    val okHttpClient = OkHttpClient.Builder()
                        .sslSocketFactory(handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager())
                        .build()
    
                    // use our custom okHttp instead of default HTTPUrlConnection
                    registry.replace(
                        GlideUrl::class.java,
                        InputStream::class.java,
                        OkHttpUrlLoader.Factory(okHttpClient)
                    )
                } catch (t: Throwable) {
                    super.registerComponents(context, glide, registry)
                }
            } else {
                super.registerComponents(context, glide, registry)
            }
        }
    }
    

    参见示例 in this PR .