Subject Alternate Name is null when using TLS endpoint validation

Context

I’m attempting to create an SSLEngine that will validate that the subject alternate name is correct for the certificate returned by the server. I understand that java does not do this automatically and must be asked to do this. I’ve set up my SSLEngine as follows

     //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;

Without the line sslParams.setEndpointIdentificationAlgorithm("HTTPS"); the connection succeeds for any valid signed certificate (even if its subject alternate name is incorrect).

However once I add that line I get an exception

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$4.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$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.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$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.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$2.run(WaitDispatchSupport.java:190)
    at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:235)
    at java.desktop/java.awt.WaitDispatchSupport$4.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$4.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$1.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$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.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$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.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$2.run(WaitDispatchSupport.java:190)
    at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:235)
    at java.desktop/java.awt.WaitDispatchSupport$4.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$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.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$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.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$0(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

Interestingly, debugging whats going on within HostNameChecker#match shows that the X509Certificate doesn’t have any subject alternate names. But the toString representation of it does.

enter image description here

These are internal network certificates created by me, signed by a local root CA, so its very possibly I’ve created the certificates wrong

Question

How should I be setting up an sslEngine to validate the domain name as well as the certificate validation? Why am I getting this null pointer exception?

Answer

Looks like the problem was that in hostname checker the “expected name” was to be supplied by me, not as I had imagined based on the URL I was connecting to.

The correct way for me to create the SSL engine was as follows

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

It is up to my code to know what host and port I expect to be communicating with (which luckily I do in my case)

Leave a Reply

Your email address will not be published. Required fields are marked *