commit
985edddf91
|
@ -30,6 +30,8 @@ import java.net.InetAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SNIMatcher;
|
||||||
|
import javax.net.ssl.SNIServerName;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
@ -1474,6 +1476,11 @@ public class WolfSSLEngineHelper {
|
||||||
* collected (ex: protocol version). */
|
* collected (ex: protocol version). */
|
||||||
this.session.updateStoredSessionValues();
|
this.session.updateStoredSessionValues();
|
||||||
|
|
||||||
|
if (!this.clientMode && !matchSNI()) {
|
||||||
|
throw new SSLHandshakeException(
|
||||||
|
"Unrecognized Server Name");
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1531,6 +1538,59 @@ public class WolfSSLEngineHelper {
|
||||||
return "legacy".equals(dhKeySize);
|
return "legacy".equals(dhKeySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates Server Name Indication (SNI) match between client request and
|
||||||
|
* server matchers.
|
||||||
|
*
|
||||||
|
* This helper method is used only on the server side during the TLS
|
||||||
|
* handshake to check if there is a server name in the list of requested
|
||||||
|
* server names that matches the SNI matcher parameter. The check will be
|
||||||
|
* ignored (return true) if no requested server name were sent by client
|
||||||
|
* or if the SNI matcher parameter has not been set.
|
||||||
|
*
|
||||||
|
* Triggers an SSLHandshakeException on server side during handshake when
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @return true on success or false if no match was found
|
||||||
|
*/
|
||||||
|
protected synchronized boolean matchSNI(){
|
||||||
|
List <SNIMatcher> matchers = this.params.getSNIMatchers();
|
||||||
|
if (matchers != null && !matchers.isEmpty()) {
|
||||||
|
/* Match a server name to SNI requested by Client */
|
||||||
|
List <SNIServerName> serverNames = this.session
|
||||||
|
.getRequestedServerNames();
|
||||||
|
if (serverNames != null && !serverNames.isEmpty()) {
|
||||||
|
for (SNIServerName serverName : serverNames) {
|
||||||
|
if (serverName.getType() == WolfSSL.WOLFSSL_SNI_HOST_NAME) {
|
||||||
|
/* If the SNI is of type WOLFSSL_SNI_HOST_NAME, compare
|
||||||
|
* the name to matchers list */
|
||||||
|
for (SNIMatcher matcher : matchers) {
|
||||||
|
if (matcher.matches(serverName)) {
|
||||||
|
/* If a match is found, accept the server name
|
||||||
|
* and return true value, break from loop */
|
||||||
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||||
|
() -> "Accepted SNI: " + serverName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If server names are null or empty, ignore server name
|
||||||
|
* indication */
|
||||||
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||||
|
() -> "No server names found, ignoring SNI");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If matchers are null or empty, ignore server name indication */
|
||||||
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||||
|
() -> "No SNIMatchers set");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unset the native verify callback and reset internal verify
|
* Unset the native verify callback and reset internal verify
|
||||||
* callback state.
|
* callback state.
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
package com.wolfssl.provider.jsse;
|
package com.wolfssl.provider.jsse;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.net.ssl.SNIMatcher;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,6 +47,7 @@ final class WolfSSLParameters {
|
||||||
private boolean needClientAuth = false;
|
private boolean needClientAuth = false;
|
||||||
private String endpointIdAlgorithm = null;
|
private String endpointIdAlgorithm = null;
|
||||||
private List<WolfSSLSNIServerName> serverNames;
|
private List<WolfSSLSNIServerName> serverNames;
|
||||||
|
private List<SNIMatcher> sniMatchers;
|
||||||
private boolean useCipherSuiteOrder = true;
|
private boolean useCipherSuiteOrder = true;
|
||||||
String[] applicationProtocols = new String[0];
|
String[] applicationProtocols = new String[0];
|
||||||
private boolean useSessionTickets = false;
|
private boolean useSessionTickets = false;
|
||||||
|
@ -72,7 +75,7 @@ final class WolfSSLParameters {
|
||||||
|
|
||||||
/* TODO: duplicate other properties here when WolfSSLParameters
|
/* TODO: duplicate other properties here when WolfSSLParameters
|
||||||
* can handle them */
|
* can handle them */
|
||||||
|
cp.setSNIMatchers(this.getSNIMatchers());
|
||||||
return cp;
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,14 +187,27 @@ final class WolfSSLParameters {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO, create our own class for SNIMatcher, in case Java doesn't support it */
|
/* TODO, create our own class for SNIMatcher, in case Java doesn't support it */
|
||||||
//void setSNIMatchers(Collection<SNIMatcher> matchers) {
|
void setSNIMatchers(Collection<SNIMatcher> matchers) {
|
||||||
// /* TODO */
|
if (matchers != null && !matchers.isEmpty()) {
|
||||||
//}
|
if (this.sniMatchers == null) {
|
||||||
|
this.sniMatchers = new ArrayList<SNIMatcher>();
|
||||||
|
}
|
||||||
|
for (SNIMatcher matcher : matchers) {
|
||||||
|
this.sniMatchers.add(matcher);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.sniMatchers = new ArrayList<SNIMatcher>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO, create our own class for SNIMatcher, in case Java doesn't support it */
|
/* TODO, create our own class for SNIMatcher, in case Java doesn't support it */
|
||||||
//Collection<SNIMatcher> getSNIMatchers() {
|
List<SNIMatcher> getSNIMatchers() {
|
||||||
// return null; /* TODO */
|
if (this.sniMatchers != null && !this.sniMatchers.isEmpty()) {
|
||||||
//}
|
return Collections.unmodifiableList(new ArrayList<SNIMatcher>(sniMatchers));
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setUseCipherSuitesOrder(boolean honorOrder) {
|
void setUseCipherSuitesOrder(boolean honorOrder) {
|
||||||
this.useCipherSuiteOrder = honorOrder;
|
this.useCipherSuiteOrder = honorOrder;
|
||||||
|
|
|
@ -39,6 +39,8 @@ public class WolfSSLParametersHelper
|
||||||
private static Method setApplicationProtocols = null;
|
private static Method setApplicationProtocols = null;
|
||||||
private static Method getEndpointIdentificationAlgorithm = null;
|
private static Method getEndpointIdentificationAlgorithm = null;
|
||||||
private static Method setEndpointIdentificationAlgorithm = null;
|
private static Method setEndpointIdentificationAlgorithm = null;
|
||||||
|
private static Method getSNIMatchers = null;
|
||||||
|
private static Method setSNIMatchers = null;
|
||||||
private static Method getMaximumPacketSize = null;
|
private static Method getMaximumPacketSize = null;
|
||||||
private static Method setMaximumPacketSize = null;
|
private static Method setMaximumPacketSize = null;
|
||||||
|
|
||||||
|
@ -77,6 +79,12 @@ public class WolfSSLParametersHelper
|
||||||
case "setEndpointIdentificationAlgorithm":
|
case "setEndpointIdentificationAlgorithm":
|
||||||
setEndpointIdentificationAlgorithm = m;
|
setEndpointIdentificationAlgorithm = m;
|
||||||
continue;
|
continue;
|
||||||
|
case "getSNIMatchers":
|
||||||
|
getSNIMatchers = m;
|
||||||
|
continue;
|
||||||
|
case "setSNIMatchers":
|
||||||
|
setSNIMatchers = m;
|
||||||
|
continue;
|
||||||
case "getMaximumPacketSize":
|
case "getMaximumPacketSize":
|
||||||
getMaximumPacketSize = m;
|
getMaximumPacketSize = m;
|
||||||
continue;
|
continue;
|
||||||
|
@ -126,7 +134,7 @@ public class WolfSSLParametersHelper
|
||||||
* do not existing in older JDKs. Since older JDKs will not have them,
|
* do not existing in older JDKs. Since older JDKs will not have them,
|
||||||
* use Java reflection to detect availability in helper class. */
|
* use Java reflection to detect availability in helper class. */
|
||||||
if (setServerNames != null || setApplicationProtocols != null ||
|
if (setServerNames != null || setApplicationProtocols != null ||
|
||||||
setEndpointIdentificationAlgorithm != null) {
|
setEndpointIdentificationAlgorithm != null || setSNIMatchers != null) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* load WolfSSLJDK8Helper at runtime, not compiled
|
/* load WolfSSLJDK8Helper at runtime, not compiled
|
||||||
|
@ -155,6 +163,10 @@ public class WolfSSLParametersHelper
|
||||||
mth.invoke(obj, ret,
|
mth.invoke(obj, ret,
|
||||||
setEndpointIdentificationAlgorithm, in);
|
setEndpointIdentificationAlgorithm, in);
|
||||||
}
|
}
|
||||||
|
if (setSNIMatchers != null) {
|
||||||
|
mth = cls.getDeclaredMethod("setSNIMatchers", paramList);
|
||||||
|
mth.invoke(obj, ret, setSNIMatchers, in);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
/* ignore, class not found */
|
/* ignore, class not found */
|
||||||
|
@ -173,6 +185,14 @@ public class WolfSSLParametersHelper
|
||||||
/* Not available, just ignore and continue */
|
/* Not available, just ignore and continue */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (setSNIMatchers != null) {
|
||||||
|
ret.setSNIMatchers(in.getSNIMatchers());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
/* Not available, just ignore and continue */
|
||||||
|
}
|
||||||
|
|
||||||
/* The following SSLParameters features are not yet supported
|
/* The following SSLParameters features are not yet supported
|
||||||
* by wolfJSSE (see Android API 23 note above). They are supported
|
* by wolfJSSE (see Android API 23 note above). They are supported
|
||||||
* with newer versions of SSLParameters, but will need to be added
|
* with newer versions of SSLParameters, but will need to be added
|
||||||
|
@ -222,7 +242,7 @@ public class WolfSSLParametersHelper
|
||||||
* do not existing in older JDKs. Since older JDKs will not have them,
|
* do not existing in older JDKs. Since older JDKs will not have them,
|
||||||
* use Java reflection to detect availability in helper class. */
|
* use Java reflection to detect availability in helper class. */
|
||||||
if (getServerNames != null || getApplicationProtocols != null ||
|
if (getServerNames != null || getApplicationProtocols != null ||
|
||||||
getEndpointIdentificationAlgorithm != null) {
|
getEndpointIdentificationAlgorithm != null || getSNIMatchers != null) {
|
||||||
try {
|
try {
|
||||||
/* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */
|
/* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */
|
||||||
Class<?> cls = Class.forName(
|
Class<?> cls = Class.forName(
|
||||||
|
@ -247,6 +267,10 @@ public class WolfSSLParametersHelper
|
||||||
"getEndpointIdentificationAlgorithm", paramList);
|
"getEndpointIdentificationAlgorithm", paramList);
|
||||||
mth.invoke(obj, in, out);
|
mth.invoke(obj, in, out);
|
||||||
}
|
}
|
||||||
|
if (getSNIMatchers != null){
|
||||||
|
mth = cls.getDeclaredMethod("getSNIMatchers", paramList);
|
||||||
|
mth.invoke(obj, in, out);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
/* ignore, class not found */
|
/* ignore, class not found */
|
||||||
|
@ -276,6 +300,9 @@ public class WolfSSLParametersHelper
|
||||||
out.setSNIMatchers(in.getSNIMatchers());
|
out.setSNIMatchers(in.getSNIMatchers());
|
||||||
out.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
|
out.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
out.setSNIMatchers(in.getSNIMatchers());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1569,6 +1569,10 @@ public class WolfSSLSocket extends SSLSocket {
|
||||||
close();
|
close();
|
||||||
throw e;
|
throw e;
|
||||||
|
|
||||||
|
} catch (SSLHandshakeException e){
|
||||||
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||||
|
() -> "got SSLHandshakeException in doHandshake()");
|
||||||
|
throw e;
|
||||||
} catch (SSLException e) {
|
} catch (SSLException e) {
|
||||||
final int tmpErr = err;
|
final int tmpErr = err;
|
||||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
@ -62,6 +63,7 @@ import javax.net.ssl.SSLHandshakeException;
|
||||||
import javax.net.ssl.HandshakeCompletedListener;
|
import javax.net.ssl.HandshakeCompletedListener;
|
||||||
import javax.net.ssl.HandshakeCompletedEvent;
|
import javax.net.ssl.HandshakeCompletedEvent;
|
||||||
import javax.net.ssl.SNIHostName;
|
import javax.net.ssl.SNIHostName;
|
||||||
|
import javax.net.ssl.SNIMatcher;
|
||||||
import javax.net.ssl.SNIServerName;
|
import javax.net.ssl.SNIServerName;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
|
@ -3395,6 +3397,113 @@ public class WolfSSLSocketTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSNIMatchers() throws Exception {
|
||||||
|
|
||||||
|
System.out.print("\tTesting SNI Matchers");
|
||||||
|
|
||||||
|
/* create new CTX */
|
||||||
|
this.ctx = tf.createSSLContext("TLS", ctxProvider);
|
||||||
|
|
||||||
|
/* create SSLServerSocket first to get ephemeral port */
|
||||||
|
final SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
|
||||||
|
.createServerSocket(0);
|
||||||
|
|
||||||
|
/* Configure SNI matcher for server*/
|
||||||
|
SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.com");
|
||||||
|
Collection<SNIMatcher> matchers = new ArrayList<>();
|
||||||
|
matchers.add(matcher);
|
||||||
|
SSLParameters sp = ss.getSSLParameters();
|
||||||
|
sp.setSNIMatchers(matchers);
|
||||||
|
ss.setSSLParameters(sp);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
/* ------------------------------------ */
|
||||||
|
/* Test matched SNI case, should pass */
|
||||||
|
/* ------------------------------------ */
|
||||||
|
|
||||||
|
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
|
||||||
|
cs.connect(new InetSocketAddress(ss.getLocalPort()));
|
||||||
|
|
||||||
|
/* Set SNI hostname for client */
|
||||||
|
SNIHostName serverName = new SNIHostName("www.example.com");
|
||||||
|
List<SNIServerName> serverNames = new ArrayList<>();
|
||||||
|
serverNames.add(serverName);
|
||||||
|
SSLParameters cp = cs.getSSLParameters();
|
||||||
|
cp.setServerNames(serverNames);
|
||||||
|
cs.setSSLParameters(cp);
|
||||||
|
|
||||||
|
final SSLSocket serverMatched = (SSLSocket)ss.accept();
|
||||||
|
|
||||||
|
ExecutorService es = Executors.newSingleThreadExecutor();
|
||||||
|
Future<Void> serverFuture = es.submit(new Callable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call() throws Exception {
|
||||||
|
try {
|
||||||
|
serverMatched.startHandshake();
|
||||||
|
serverMatched.close();
|
||||||
|
} catch (SSLException e) {
|
||||||
|
System.out.println("\t... failed");
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cs.startHandshake();
|
||||||
|
cs.close();
|
||||||
|
|
||||||
|
es.shutdown();
|
||||||
|
serverFuture.get();
|
||||||
|
|
||||||
|
/* ------------------------------------ */
|
||||||
|
/* Test unmatched SNI case, should fail */
|
||||||
|
/* ------------------------------------ */
|
||||||
|
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
|
||||||
|
cs.connect(new InetSocketAddress(ss.getLocalPort()));
|
||||||
|
|
||||||
|
/* Set non-matching SNI hostname for client */
|
||||||
|
serverName = new SNIHostName("www.example.org");
|
||||||
|
serverNames = new ArrayList<>();
|
||||||
|
serverNames.add(serverName);
|
||||||
|
cp = cs.getSSLParameters();
|
||||||
|
cp.setServerNames(serverNames);
|
||||||
|
cs.setSSLParameters(cp);
|
||||||
|
|
||||||
|
final SSLSocket serverUnmatched = (SSLSocket)ss.accept();
|
||||||
|
|
||||||
|
es = Executors.newSingleThreadExecutor();
|
||||||
|
serverFuture = es.submit(() -> {
|
||||||
|
try {
|
||||||
|
serverUnmatched.startHandshake();
|
||||||
|
fail("Server handshake succeeded with non-matching SNI");
|
||||||
|
} catch (SSLHandshakeException e) {
|
||||||
|
/* Expected failure with non-matching SNI */
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
cs.startHandshake();
|
||||||
|
} catch (SSLHandshakeException e) {
|
||||||
|
/* Expect client to close connection, wolfJSSE does not expect
|
||||||
|
* to an exception. However, SunJSSE will throw an exception */
|
||||||
|
}
|
||||||
|
|
||||||
|
es.shutdown();
|
||||||
|
serverFuture.get();
|
||||||
|
cs.close();
|
||||||
|
|
||||||
|
System.out.println("\t\t... passed");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("\t\t... failed");
|
||||||
|
fail();
|
||||||
|
} finally {
|
||||||
|
ss.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inner class used to hold configuration options for
|
* Inner class used to hold configuration options for
|
||||||
* TestServer and TestClient classes.
|
* TestServer and TestClient classes.
|
||||||
|
|
Loading…
Reference in New Issue