7
votes

CertPathValidatorException se connectant à un hôte Let's Encrypt sur Android M ou version antérieure

La connexion à https://valid-isrgrootx1.letsencrypt.org/ via OkHttp sur Android M ou version antérieure échoue, tandis que la connexion fonctionne sur N ou version ultérieure.

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
    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();
    }


0 commentaires

3 Réponses :


9
votes

Le problème concerne l'expiration connue du certificat racine ISRG pour Let's encrypt en 2021. Ce serveur (de test) utilise le certificat de remplacement qui n'est pris en charge que sur les versions d'Android N (7.1.1) et versions ultérieures.

Lisez https://letsencrypt.org/2020/11/06/own-two-feet.html pour le contexte.

Le code suivant fonctionnera avec le certificat racine utilisé par permet de chiffrer à l'avenir. Il s'appuie sur okhttp-tls .

Remarque: aucun de ces conseils ne s'applique en combinaison avec CertificatePinner, si vous choisissez également d'épingler des certificats, veuillez discuter de votre stratégie avec votre équipe de sécurité interne.

$  ./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

Les nouveaux certificats d'hôte sont root signés par l' ISRG Root X1 certification ISRG Root X1 .

 ./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

Les certificats existants sont signés par DST Root CA X3 qui expire en septembre 2021.

    boolean androidNorEarlier = Build.VERSION.SDK_INT <= 25;

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

    if (androidNorEarlier) {
      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());
    }

Correctifs disponibles

correctifs de version d'Excel


8 commentaires

Pouvez-vous créer un lien pour plus d'informations sur le problème de la pression de la mémoire? Est-ce parce que okhttp copiera toutes les autorités de certification racine du système dans la mémoire?


Rien de spécifique, mais quelles que soient les optimisations qu'un fournisseur intégré pourrait faire pour éviter de charger tous les certificats en mémoire, nous les annulerons. Si chaque application Android que vous exécutez fait cela, cela peut avoir un impact important. Sur l'émulateur Android 22, j'obtiens 163 certificats. Je suppose donc qu'un petit nombre de mégaoctets au maximum par instance de client?


Quelle est la solution de contournement pour, par exemple, un navigateur utilisant WebView fonctionnant sur Lollipop ou Mashmallow?


@CraigRussell pas sûr, vous devriez demander sur le forum LE. Je soupçonne que si vous testez cela ne fonctionnera pas et vous voudrez peut-être utiliser un navigateur externe pour afficher ce contenu. Mais je ne sais pas.


L'AndroidKeyStore est disponible à partir d'Android 4.3, peut-être qu'une application peut simplement installer les informations de certificats?


D'où vient cette classe HandshakeCertificates ?


okhttp-tls, je vais ajouter un commentaire.


Merci beaucoup! :)



0
votes

Ce code ci-dessus doit inclure Android 7 car le certificat n'est disponible que sur Android 7.1.1 et supérieur.

Un exemple d'appareil affecté est le Honor 8, qui n'a qu'Android 7.0 et n'a pas obtenu la mise à jour 7.1.1.


1 commentaires

adressé cela et ajouté une solution manifeste.



2
votes

Sur Android Nougat (7), vous pouvez ajouter / mettre à jour android:networkSecurityConfig networkSecurityConfig dans AndroidManifest.xml pour pointer vers un certificat local.

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>


0 commentaires