使用 TLS 端点验证时主题备用名称为空

Subject Alternate Name is null when using TLS endpoint validation

上下文

我正在尝试创建一个 SSLEngine 来验证主题备用名称对于服务器返回的证书是否正确。我知道 java 不会自动执行此操作,必须要求执行此操作。我按如下方式设置了我的 SSLEngine

     //this keystore will provide client certificates, an optional part of TLS to identify the client.
     KeyStore personalKS = KeyStore.getInstance( "Windows-MY" ); //$NON-NLS-1$
     personalKS.load( null, null );

     KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );

     keyManagerFactory.init(  personalKS, null );
     SSLContext context = SSLContext.getInstance( "TLSv1.3" );

     SSLParameters sslParams = new SSLParameters();
     sslParams.setEndpointIdentificationAlgorithm("HTTPS");
     context.init( keyManagerFactory.getKeyManagers(), null, null );

     SSLEngine sslEngine = context.createSSLEngine();
     sslEngine.setSSLParameters( sslParams );

     return sslEngine;

没有 sslParams.setEndpointIdentificationAlgorithm("HTTPS"); 行,任何有效签名证书的连接都会成功(即使其主题备用名称不正确)。

但是,一旦我添加该行,我就会遇到异常

com.mirada.data.dicom.network.base.AssociationFailedException: Failed to connect
    at com.mirada.data.dicom.network.ServerAssociationConnector.waitForImpl(ServerAssociationConnector.java:328)
    at com.mirada.data.dicom.network.ServerAssociationConnector.waitFor(ServerAssociationConnector.java:183)
    at com.mirada.fusionxd.export.configuration.DicomConfigurationPage$ScpDialog.actionPerformed(DicomConfigurationPage.java:701)
    at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
    at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
    at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
    at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
    at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6632)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
    at java.desktop/java.awt.Component.processEvent(Component.java:6397)
    at java.desktop/java.awt.Container.processEvent(Container.java:2263)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5008)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2772)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)
    at java.desktop/java.awt.WaitDispatchSupport.run(WaitDispatchSupport.java:190)
    at java.desktop/java.awt.WaitDispatchSupport.run(WaitDispatchSupport.java:235)
    at java.desktop/java.awt.WaitDispatchSupport.run(WaitDispatchSupport.java:233)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:233)
    at java.desktop/java.awt.Dialog.show(Dialog.java:1070)
    at java.desktop/java.awt.Component.show(Component.java:1716)
    at java.desktop/java.awt.Component.setVisible(Component.java:1663)
    at java.desktop/java.awt.Window.setVisible(Window.java:1031)
    at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1005)
    at com.mirada.fusionxd.export.configuration.DicomConfigurationPage.actionPerformed(DicomConfigurationPage.java:319)
    at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
    at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
    at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
    at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
    at java.desktop/javax.swing.AbstractButton.doClick(AbstractButton.java:369)
    at java.desktop/javax.swing.AbstractButton.doClick(AbstractButton.java:349)
    at com.mirada.fusionxd.export.configuration.DicomConfigurationPage.mouseClicked(DicomConfigurationPage.java:248)
    at java.desktop/java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:278)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6635)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
    at java.desktop/java.awt.Component.processEvent(Component.java:6397)
    at java.desktop/java.awt.Container.processEvent(Container.java:2263)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5008)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4556)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2772)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)
    at java.desktop/java.awt.WaitDispatchSupport.run(WaitDispatchSupport.java:190)
    at java.desktop/java.awt.WaitDispatchSupport.run(WaitDispatchSupport.java:235)
    at java.desktop/java.awt.WaitDispatchSupport.run(WaitDispatchSupport.java:233)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:233)
    at java.desktop/java.awt.Dialog.show(Dialog.java:1070)
    at java.desktop/java.awt.Component.show(Component.java:1716)
    at java.desktop/java.awt.Component.setVisible(Component.java:1663)
    at java.desktop/java.awt.Window.setVisible(Window.java:1031)
    at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1005)
    at com.mirada.platform.core.ui.dialog.DialogUi.show(DialogUi.java:104)
    at com.mirada.platform.core.ui.dialog.StandardDialog.show(StandardDialog.java:171)
    at com.mirada.platform.core.ui.configuration.ConfigurationPageDialog.show(ConfigurationPageDialog.java:227)
    at com.mirada.fusionxd.application.action.PreferencesDialogAction.actionPerformed(PreferencesDialogAction.java:51)
    at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
    at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
    at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
    at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
    at java.desktop/javax.swing.AbstractButton.doClick(AbstractButton.java:369)
    at java.desktop/javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:1020)
    at java.desktop/javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:1064)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6632)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
    at java.desktop/java.awt.Component.processEvent(Component.java:6397)
    at java.desktop/java.awt.Container.processEvent(Container.java:2263)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5008)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2772)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.util.concurrent.ExecutionException: java.io.IOException
    at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:531)
    at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:512)
    at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:83)
    at com.mirada.data.dicom.network.AssociationEventHandler.get(AssociationEventHandler.java:104)
    at com.mirada.data.dicom.network.AssociationEventHandler.get(AssociationEventHandler.java:1)
    at com.mirada.data.dicom.network.ServerAssociationConnector.waitForImpl(ServerAssociationConnector.java:315)
    ... 131 more
Caused by: java.io.IOException
    at com.mirada.data.dicom.network.base.StateMachine.threadEntry(StateMachine.java:655)
    at com.mirada.data.dicom.network.base.StateMachine.lambda[=11=](StateMachine.java:187)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException
    at java.base/sun.net.util.IPAddressUtil.textToNumericFormatV4(IPAddressUtil.java:49)
    at java.base/sun.net.util.IPAddressUtil.isIPv4LiteralAddress(IPAddressUtil.java:241)
    at java.base/sun.security.util.HostnameChecker.isIpAddress(HostnameChecker.java:117)
    at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:95)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:459)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:434)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:291)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:141)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1307)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1204)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1151)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
    at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1065)
    at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1052)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:999)
    at com.mirada.data.dicom.network.base.StateMachine.readFromSocket(StateMachine.java:839)
    at com.mirada.data.dicom.network.base.StateMachine.readNext(StateMachine.java:747)
    at com.mirada.data.dicom.network.base.StateMachine.threadEntry(StateMachine.java:582)
    ... 5 more

有趣的是,调试 HostNameChecker#match 中发生的事情表明 X509Certificate 没有任何主题备用名称。但它的 toString 表示确实如此。

这些是我创建的内部网络证书,由本地根 CA 签名,所以很可能我创建的证书有误

问题

我应该如何设置 sslEngine 来验证域名和证书验证?为什么我会收到此空指针异常?

看起来问题是在主机名检查器中,“预期名称”是由我提供的,而不是我根据我正在连接的 URL 想象的那样。

我创建 SSL 引擎的正确方法如下

SSLEngine sslEngine = context.createSSLEngine(hostname, port);

我的代码知道我希望与之通信的主机和端口(幸运的是,在我的例子中我这样做了)