Java11实现X509TrustManager报错SSLHandshakeException分析及解决办法

    最近在学习用java实现HTTPS协议,Client用HttpsURLConnection来发起访问。传给SSLContext的TrustManager是我自己实现的一个简单的X509TrustManager,按照以往经验将checkClientTrusted和checkServerTrusted重写为空函数,getAcceptedIssuers方法直接返回null。本以为不会再有认证服务器的错误,但依然报错如下:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: No subject alternative names present
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:352)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:295)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:290)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
	at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
	at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:171)
	at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1359)
	at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1268)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:401)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:373)
	at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1592)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1520)
	at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:250)
	at com.company.SimpleHTTPClient.main(SimpleHTTPClient.java:52)
Caused by: java.security.cert.CertificateException: No subject alternative names present
	at java.base/sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142)
	at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:101)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:429)
	at java.base/sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:1544)
	at java.base/sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:1511)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:638)
	... 17 more

我使用的Java版本是Java 11.0.10,先给出解决办法:

X509TrustManager trustManager = new X509TrustManager() {

    ...

}

改为

X509TrustManager trustManager = new X509ExtendedTrustManager() {

    ...

}

有兴趣了解分析过程的小伙伴可以接着看下面内容。

    调试观察函数调用栈,发现调用CertificateMessage.checkServerCerts()方法时,会调用tm.checkServerTrusted(),但这个tm是sun.security.ssl.X509TrustManagerImpl,而不是我传进去自己实现的X509TrustManager。

    为了找出原因,再跟踪SSLContext.init()过程。sun.security.ssl.SSLContextImpl.engineInit()方法中通过this.trustManager = this.chooseTrustManager(tm)的带SSL上下文的trustManager。下面是chooseTrustManager方法的代码。

private X509TrustManager chooseTrustManager(TrustManager[] tm) throws KeyManagementException {
        for(int i = 0; tm != null && i < tm.length; ++i) {
            if (tm[i] instanceof X509TrustManager) {
                if (SunJSSE.isFIPS() && !(tm[i] instanceof X509TrustManagerImpl)) {
                    throw new KeyManagementException("FIPS mode: only SunJSSE TrustManagers may be used");
                }

                if (tm[i] instanceof X509ExtendedTrustManager) {
                    return (X509TrustManager)tm[i];
                }

                return new AbstractTrustManagerWrapper((X509TrustManager)tm[i]);
            }
        }

        return DummyX509TrustManager.INSTANCE;
}

可以看到,如果我们实现的是X509TrustManager,而不是一个X509ExtendedTrustManager,调用返回new AbstractTrustManagerWrapper((X509TrustManager)tm[i]);因此我需要实现的是X509ExtendedTrustManager而不止是X509TrustManager。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值