Configures the SSL engine to use Server Name Indication. This option is only useful to initiators.
-
Y N
-
N
+
EndpointIdentificationAlgorithm
+
Sets the endpoint identification algorithm. If the algorithm parameter is non-null, the endpoint identification/verification procedures must be handled during SSL/TLS handshaking. See
+ Endpoint Identification
+ Algorithm Names
+
+
diff --git a/quickfixj-core/src/main/java/quickfix/mina/acceptor/AbstractSocketAcceptor.java b/quickfixj-core/src/main/java/quickfix/mina/acceptor/AbstractSocketAcceptor.java
index 2b7cb2b359..93a10c98c9 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/acceptor/AbstractSocketAcceptor.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/acceptor/AbstractSocketAcceptor.java
@@ -23,6 +23,7 @@
import org.apache.mina.core.buffer.SimpleBufferAllocator;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.ssl.SslFilter;
import quickfix.Acceptor;
import quickfix.Application;
import quickfix.ConfigError;
@@ -45,7 +46,6 @@
import quickfix.mina.message.FIXProtocolCodecFactory;
import quickfix.mina.ssl.SSLConfig;
import quickfix.mina.ssl.SSLContextFactory;
-import quickfix.mina.ssl.SSLFilter;
import quickfix.mina.ssl.SSLSupport;
import javax.net.ssl.SSLContext;
@@ -132,10 +132,9 @@ private void installSSL(AcceptorSocketDescriptor descriptor,
log.info("Installing SSL filter for {}", descriptor.getAddress());
SSLConfig sslConfig = descriptor.getSslConfig();
SSLContext sslContext = SSLContextFactory.getInstance(sslConfig);
- SSLFilter sslFilter = new SSLFilter(sslContext);
- sslFilter.setUseClientMode(false);
+ SslFilter sslFilter = new SslFilter(sslContext);
sslFilter.setNeedClientAuth(sslConfig.isNeedClientAuth());
- sslFilter.setCipherSuites(sslConfig.getEnabledCipherSuites() != null ? sslConfig.getEnabledCipherSuites()
+ sslFilter.setEnabledCipherSuites(sslConfig.getEnabledCipherSuites() != null ? sslConfig.getEnabledCipherSuites()
: SSLSupport.getDefaultCipherSuites(sslContext));
sslFilter.setEnabledProtocols(sslConfig.getEnabledProtocols() != null ? sslConfig.getEnabledProtocols()
: SSLSupport.getSupportedProtocols(sslContext));
diff --git a/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java b/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java
index 49bbdf620f..bdbc10f459 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java
@@ -22,16 +22,12 @@
import org.apache.mina.core.session.IoSession;
import org.apache.mina.proxy.AbstractProxyIoHandler;
-import quickfix.mina.ssl.SSLFilter;
-
class InitiatorProxyIoHandler extends AbstractProxyIoHandler {
private final InitiatorIoHandler initiatorIoHandler;
- private final SSLFilter sslFilter;
- InitiatorProxyIoHandler(InitiatorIoHandler initiatorIoHandler, SSLFilter sslFilter) {
+ InitiatorProxyIoHandler(InitiatorIoHandler initiatorIoHandler) {
super();
this.initiatorIoHandler = initiatorIoHandler;
- this.sslFilter = sslFilter;
}
@Override
@@ -60,9 +56,6 @@ public void exceptionCaught(IoSession ioSession, Throwable cause) throws Excepti
}
@Override
- public void proxySessionOpened(IoSession ioSession) throws Exception {
- if (this.sslFilter != null) {
- this.sslFilter.initiateHandshake(ioSession);
- }
+ public void proxySessionOpened(IoSession ioSession) {
}
}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java b/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
index e6c97eeeb8..b3826b091f 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
@@ -24,6 +24,7 @@
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.ssl.SslFilter;
import org.apache.mina.proxy.ProxyConnector;
import org.apache.mina.transport.socket.SocketConnector;
import quickfix.ConfigError;
@@ -40,7 +41,6 @@
import quickfix.mina.message.FIXProtocolCodecFactory;
import quickfix.mina.ssl.SSLConfig;
import quickfix.mina.ssl.SSLContextFactory;
-import quickfix.mina.ssl.SSLFilter;
import quickfix.mina.ssl.SSLSupport;
import javax.net.ssl.SSLContext;
@@ -153,9 +153,9 @@ private void setupIoConnector() throws ConfigError, GeneralSecurityException {
boolean hasProxy = proxyType != null && proxyPort > 0 && socketAddresses[nextSocketAddressIndex] instanceof InetSocketAddress;
- SSLFilter sslFilter = null;
+ SslFilter sslFilter = null;
if (sslEnabled) {
- sslFilter = installSslFilter(ioFilterChainBuilder, !hasProxy);
+ sslFilter = installSslFilter(ioFilterChainBuilder);
}
ioFilterChainBuilder.addLast(FIXProtocolCodecFactory.FILTER_NAME, new ProtocolCodecFilter(new FIXProtocolCodecFactory()));
@@ -175,9 +175,7 @@ private void setupIoConnector() throws ConfigError, GeneralSecurityException {
);
proxyConnector.setHandler(new InitiatorProxyIoHandler(
- new InitiatorIoHandler(fixSession, sessionSettings, networkingOptions, eventHandlingStrategy),
- sslFilter
- ));
+ new InitiatorIoHandler(fixSession, sessionSettings, networkingOptions, eventHandlingStrategy)));
newConnector = proxyConnector;
}
@@ -188,16 +186,15 @@ private void setupIoConnector() throws ConfigError, GeneralSecurityException {
ioConnector = newConnector;
}
- private SSLFilter installSslFilter(CompositeIoFilterChainBuilder ioFilterChainBuilder, boolean autoStart)
+ private SslFilter installSslFilter(CompositeIoFilterChainBuilder ioFilterChainBuilder)
throws GeneralSecurityException {
final SSLContext sslContext = SSLContextFactory.getInstance(sslConfig);
- final SSLFilter sslFilter = new SSLFilter(sslContext, autoStart);
- sslFilter.setUseClientMode(true);
- sslFilter.setCipherSuites(sslConfig.getEnabledCipherSuites() != null ? sslConfig.getEnabledCipherSuites()
+ final SslFilter sslFilter = new SslFilter(sslContext);
+ sslFilter.setEnabledCipherSuites(sslConfig.getEnabledCipherSuites() != null ? sslConfig.getEnabledCipherSuites()
: SSLSupport.getDefaultCipherSuites(sslContext));
sslFilter.setEnabledProtocols(sslConfig.getEnabledProtocols() != null ? sslConfig.getEnabledProtocols()
: SSLSupport.getSupportedProtocols(sslContext));
- sslFilter.setUseSNI(sslConfig.isUseSNI());
+ sslFilter.setEndpointIdentificationAlgorithm(sslConfig.getEndpointIdentificationAlgorithm());
ioFilterChainBuilder.addLast(SSLSupport.FILTER_NAME, sslFilter);
return sslFilter;
}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLConfig.java b/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLConfig.java
index 667750f341..4d45fe9f8c 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLConfig.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLConfig.java
@@ -20,6 +20,7 @@
package quickfix.mina.ssl;
import java.util.Arrays;
+import java.util.Objects;
/**
* Groups together SSL related configuration.
@@ -36,58 +37,7 @@ public class SSLConfig {
private String[] enabledProtocols;
private String[] enabledCipherSuites;
private boolean needClientAuth;
- private boolean useSNI;
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- SSLConfig other = (SSLConfig) obj;
- if (!Arrays.equals(enabledCipherSuites, other.enabledCipherSuites))
- return false;
- if (!Arrays.equals(enabledProtocols, other.enabledProtocols))
- return false;
- if (keyManagerFactoryAlgorithm == null) {
- if (other.keyManagerFactoryAlgorithm != null)
- return false;
- } else if (!keyManagerFactoryAlgorithm.equals(other.keyManagerFactoryAlgorithm))
- return false;
- if (keyStoreName == null) {
- if (other.keyStoreName != null)
- return false;
- } else if (!keyStoreName.equals(other.keyStoreName))
- return false;
- if (!Arrays.equals(keyStorePassword, other.keyStorePassword))
- return false;
- if (keyStoreType == null) {
- if (other.keyStoreType != null)
- return false;
- } else if (!keyStoreType.equals(other.keyStoreType))
- return false;
- if (needClientAuth != other.needClientAuth)
- return false;
- if (trustManagerFactoryAlgorithm == null) {
- if (other.trustManagerFactoryAlgorithm != null)
- return false;
- } else if (!trustManagerFactoryAlgorithm.equals(other.trustManagerFactoryAlgorithm))
- return false;
- if (trustStoreName == null) {
- if (other.trustStoreName != null)
- return false;
- } else if (!trustStoreName.equals(other.trustStoreName))
- return false;
- if (!Arrays.equals(trustStorePassword, other.trustStorePassword))
- return false;
- if(useSNI != other.useSNI)
- return false;
- if (trustStoreType == null) {
- return other.trustStoreType == null;
- } else return trustStoreType.equals(other.trustStoreType);
- }
+ private String endpointIdentificationAlgorithm;
public String[] getEnabledCipherSuites() {
return enabledCipherSuites;
@@ -129,31 +79,12 @@ public String getTrustStoreType() {
return trustStoreType;
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.hashCode(enabledCipherSuites);
- result = prime * result + Arrays.hashCode(enabledProtocols);
- result = prime * result + ((keyManagerFactoryAlgorithm == null) ? 0 : keyManagerFactoryAlgorithm.hashCode());
- result = prime * result + ((keyStoreName == null) ? 0 : keyStoreName.hashCode());
- result = prime * result + Arrays.hashCode(keyStorePassword);
- result = prime * result + ((keyStoreType == null) ? 0 : keyStoreType.hashCode());
- result = prime * result + (needClientAuth ? 1231 : 1237);
- result = prime * result
- + ((trustManagerFactoryAlgorithm == null) ? 0 : trustManagerFactoryAlgorithm.hashCode());
- result = prime * result + ((trustStoreName == null) ? 0 : trustStoreName.hashCode());
- result = prime * result + Arrays.hashCode(trustStorePassword);
- result = prime * result + ((trustStoreType == null) ? 0 : trustStoreType.hashCode());
- return result;
- }
-
public boolean isNeedClientAuth() {
return needClientAuth;
}
- public boolean isUseSNI() {
- return useSNI;
+ public String getEndpointIdentificationAlgorithm() {
+ return endpointIdentificationAlgorithm;
}
public void setEnabledCipherSuites(String[] enabledCipherSuites) {
@@ -184,8 +115,8 @@ public void setNeedClientAuth(boolean needClientAuth) {
this.needClientAuth = needClientAuth;
}
- public void setUseSNI(boolean useSNI) {
- this.useSNI = useSNI;
+ public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
+ this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
}
public void setTrustManagerFactoryAlgorithm(String trustManagerFactoryAlgorithm) {
@@ -203,4 +134,33 @@ public void setTrustStorePassword(char[] trustStorePassword) {
public void setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SSLConfig sslConfig = (SSLConfig) o;
+ return needClientAuth == sslConfig.needClientAuth &&
+ Objects.equals(keyStoreName, sslConfig.keyStoreName) &&
+ Arrays.equals(keyStorePassword, sslConfig.keyStorePassword) &&
+ Objects.equals(keyManagerFactoryAlgorithm, sslConfig.keyManagerFactoryAlgorithm) &&
+ Objects.equals(keyStoreType, sslConfig.keyStoreType) &&
+ Objects.equals(trustStoreName, sslConfig.trustStoreName) &&
+ Arrays.equals(trustStorePassword, sslConfig.trustStorePassword) &&
+ Objects.equals(trustManagerFactoryAlgorithm, sslConfig.trustManagerFactoryAlgorithm) &&
+ Objects.equals(trustStoreType, sslConfig.trustStoreType) &&
+ Arrays.equals(enabledProtocols, sslConfig.enabledProtocols) &&
+ Arrays.equals(enabledCipherSuites, sslConfig.enabledCipherSuites) &&
+ Objects.equals(endpointIdentificationAlgorithm, sslConfig.endpointIdentificationAlgorithm);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(keyStoreName, keyManagerFactoryAlgorithm, keyStoreType, trustStoreName, trustManagerFactoryAlgorithm, trustStoreType, needClientAuth, endpointIdentificationAlgorithm);
+ result = 31 * result + Arrays.hashCode(keyStorePassword);
+ result = 31 * result + Arrays.hashCode(trustStorePassword);
+ result = 31 * result + Arrays.hashCode(enabledProtocols);
+ result = 31 * result + Arrays.hashCode(enabledCipherSuites);
+ return result;
+ }
}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLFilter.java b/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLFilter.java
deleted file mode 100644
index b9c036f9d2..0000000000
--- a/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLFilter.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*******************************************************************************
- * Copyright (c) quickfixengine.org All rights reserved.
- *
- * This file is part of the QuickFIX FIX Engine
- *
- * This file may be distributed under the terms of the quickfixengine.org
- * license as defined by quickfixengine.org and appearing in the file
- * LICENSE included in the packaging of this file.
- *
- * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
- * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE.
- *
- * See http://www.quickfixengine.org/LICENSE for licensing information.
- *
- * Contact ask@quickfixengine.org if any conditions of this licensing
- * are not clear to you.
- ******************************************************************************/
-
-package quickfix.mina.ssl;
-
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import javax.net.ssl.SSLContext;
-
-import javax.net.ssl.SSLException;
-import org.apache.mina.core.filterchain.IoFilterChain;
-import org.apache.mina.core.session.IoSession;
-import org.apache.mina.filter.ssl.SslFilter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * An extended SSL filter based on MINA {@link SslFilter} that applies
- * some adaptations.
- */
-public class SSLFilter extends SslFilter {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
- private boolean useSNI;
-
- public SSLFilter(SSLContext sslContext, boolean autoStart) {
- super(sslContext, autoStart);
- }
-
- public SSLFilter(SSLContext sslContext) {
- super(sslContext);
- }
-
- /**
- * Called from {@link SslFilter#onPreAdd} every time a new
- * session is created which makes it impossible to override enabled cipher
- * suites configuration.
- */
- @Override
- public void setEnabledCipherSuites(String[] cipherSuites) {
- }
-
- public void setCipherSuites(String[] cipherSuites) {
- super.setEnabledCipherSuites(cipherSuites);
- }
-
- /**
- * Called before filter is added into the chain.
- * We activate Server Name Indication if it is enabled in the session config.
- */
- @Override
- public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter)
- throws SSLException {
-
- if (useSNI) {
- IoSession session = parent.getSession();
- SocketAddress remoteAddress = session.getRemoteAddress();
-
- if (remoteAddress instanceof InetSocketAddress) {
- // activate the SNI support in the JSSE SSLEngine
- log.info("Activating TLS SNI support for peer address: {}", remoteAddress);
- session.setAttribute(PEER_ADDRESS, remoteAddress);
- }
- }
-
- super.onPreAdd(parent, name, nextFilter);
- }
-
- public void setUseSNI(boolean useSNI) {
- this.useSNI = useSNI;
- }
-}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLSupport.java b/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLSupport.java
index d821f2f64f..52550ad813 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLSupport.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/ssl/SSLSupport.java
@@ -39,7 +39,7 @@ public class SSLSupport {
public static final String SETTING_TRUST_MANAGER_FACTORY_ALGORITHM = "TrustManagerFactoryAlgorithm";
public static final String SETTING_TRUST_STORE_TYPE = "TrustStoreType";
public static final String SETTING_NEED_CLIENT_AUTH = "NeedClientAuth";
- public static final String SETTING_USE_SNI = "UseSNI";
+ public static final String SETTING_ENDPOINT_IDENTIFICATION_ALGORITHM = "EndpointIdentificationAlgorithm";
public static final String SETTING_ENABLED_PROTOCOLS = "EnabledProtocols";
public static final String SETTING_CIPHER_SUITES = "CipherSuites";
static final String DEFAULT_STORE_TYPE = "JKS";
@@ -111,7 +111,7 @@ public static SSLConfig getSslConfig(SessionSettings sessionSettings, SessionID
sslConfig.setEnabledCipherSuites(getEnabledCipherSuites(sessionSettings, sessionID));
sslConfig.setEnabledProtocols(getEnabledProtocols(sessionSettings, sessionID));
sslConfig.setNeedClientAuth(isNeedClientAuth(sessionSettings, sessionID));
- sslConfig.setUseSNI(isUseSNI(sessionSettings, sessionID));
+ sslConfig.setEndpointIdentificationAlgorithm(getEndpointIdentificationAlgorithm(sessionSettings, sessionID));
return sslConfig;
}
@@ -150,7 +150,7 @@ public static boolean isNeedClientAuth(SessionSettings sessionSettings, SessionI
return "Y".equals(getString(sessionSettings, sessionID, SETTING_NEED_CLIENT_AUTH, "N"));
}
- public static boolean isUseSNI(SessionSettings sessionSettings, SessionID sessionID) {
- return "Y".equals(getString(sessionSettings, sessionID, SETTING_USE_SNI, "N"));
+ public static String getEndpointIdentificationAlgorithm(SessionSettings sessionSettings, SessionID sessionID) {
+ return getString(sessionSettings, sessionID, SETTING_ENDPOINT_IDENTIFICATION_ALGORITHM, null);
}
}
diff --git a/quickfixj-core/src/test/java/quickfix/mina/ssl/SSLCertificateTest.java b/quickfixj-core/src/test/java/quickfix/mina/ssl/SSLCertificateTest.java
index 053dbb2018..6d31b9293f 100644
--- a/quickfixj-core/src/test/java/quickfix/mina/ssl/SSLCertificateTest.java
+++ b/quickfixj-core/src/test/java/quickfix/mina/ssl/SSLCertificateTest.java
@@ -22,6 +22,8 @@
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.filterchain.IoFilterChain;
import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.ssl.SslFilter;
+import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,6 +44,7 @@
import quickfix.mina.ProtocolFactory;
import quickfix.mina.SessionConnector;
+import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import java.lang.reflect.Field;
@@ -51,10 +54,14 @@
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import org.apache.mina.util.AvailablePortFinder;
import org.junit.After;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
public class SSLCertificateTest {
// Note: To diagnose cipher suite errors, run with -Djavax.net.debug=ssl:handshake
@@ -102,6 +109,111 @@ public void shouldAuthenticateServerCertificate() throws Exception {
}
}
+ /**
+ * Server certificate has Common Name = localhost and no Server Alternative Name extension.
+ */
+ @Test
+ public void shouldAuthenticateServerNameUsingServerCommonName() throws Exception {
+ int freePort = AvailablePortFinder.getNextAvailable();
+ TestAcceptor acceptor = new TestAcceptor(createAcceptorSettings("single-session/server-cn.keystore", false,
+ "single-session/empty.keystore", CIPHER_SUITES_TLS, "TLSv1.2", "JKS", "JKS", freePort));
+
+ try {
+ acceptor.start();
+
+ TestInitiator initiator = new TestInitiator(
+ createInitiatorSettings("single-session/empty.keystore", "single-session/client-cn.truststore",
+ CIPHER_SUITES_TLS, "TLSv1.2", "ZULU", "ALFA", Integer.toString(freePort), "JKS", "JKS"));
+
+ try {
+ initiator.start();
+
+ initiator.assertNoSslExceptionThrown();
+ initiator.assertLoggedOn(new SessionID(FixVersions.BEGINSTRING_FIX44, "ZULU", "ALFA"));
+ initiator.assertAuthenticated(new SessionID(FixVersions.BEGINSTRING_FIX44, "ZULU", "ALFA"),
+ new BigInteger("1683903911"));
+
+ acceptor.assertNoSslExceptionThrown();
+ acceptor.assertLoggedOn(new SessionID(FixVersions.BEGINSTRING_FIX44, "ALFA", "ZULU"));
+ acceptor.assertNotAuthenticated(new SessionID(FixVersions.BEGINSTRING_FIX44, "ALFA", "ZULU"));
+ } finally {
+ initiator.stop();
+ }
+ } finally {
+ acceptor.stop();
+ }
+ }
+
+ /**
+ * Server certificate has Common Name = server, but it has Server Alternative Name extension (DNS name).
+ */
+ @Test
+ public void shouldAuthenticateServerNameUsingSNIExtension() throws Exception {
+ int freePort = AvailablePortFinder.getNextAvailable();
+ TestAcceptor acceptor = new TestAcceptor(createAcceptorSettings("single-session/server-sni.keystore", false,
+ "single-session/empty.keystore", CIPHER_SUITES_TLS, "TLSv1.2", "JKS", "JKS", freePort));
+
+ try {
+ acceptor.start();
+
+ TestInitiator initiator = new TestInitiator(
+ createInitiatorSettings("single-session/empty.keystore", "single-session/client-sni.truststore",
+ CIPHER_SUITES_TLS, "TLSv1.2", "ZULU", "ALFA", Integer.toString(freePort), "JKS", "JKS"));
+
+ try {
+ initiator.start();
+
+ initiator.assertNoSslExceptionThrown();
+ initiator.assertLoggedOn(new SessionID(FixVersions.BEGINSTRING_FIX44, "ZULU", "ALFA"));
+ initiator.assertAuthenticated(new SessionID(FixVersions.BEGINSTRING_FIX44, "ZULU", "ALFA"),
+ new BigInteger("1683904647"));
+
+ acceptor.assertNoSslExceptionThrown();
+ acceptor.assertLoggedOn(new SessionID(FixVersions.BEGINSTRING_FIX44, "ALFA", "ZULU"));
+ acceptor.assertNotAuthenticated(new SessionID(FixVersions.BEGINSTRING_FIX44, "ALFA", "ZULU"));
+ } finally {
+ initiator.stop();
+ }
+ } finally {
+ acceptor.stop();
+ }
+ }
+
+ /**
+ * Server certificate has Common Name = server and no Server Alternative Name extension.
+ */
+ @Test
+ public void shouldFailWhenHostnameDoesNotMatchServerName() throws Exception {
+ int freePort = AvailablePortFinder.getNextAvailable();
+
+ TestAcceptor acceptor = new TestAcceptor(createAcceptorSettings("single-session/server-bad-cn.keystore", false,
+ "single-session/empty.keystore", CIPHER_SUITES_TLS, "TLSv1.2", "JKS", "JKS", freePort));
+
+ try {
+ acceptor.start();
+
+ TestInitiator initiator = new TestInitiator(
+ createInitiatorSettings("single-session/empty.keystore", "single-session/client-bad-cn.truststore",
+ CIPHER_SUITES_TLS, "TLSv1.2", "ZULU", "ALFA", Integer.toString(freePort), "JKS", "JKS", "HTTPS"));
+
+ try {
+ initiator.start();
+
+ initiator.assertSslExceptionThrown("No name matching localhost found", SSLHandshakeException.class);
+ initiator.assertNotLoggedOn(new SessionID(FixVersions.BEGINSTRING_FIX44, "ZULU", "ALFA"));
+ initiator.assertNotAuthenticated(new SessionID(FixVersions.BEGINSTRING_FIX44, "ZULU", "ALFA"));
+
+ acceptor.assertSslExceptionThrown("Received fatal alert: certificate_unknown", SSLHandshakeException.class);
+ acceptor.assertNotLoggedOn(new SessionID(FixVersions.BEGINSTRING_FIX44, "ALFA", "ZULU"));
+ acceptor.assertNotAuthenticated(new SessionID(FixVersions.BEGINSTRING_FIX44, "ALFA", "ZULU"));
+ } finally {
+ initiator.stop();
+ }
+ } finally {
+ acceptor.stop();
+ }
+ }
+
@Test
public void shouldAuthenticateServerAndClientCertificates() throws Exception {
int freePort = AvailablePortFinder.getNextAvailable();
@@ -446,19 +558,21 @@ static abstract class TestConnector {
private final SessionConnector connector;
private final CountDownLatch exceptionThrownLatch;
+ private final AtomicReference exception;
public TestConnector(SessionSettings sessionSettings) throws ConfigError {
this.connector = prepareConnector(sessionSettings);
this.exceptionThrownLatch = new CountDownLatch(1);
+ this.exception = new AtomicReference<>();
}
private SessionConnector prepareConnector(SessionSettings sessionSettings) throws ConfigError {
SessionConnector sessionConnector = createConnector(sessionSettings);
sessionConnector.setIoFilterChainBuilder(chain -> chain.addFirst("Exception handler", new IoFilterAdapter() {
@Override
- public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause)
- throws Exception {
+ public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) {
LOGGER.info("exceptionCaught", cause);
+ exception.set(cause);
exceptionThrownLatch.countDown();
nextFilter.exceptionCaught(session, cause);
}
@@ -476,12 +590,12 @@ private SSLSession findSSLSession(Session session) throws Exception {
return null;
IoFilterChain filterChain = ioSession.getFilterChain();
- SSLFilter sslFilter = (SSLFilter) filterChain.get(SSLSupport.FILTER_NAME);
+ SslFilter sslFilter = (SslFilter) filterChain.get(SSLSupport.FILTER_NAME);
if (sslFilter == null)
return null;
- return sslFilter.getSslSession(ioSession);
+ return (SSLSession) ioSession.getAttribute(SslFilter.SSL_SECURED);
}
private Session findSession(SessionID sessionID) {
@@ -541,14 +655,14 @@ public void assertNotAuthenticated(SessionID sessionID) throws Exception {
}
}
- public void assertLoggedOn(SessionID sessionID) throws InterruptedException {
+ public void assertLoggedOn(SessionID sessionID) {
Session session = findSession(sessionID);
if (!session.isLoggedOn())
throw new AssertionError("Session is not logged on");
}
- public void assertNotLoggedOn(SessionID sessionID) throws InterruptedException {
+ public void assertNotLoggedOn(SessionID sessionID) {
Session session = findSession(sessionID);
if (session.isLoggedOn())
@@ -556,11 +670,25 @@ public void assertNotLoggedOn(SessionID sessionID) throws InterruptedException {
}
public void assertSslExceptionThrown() throws Exception {
+ assertSslExceptionThrown(null, null);
+ }
+
+ public void assertSslExceptionThrown(String errorMessage, Class> errorType) throws Exception {
boolean reachedZero = exceptionThrownLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (!reachedZero) {
throw new AssertionError("No SSL exception thrown");
}
+
+ Throwable throwable = exception.get();
+
+ if (errorMessage != null) {
+ assertEquals(errorMessage, throwable.getMessage());
+ }
+
+ if (errorType != null) {
+ assertSame(errorType, throwable.getClass());
+ }
}
public void assertNoSslExceptionThrown() throws Exception {
@@ -711,8 +839,14 @@ private SessionSettings createAcceptorSettings(String keyStoreName, boolean need
}
private SessionSettings createInitiatorSettings(String keyStoreName, String trustStoreName, String cipherSuites,
- String protocols, String senderId, String targetId, String port, String keyStoreType,
- String trustStoreType) {
+ String protocols, String senderId, String targetId, String port, String keyStoreType,
+ String trustStoreType) {
+ return createInitiatorSettings(keyStoreName, trustStoreName, cipherSuites, protocols, senderId, targetId, port,keyStoreType, trustStoreType, null);
+ }
+
+ private SessionSettings createInitiatorSettings(String keyStoreName, String trustStoreName, String cipherSuites,
+ String protocols, String senderId, String targetId, String port, String keyStoreType,
+ String trustStoreType, String endpointIdentificationAlgorithm) {
HashMap