我正在使用以下证书固定代码,该代码已经工作了一段时间(为简洁起见,错误处理已编辑(:
private static SSLContext _ssl_context = null;
public static SSLSocketFactory get_ssl_socket_factory(Context context)
{
if (_ssl_context != null) {
return _ssl_context.getSocketFactory();
}
KeyStore keystore = get_keystore(context);
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(keystore);
_ssl_context = SSLContext.getInstance("TLS");
_ssl_context.init(null, tmf.getTrustManagers(), null);
return _ssl_context.getSocketFactory();
}
catch (GeneralSecurityException e) {
// ...
}
}
这或多或少是官方文档提供的代码。然后按如下方式使用套接字工厂:
if ("https".equals(target.getProtocol()) &&
"example.com".equals(target.getHost()) &&
huc instanceof HttpsURLConnection)
{
((HttpsURLConnection) huc).setSSLSocketFactory(
SSLHelper.get_ssl_socket_factory(this));
}
当我在 Android 8 设备上运行此代码时,一切按预期工作。但是,在我的 Android 9 模拟器上,会抛出异常:
E/App: https://example.com/page.html could not be retrieved! (Hostname example.com not verified:
certificate: sha1/VYMjxowFaRuZpycEoz+srAuXzlU=
subjectAltNames: [])
javax.net.ssl.SSLPeerUnverifiedException: Hostname example.com not verified:
certificate: sha1/VYMjxowFaRuZpycEoz+srAuXzlU=
subjectAltNames: []
at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:201)
at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:149)
at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:112)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:184)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:126)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:95)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:281)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:224)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:461)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:26)
at ...
Android 9 似乎发生了一些变化,但到目前为止,我还没有找到有关此行为的任何信息。我的想法如下:
- 也许这种执行证书固定的方法已被弃用
- 也许Android 9将不再使用SHA1证书验证域
还有其他想法吗?
我刚刚遇到了同样的问题。根据 Android 9 更新日志,对于没有 SAN 的证书,这是预期的:
RFC 2818 描述了两种将域名与证书匹配的方法 — 使用 subjectAltName (SAN( 扩展名中的可用名称,或者在没有 SAN 扩展名的情况下回退到公用名 (CN(。
但是,RFC 2818 中已弃用对 CN 的回退。出于这个原因,Android不再回退到使用CN。要验证主机名,服务器必须提供具有匹配 SAN 的证书。不包含与主机名匹配的 SAN 的证书不再受信任。
源: 使用证书进行主机名验证
要验证主机名,服务器必须提供具有匹配 SAN 的证书。不包含与主机名匹配的 SAN 的证书不再受信任。
我的查询是证书是否是通配符,例如*.mydomain.com
对于像online.mydomain.com
这样的域。SAN*.mydomain.com
是否正常工作或不再支持通配符?