SSL证书固定在Android 9上不再起作用

7
我正在使用以下证书绑定代码,这段时间一直有效(为了简洁起见,编辑掉了错误处理):
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) {
        // ...
    }
}

这基本上是由官方文档提供的代码。然后SocketFactory的使用如下:

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 ...

看起来安卓9有些变化,但目前我还没有找到任何关于这种行为的信息。我的观点如下:

  • 也许这种证书固定方式已经被弃用
  • 可能安卓9将不再验证使用SHA1证书的域名

还有其他的想法吗?


根据堆栈跟踪,你的情况是主机名未经验证。例如:example.com 与 domain.com。我有类似的问题。当证书使用通配符(例如 cn=*.domain.com)时,固定证书无效。因此,我的 my.domain.com 被拒绝了。 - Anton
很抱歉关于“domain.com”的混淆,它已根据SO准则进行了编辑,但有一个被遗漏了。应用程序中的实际域名在两种情况下完全匹配,而不是通配符。 - executifs
我必须创建一个新的证书,并添加主体备用名称,例如SAN DNS:*.domain.com。现在它已经可以使用了。看起来Android 9对CN验证施加了某些限制。 - Anton
你能澄清一下你需要添加什么作为SAN吗? - executifs
2个回答

5
我刚遇到了同样的问题。根据Android 9变更日志,对于没有SAN的证书,这是预期的:
RFC 2818描述了两种方法来匹配域名和证书 - 使用主题备用名称(SAN)扩展中的可用名称,或者在没有SAN扩展的情况下回退到公共名称(CN)。但是,在RFC 2818中,回退到CN已被弃用。因此,Android不再回退到使用CN。为了验证主机名,服务器必须呈现具有匹配SAN的证书。不包含与主机名匹配的SAN的证书不再受信任。
来源:使用证书进行主机名验证

0

为了验证主机名,服务器必须提供一个具有匹配SAN的证书。不包含与主机名匹配的SAN的证书将不再受信任。

我的问题是,如果证书是通配符,例如*.mydomain.com,对于类似online.mydomain.com的域,是否仍可使用SAN *.mydomain.com,或者通配符不再支持?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接