以编程方式记录 SSL 证书



我在野外有 2 台设备无法连接到我的 TLS v1.2 端点。所有其他设备似乎都可以,包括浏览器,PostMan和iOS设备。

这些设备运行的是Android 5和7(因此TLS v1.2支持应该没有问题(。

注意:这不是自签名证书。它由亚马逊签名。

立即想到的是:

  1. Android碎片化 - 也许是设备(一个是Kindle Fire 7( 未将正确的证书包含在操作系统中。它不会 是设备制造商第一次做出奇怪的决定 这会破坏功能。

  2. API 正在通过代理访问,实际上有一个中间人,被正确检测到。


修复 (1( 意味着捆绑我们的证书,并在我们的证书过期时导致常见问题。

我更愿意让用户安装一个调试版本,以确认 (1( 或 (2( 是问题所在。这样的构建将检查服务器/代理提供的SSL证书,并将其记录回给我。


网络框架:

  • 改造 v2.3.0
  • OkHttp v3.9.1

问题:

如何检查设备在到达我的端点时看到的 SSL 证书的信息?


@SangeetSuresh每条评论更新:

事实证明,抛出了 2 种不同的异常。

Kindle Fire 7"平板电脑(KFAUWI,OS 5.1.1(正在抛出我已经开始调查的平板电脑,这个问题应该关注。 即基本的SSL故障。

java.security.cert.CertPathValidatorException: 
Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:331)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:232)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:114)

LG设备(LG-SP200,OS 7.1.2(正在被对等方关闭连接,如果此处未解决,则应在新问题下解决:

javax.net.ssl.SSLHandshakeException: 
Connection closed by peer
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:299)

Robby Cornelissen在引用OkHttpResponse的评论中提供了基本答案:

信息应可从以下网址获得response.handshake().peerCertificates().

在给定有效握手的情况下,实施了一个简单的Interceptor来检查证书:

private static class SslCertificateLogger implements Interceptor {
public static final String TAG = "SSL";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
Log.d(TAG, "<-- HTTP FAILED: " + e);
throw e;
}
Handshake handshake = response.handshake();
if (handshake == null) {
Log.d(TAG, "no handshake");
return response;
}

Log.d(TAG, "handshake success");
List<Certificate> certificates = handshake.peerCertificates();
if (certificates == null) {
Log.d(TAG, "no peer certificates");
return response;
}
String s;
for (Certificate certificate : certificates) {
s = certificate.toString();
Log.d(TAG, s);
}
return response;
}
}

这通常会添加到OkHttpClient

OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new SslCertificateLogger())
.build();

Sangeet Suresh提出了类似的解决方案,引用了RetrofitResponse对象:

response?.raw()?.handshake()我认为这会对你有所帮助

这里的重要信息是,Retrofit 允许以这种方式访问原始 OkHttp 响应。

这不会在Interceptor中使用,而是在从 API 获取改造Response<>后,在实际的改造处理代码中用于更高级别。

将他的 Kotlin 解决方案转换回 Java 可能会产生这样的结果:

okhttp3.Response raw = httpResponse.raw();
if (raw != null) {
Handshake handshake = raw.handshake();
if (handshake != null) {
List<Certificate> certificates = handshake.peerCertificates();
if (certificates != null) {
for (Certificate certificate : certificates) {
Log.d(TAG, certificate.toString());
}
}
}
}

两种解决方案都可以正常工作,前提是handshake()不为空,即握手成功时。

鉴于这是对失败握手的调查,需要进一步的步骤来"信任所有证书"(仅限 NB 调试版本!

这已经被记录了很多次 - 这是一个这样的版本:

  • 不安全的 SSL 客户端(不要在生产中执行此操作(

最新更新