commit
985edddf91
|
@ -30,6 +30,8 @@ import java.net.InetAddress;
|
|||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SNIMatcher;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
@ -1474,6 +1476,11 @@ public class WolfSSLEngineHelper {
|
|||
* collected (ex: protocol version). */
|
||||
this.session.updateStoredSessionValues();
|
||||
|
||||
if (!this.clientMode && !matchSNI()) {
|
||||
throw new SSLHandshakeException(
|
||||
"Unrecognized Server Name");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1531,6 +1538,59 @@ public class WolfSSLEngineHelper {
|
|||
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
|
||||
* callback state.
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
package com.wolfssl.provider.jsse;
|
||||
|
||||
import java.util.List;
|
||||
import javax.net.ssl.SNIMatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +47,7 @@ final class WolfSSLParameters {
|
|||
private boolean needClientAuth = false;
|
||||
private String endpointIdAlgorithm = null;
|
||||
private List<WolfSSLSNIServerName> serverNames;
|
||||
private List<SNIMatcher> sniMatchers;
|
||||
private boolean useCipherSuiteOrder = true;
|
||||
String[] applicationProtocols = new String[0];
|
||||
private boolean useSessionTickets = false;
|
||||
|
@ -72,7 +75,7 @@ final class WolfSSLParameters {
|
|||
|
||||
/* TODO: duplicate other properties here when WolfSSLParameters
|
||||
* can handle them */
|
||||
|
||||
cp.setSNIMatchers(this.getSNIMatchers());
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
@ -184,14 +187,27 @@ final class WolfSSLParameters {
|
|||
}
|
||||
|
||||
/* TODO, create our own class for SNIMatcher, in case Java doesn't support it */
|
||||
//void setSNIMatchers(Collection<SNIMatcher> matchers) {
|
||||
// /* TODO */
|
||||
//}
|
||||
void setSNIMatchers(Collection<SNIMatcher> matchers) {
|
||||
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 */
|
||||
//Collection<SNIMatcher> getSNIMatchers() {
|
||||
// return null; /* TODO */
|
||||
//}
|
||||
List<SNIMatcher> getSNIMatchers() {
|
||||
if (this.sniMatchers != null && !this.sniMatchers.isEmpty()) {
|
||||
return Collections.unmodifiableList(new ArrayList<SNIMatcher>(sniMatchers));
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
void setUseCipherSuitesOrder(boolean honorOrder) {
|
||||
this.useCipherSuiteOrder = honorOrder;
|
||||
|
|
|
@ -39,6 +39,8 @@ public class WolfSSLParametersHelper
|
|||
private static Method setApplicationProtocols = null;
|
||||
private static Method getEndpointIdentificationAlgorithm = null;
|
||||
private static Method setEndpointIdentificationAlgorithm = null;
|
||||
private static Method getSNIMatchers = null;
|
||||
private static Method setSNIMatchers = null;
|
||||
private static Method getMaximumPacketSize = null;
|
||||
private static Method setMaximumPacketSize = null;
|
||||
|
||||
|
@ -77,6 +79,12 @@ public class WolfSSLParametersHelper
|
|||
case "setEndpointIdentificationAlgorithm":
|
||||
setEndpointIdentificationAlgorithm = m;
|
||||
continue;
|
||||
case "getSNIMatchers":
|
||||
getSNIMatchers = m;
|
||||
continue;
|
||||
case "setSNIMatchers":
|
||||
setSNIMatchers = m;
|
||||
continue;
|
||||
case "getMaximumPacketSize":
|
||||
getMaximumPacketSize = m;
|
||||
continue;
|
||||
|
@ -126,7 +134,7 @@ public class WolfSSLParametersHelper
|
|||
* do not existing in older JDKs. Since older JDKs will not have them,
|
||||
* use Java reflection to detect availability in helper class. */
|
||||
if (setServerNames != null || setApplicationProtocols != null ||
|
||||
setEndpointIdentificationAlgorithm != null) {
|
||||
setEndpointIdentificationAlgorithm != null || setSNIMatchers != null) {
|
||||
|
||||
try {
|
||||
/* load WolfSSLJDK8Helper at runtime, not compiled
|
||||
|
@ -155,6 +163,10 @@ public class WolfSSLParametersHelper
|
|||
mth.invoke(obj, ret,
|
||||
setEndpointIdentificationAlgorithm, in);
|
||||
}
|
||||
if (setSNIMatchers != null) {
|
||||
mth = cls.getDeclaredMethod("setSNIMatchers", paramList);
|
||||
mth.invoke(obj, ret, setSNIMatchers, in);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
/* ignore, class not found */
|
||||
|
@ -173,6 +185,14 @@ public class WolfSSLParametersHelper
|
|||
/* 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
|
||||
* by wolfJSSE (see Android API 23 note above). They are supported
|
||||
* 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,
|
||||
* use Java reflection to detect availability in helper class. */
|
||||
if (getServerNames != null || getApplicationProtocols != null ||
|
||||
getEndpointIdentificationAlgorithm != null) {
|
||||
getEndpointIdentificationAlgorithm != null || getSNIMatchers != null) {
|
||||
try {
|
||||
/* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */
|
||||
Class<?> cls = Class.forName(
|
||||
|
@ -247,6 +267,10 @@ public class WolfSSLParametersHelper
|
|||
"getEndpointIdentificationAlgorithm", paramList);
|
||||
mth.invoke(obj, in, out);
|
||||
}
|
||||
if (getSNIMatchers != null){
|
||||
mth = cls.getDeclaredMethod("getSNIMatchers", paramList);
|
||||
mth.invoke(obj, in, out);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
/* ignore, class not found */
|
||||
|
@ -276,6 +300,9 @@ public class WolfSSLParametersHelper
|
|||
out.setSNIMatchers(in.getSNIMatchers());
|
||||
out.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
|
||||
*/
|
||||
|
||||
out.setSNIMatchers(in.getSNIMatchers());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1569,6 +1569,10 @@ public class WolfSSLSocket extends SSLSocket {
|
|||
close();
|
||||
throw e;
|
||||
|
||||
} catch (SSLHandshakeException e){
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
() -> "got SSLHandshakeException in doHandshake()");
|
||||
throw e;
|
||||
} catch (SSLException e) {
|
||||
final int tmpErr = err;
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
|
|
|
@ -27,6 +27,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -62,6 +63,7 @@ import javax.net.ssl.SSLHandshakeException;
|
|||
import javax.net.ssl.HandshakeCompletedListener;
|
||||
import javax.net.ssl.HandshakeCompletedEvent;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.SNIMatcher;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import java.security.Security;
|
||||
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
|
||||
* TestServer and TestClient classes.
|
||||
|
|
Loading…
Reference in New Issue