[edit:如果您在这里参加2021年1月起的Let's Encrypt到期事件,请先阅读https://letsencrypt.org/2020/12/21/extending-android-compatibility.html]
正在连接到https://valid-isrgrootx1.letsencrypt.org/在安卓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
[edit:如果您在这里参加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(及更高版本上支持的替换证书
以下代码将来将针对letsencrypt使用的根证书工作。它建立在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" +
"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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());
}
新的主机证书由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 | 网络OkHttp 3.12 +修复 | |
---|---|---|---|---|
无官方代号 | 1 | <1>|||
1.1 | 2 | 纸杯蛋糕|||
甜甜圈 | 1.6 | >|||
Eclair | 2.0–2.1 | 5–7 | ||
Froyo | 2.2–2.2.3 | |||
姜饼 | 2.3.7 | |||
蜂窝 | 3.0–3.2.6 | <11–13>|||
冰淇淋三明治 | 4.0–4.0.4 | 14–15 | X果冻豆 | |
KitKat | 4.4–4.4.4 | 19–20 | 棒棒糖||
棉花糖 | 6.0–6.0.1 | 23 | ||
牛轧糖 | 7.0–7.1.2 | |||
奥利奥 | 8.0–8.1 | 26–27 | X | X|
饼图 | 9 | |||
安卓10 | ||||
Android 11 | 11 | 30 | XX
在Android Nougat(7(上,您可以在AndroidManifest.xml中添加/更新android:networkSecurityConfig
以指向本地证书。
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>
对于2021年1月起的Let's Encrypt到期事件,不再需要这样做。
读取https://letsencrypt.org/2020/12/21/extending-android-compatibility.html申请之前。
基于@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" +
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwn" +
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vhn" +
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4n" +
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJun" +
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYn" +
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygcn" +
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+n" +
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6Un" +
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWn" +
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHn" +
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCn" +
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvn" +
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnn" +
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnn" +
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwn" +
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIn" +
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVn" +
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqn" +
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLn" +
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQn" +
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKn" +
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5n" +
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Urn" +
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCn" +
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcn" +
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxqn" +
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAn" +
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57dn" +
"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);
}
对于HttpsUrlConnection:
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();
}
}
}
对于Glide,只需为GlideUrl
模型类注册一个OkHttpModelLoaderFactory
。
@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)
}
}
}
参见本PR.中的示例