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(); }
3 Réponses :
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" + "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\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
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! :)
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.
adressé cela et ajouté une solution manifeste.
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>