javax.net.ssl.SSLPeerUnverifiedException:Android 4.x 和 5.x 上没有对等证书

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate on Android 4.x and 5.x

我 运行 我的应用程序在 Android 4.4.2 上,它抛出这个错误:

07-03 08:43:59.255 21643-21803/com.myapp W/System.err: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at com.android.org.conscrypt.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:146)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:388)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:165)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at com.myapp.Util.Util.run(Util.java:242)
07-03 08:43:59.255 21643-21803/com.myapp W/System.err:     at java.lang.Thread.run(Thread.java:841)
07-03 08:43:59.475 21643-21643/com.myapp E/ViewRootImpl: sendUserActionEvent() mView == null

它在 Android 6.x 及更高版本上正常工作。只有 Android 4.x 和 5.x 失败。我没有尝试过 Android 3.x 及以下版本,但如果我只能修复 Android 4.x 和 5.x 就可以了。有趣的是,它过去曾经工作,突然间它停止工作,而无需我更改应用程序的源代码。当我去 https://www.digicert.com/help/ 测试我的服务器 SSL 证书时,一切都通过了:

DNS resolves <Domain> to <IP address>
HTTP Server Header: Apache

SSL certificate
Common Name =

Subject Alternative Names = <subdomain>.<Domain>, <Domain>, m.<Domain>, www.<Domain>

Issuer = COMODO RSA Extended Validation Secure Server CA

Serial Number = 5A34235B2A2B53B35354232B123B23C5

SHA1 Thumbprint = 98C357AC34A23B232134B2A4C23A2B23AC23A532

Key Length = 2048

Signature algorithm = SHA256 + RSA (excellent)

Secure Renegotiation: Supported

SSL Certificate has not been revoked
OCSP Staple:    Good
OCSP Origin:    Good
CRL Status: Good

SSL Certificate expiration
The certificate expires May 24, 2019 (325 days from today)

Certificate Name matches <Domain>

Subject 
Valid from 11/Jun/2017 to 24/May/2019
Issuer  COMODO RSA Extended Validation Secure Server CA


Subject COMODO RSA Extended Validation Secure Server CA
Valid from 12/Feb/2012 to 11/Feb/2027
Issuer  COMODO RSA Certification Authority


Subject COMODO RSA Certification Authority
Valid from 30/May/2000 to 30/May/2020
Issuer  AddTrust External CA Root
SSL Certificate is correctly installed
Congratulations! This certificate is correctly installed.

我的 SSL 证书看起来不错。我想知道为什么 Android 4.x 和 5.x 会抛出这个 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 错误。

更新 1:SSL 报告显示我的 SSL 证书一切正常。为什么 Android 抱怨?这是报告:

我正在使用 https://www.ssllabs.com/ssltest/

更新 2:阅读 https://developer.android.com/reference/javax/net/ssl/SSLPeerUnverifiedException.html#SSLPeerUnverifiedException(java.lang.String),我的理解是服务器无法识别自己。为什么?我猜是因为理论上服务器缺少所需的对等证书。如果您看到在更新 1 中使用 https://www.ssllabs.com/ssltest/ 时,SSL 报告显示我的服务器正确通过了证书测试,这听起来很矛盾。

更新 3:我的证书颁发机构 (CA) 是 COMODO。我的 SSL 报告显示我的 SSL 证书一切正常:https://www.ssllabs.com/ssltest/analyze.html?d=cuponclub.net. When I make a server request from a web browser, everything is good: https://cuponclub.net/San-salvador/deals/json_index/。看看 HTTPS 连接是如何成功的,浏览器的证书是有效的,一切看起来都很完美。 Android 6.x 及以上没有错误,但为什么 Android 4.x 和 5.x 一直抱怨我的 SSL 证书,在 Android 当我尝试在 Android 4.x 或 5.x?:

上编译我的应用程序时,Studio 记录
W/System.err: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
W/System.err:     at com.android.org.conscrypt.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:146)
W/System.err:     at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)
W/System.err:     at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:388)
W/System.err:     at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:165)
W/System.err:     at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
W/System.err:     at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
W/System.err:     at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
W/System.err:     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
W/System.err:     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
W/System.err:     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
W/System.err:     at com.couponclub.Util.Util.run(Util.java:242)
W/System.err:     at java.lang.Thread.run(Thread.java:841)

更新 4:查看 ,他们认为问题可能与服务器名称指示 (SNI) 有关。当我使用 https://www.ssllabs.com/ssltest/ 分析我的网站时,我在结果中看到了这个红色的:

替代名称 [................] 不匹配 信任 否 不信任 Mozilla 苹果 Android Java Windows

也许我的问题与此服务器名称指示 (SNI) 有关或缺少它?

更新 5:我从 https://www.ssllabs.com/ssltest/ 得到这个 "No SNI" 红色的。也许这是导致问题的原因?

更新 6Certificate #2 可以忽略我的问题的影响,因为它适用于不同的域。我只关心 Certificate #1.

的结果

更新 7:网络托管公司支持团队和 SSL 证书颁发者支持团队确认 SSL 证书及其安装一切正常,他们认为这可能是关于 Android SDK for Android 4.x 和 5.x 混淆的问题,而不是 SSL 证书的实际问题。使用 SSL 证书一切看起来都很好。

更新 8:我在服务器上禁用了 TLS 1.1 和更早版本以符合 SSL 标准,我开始怀疑错误的原因是 Android 4.x 和 5.x 可能希望看到支持 TLS 1.1 及更早版本的库,当它不存在时,即使我有 TLS 1.2,它也会自动抛出 SSL 错误。这是我的一个假设。例如,我在 https://help.salesforce.com/articleView?id=000231452&type=1 阅读有关 "Preparing mobile apps for TLS 1.0 deprecation" 的信息,他们是这样说的:

Android. Unfortunately, some work might be required to ensure that your Android applications don’t break when TLS 1.0 is disabled on a Salesforce instance. We have a fix that enforces TLS 1.1 and 1.2 on Mobile SDK Android applications.

因此他们意识到禁用 TLS 1.0 可能会使某些 Android 应用程序无法使用 Salesforce 实例,并且他们知道在禁用 TLS 1.0 后必须做一些工作来解决这个问题。同样,我开始怀疑在我的情况下,问题可能是我的服务器缺乏对 TLS 1.0 及更早版本的支持。

UPDATE 9:在我向 COMODO CA 支持解释了我在 UPDATE 8 中提到的内容后,这是他们的回复:

Thank you for contacting Comodo CA support. Android versions 4.3 and earlier do not support TLS 1.2. As you have enabled only TLS 1.2 on the server, no connections will be established to your server from those legacy devices. Certificate check happens only after establishing the connection.

更新 10:一切似乎都表明发生在我身上的事情就是 https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/:

中所解释的

I recently had to work on an Android application that consumed an API which removed support for TLS 1.0 connections for security reasons. The Android documentation for SSLSocket says that TLS 1.1 and TLS 1.2 is supported within android starting API level 16+ (Android 4.1, Jelly Bean). But it is by default disabled but starting with API level 20+ (Android 4.4 for watch, Kitkat Watch and Android 5.0 for phone, Lollipop) they are enabled. But it is very hard to find any documentation about how to enable it for phones running 4.1 for example.

更新 11:查看 https://github.com/wireapp/wire-android/issues/1450, this is for example how wire.com,一个流行的协作平台,证明缺乏对 Android 4.x 的支持:

Hi, thank you for your feedback. Unfortunately, upgrading to TLSv1.2 was a necessary step to increase the security of the communications between our servers and the clients, and it is not really something we are open to reconsider. As with all decisions that involve dropping support for older versions of something, there will be people whose devices will be left behind. However, given the gains in terms of security and given the very small number of people that will lose support, we made the decision to upgrade. Know that we're not alone in dropping support for TLS pre-1.2, as Github itself has done the same. On top of all these reasons, Android 4.x phones are quite old devices that most likely are not receiving any security updates from their manufacturers, so if you are a "privacy-aware" user I would recommend getting something more recent (Android 5 came out in 2014). We're sorry if you felt like you were left behind, but please understand that this decision was taken with security in mind as the most important factor.

更新 12:有趣 link 关于问题是什么以及如何解决它:https://medium.com/tech-quizlet/working-with-tls-1-2-on-android-4-4-and-lower-f4f5205629a。正如您在我对自己问题的回答中看到的那样,我已经继续前进了,但也许这个 link 可能对其他人有用。

根据您的描述,这似乎是 Android 问题。可能已在较新版本中修复。如果我不得不猜测,我会说你的证书解析在 Android 端的某个地方静默失败,片刻后 当 Android 尝试 getPeerCertificates() 它抛出异常,因为它们没有被初始化(由于我推测的静默失败)。如果失败不是无声的,您可能会尝试在问题发生之前找到一些堆栈跟踪。

您也可以尝试通过调整您的网站以适应旧 Android OS 来解决此问题。我没有好的建议或解决方案,但我会首先将您的证书与来自旧 Android OS 观点的网站的证书进行比较。

原因: TLS 1.2 在 Android 4.x 上默认禁用。 解决方案: 由于启用 TLS 1.2 不像在 build.gradle 文件中定义我想使用的 TLS 版本那么简单,我决定不支持 Android 4.x 对于我的应用程序,遵循 wire.com 使用的相同理由以及我在问题的 UPDATE 11 中复制的理由。