* Fixes #5379 - Better handling for wrong SNI. Reworked the SNI logic. Added support for IP addresses in the SAN extension of certificates in the X509 class. Fixed keystores to have CN=localhost and SAN with ip=127.0.0.1 and ip=[::1]. Fixed tests that were not using the correct Host header. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
dcf4a835d7
commit
1cd15e8d85
|
@ -27,10 +27,14 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
|
@ -49,7 +53,7 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
*/
|
||||
public class HostnameVerificationTest
|
||||
{
|
||||
private SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client();
|
||||
private final SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client();
|
||||
private Server server;
|
||||
private HttpClient client;
|
||||
private NetworkConnector connector;
|
||||
|
@ -64,7 +68,13 @@ public class HostnameVerificationTest
|
|||
SslContextFactory.Server serverSslContextFactory = new SslContextFactory.Server();
|
||||
serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.p12");
|
||||
serverSslContextFactory.setKeyStorePassword("storepwd");
|
||||
connector = new ServerConnector(server, serverSslContextFactory);
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
SecureRequestCustomizer customizer = new SecureRequestCustomizer();
|
||||
customizer.setSniHostCheck(false);
|
||||
httpConfig.addCustomizer(customizer);
|
||||
HttpConnectionFactory http = new HttpConnectionFactory(httpConfig);
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(serverSslContextFactory, http.getProtocol());
|
||||
connector = new ServerConnector(server, 1, 1, ssl, http);
|
||||
server.addConnector(connector);
|
||||
server.setHandler(new DefaultHandler()
|
||||
{
|
||||
|
@ -102,14 +112,17 @@ public class HostnameVerificationTest
|
|||
|
||||
/**
|
||||
* This test is supposed to verify that hostname verification works as described in:
|
||||
* http://www.ietf.org/rfc/rfc2818.txt section 3.1. It uses a certificate with a common name different to localhost
|
||||
* and sends a request to localhost. This should fail with an SSLHandshakeException.
|
||||
* http://www.ietf.org/rfc/rfc2818.txt section 3.1.
|
||||
* It uses a certificate with a common name "localhost" and SAN=127.0.0.1,
|
||||
* and sends a request to 127.0.0.2.
|
||||
* This should fail with on the client an SSLHandshakeException, because SNI
|
||||
* host checking on the server side is disabled.
|
||||
*/
|
||||
@Test
|
||||
public void simpleGetWithHostnameVerificationEnabledTest()
|
||||
{
|
||||
clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
String uri = "https://localhost:" + connector.getLocalPort() + "/";
|
||||
String uri = "https://127.0.0.2:" + connector.getLocalPort() + "/";
|
||||
|
||||
ExecutionException x = assertThrows(ExecutionException.class, () -> client.GET(uri));
|
||||
Throwable cause = x.getCause();
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,9 +1,9 @@
|
|||
Since OpenJDK 13.0.2/11.0.6 it is required that CA certificates have the extension CA=true.
|
||||
Since OpenJDK 13.0.2/11.0.6 it is required that CA certificates have the extension "bc=ca:true".
|
||||
|
||||
The keystores are generated in the following way:
|
||||
|
||||
# Generates the server keystore. Note the BasicConstraint=CA:true extension.
|
||||
$ keytool -v -genkeypair -validity 36500 -keyalg RSA -keysize 2048 -keystore keystore.p12 -storetype pkcs12 -dname "CN=server, OU=Jetty, O=Webtide, L=Omaha, S=NE, C=US" -ext BC=CA:true
|
||||
$ keytool -v -genkeypair -validity 36500 -keyalg RSA -keysize 2048 -keystore keystore.p12 -storetype pkcs12 -dname "CN=localhost, OU=Jetty, O=Webtide, L=Omaha, S=NE, C=US" -ext bc=ca:true -ext san=ip:127.0.0.1,ip:[::1]
|
||||
|
||||
# Export the server certificate.
|
||||
$ keytool -v -export -keystore keystore.p12 -rfc -file server.crt
|
||||
|
|
|
@ -57,4 +57,7 @@ For example, if you have bought domains `domain.com` and `domain.org`, you want
|
|||
|
||||
Furthermore, to specify additional domains or subdomains within the same certificate, you must specify the SAN extension.
|
||||
In the example above, `san=dns:www.domain.com,dns:domain.org` specifies `www.domain.com` and `domain.org` as alternative names for your web applications (that you can configure using xref:og-deploy-virtual-hosts[virtual hosts]).
|
||||
|
||||
In rare cases, you may want to specify IP addresses, rather than domains, in the SAN extension.
|
||||
The syntax in such case is `san=ip:127.0.0.1,ip:[::1]`, which specifies as subject alternative names IPv4 `127.0.0.1` and IPv6 `[::1]`.
|
||||
====
|
||||
|
|
|
@ -195,8 +195,10 @@ The KeyStore contains two certificates, one for `one.com` and one for `two.net`.
|
|||
|
||||
There are three `ssl` module properties that control the SNI behavior on the server: one that works at the TLS level, and two that works at the HTTP level.
|
||||
|
||||
The property that works at the TLS level is:
|
||||
|
||||
`jetty.sslContext.sniRequired`::
|
||||
This is the TLS level property, defaults to `false`.
|
||||
Whether SNI is required at the TLS level, defaults to `false`.
|
||||
|
||||
Its behavior is explained by the following table:
|
||||
|
||||
|
@ -222,54 +224,77 @@ Its behavior is explained by the following table:
|
|||
|
||||
|===
|
||||
|
||||
WARNING: The _default certificate_ is the certificate returned by the TLS implementation in case there is no SNI match, and you should not rely on this certificate to be the same across Java vendors and versions, or Jetty versions, or TLS provider vendors and versions.
|
||||
[WARNING]
|
||||
====
|
||||
The _default certificate_ is the certificate returned by the TLS implementation in case there is no SNI match, and you should not rely on this certificate to be the same across Java vendors and versions, or Jetty versions, or TLS provider vendors and versions.
|
||||
|
||||
In the example above it could be either the `one.com` certificate or the `two.net` certificate.
|
||||
====
|
||||
|
||||
When `jetty.sslContext.sniRequired=true`, clients that don't send a valid SNI receive a TLS failure, and their attempt to connect to the server fails.
|
||||
The details of this failure may not be reported and could be difficult to figure out that the failure is related to an invalid SNI.
|
||||
|
||||
For this reason, other two properties are defined at the HTTP level, so that clients can received an HTTP 400 response with more details about what went wrong while trying to connect to the server:
|
||||
|
||||
`jetty.ssl.sniRequired`::
|
||||
Whether SNI is required at the HTTP level, defaults to `false`.
|
||||
`jetty.ssl.sniHostCheck`::
|
||||
Whether the SNI domain must be verified at the HTTP level against the `Host` header, defaults to `true`.
|
||||
|
||||
Their combined behavior is explained by the following table.
|
||||
Its behavior is similar to the `jetty.sslContext.sniRequired` property above, and is explained by the following table:
|
||||
|
||||
.Behavior of the `jetty.ssl.[sniRequired|sniHostCheck]` properties
|
||||
[cols="5*a"]
|
||||
.Behavior of the `jetty.ssl.sniRequired` property
|
||||
[cols=3*a]
|
||||
|===
|
||||
|
||||
|
|
||||
| `sniRequired=false` +
|
||||
`sniHostCheck=false`
|
||||
|
||||
| `sniRequired=false` +
|
||||
`sniHostCheck=true`
|
||||
|
||||
| `sniRequired=true` +
|
||||
`sniHostCheck=false`
|
||||
|
||||
| `sniRequired=true` +
|
||||
`sniHostCheck=true`
|
||||
| `sniRequired=false`
|
||||
| `sniRequired=true`
|
||||
|
||||
| SNI = `null`
|
||||
| 200 OK
|
||||
| 200 OK
|
||||
| 400 Bad Request
|
||||
| 400 Bad Request
|
||||
| Accept
|
||||
| Reject: 400 Bad Request
|
||||
|
||||
| SNI = `wrong.org`
|
||||
| 200 OK
|
||||
| 400 Bad Request
|
||||
| 400 Bad Request
|
||||
| 400 Bad Request
|
||||
| Accept
|
||||
| Reject: 400 Bad Request
|
||||
|
||||
| SNI = `one.com`
|
||||
| 200 OK
|
||||
| SNI matches `Host` header ? +
|
||||
200 OK : 400 Bad Request
|
||||
| 200 OK
|
||||
| SNI matches `Host` header ? +
|
||||
200 OK : 400 Bad Request
|
||||
| Accept
|
||||
| Accept
|
||||
|
||||
|===
|
||||
|
||||
In the normal case, with modern TLS clients and HTTP requests with a correct `Host` header, Jetty will pick the correct certificate from the KeyStore based on the SNI and respond with an HTTP 200 OK.
|
||||
When `jetty.ssl.sniRequired=true`, the SNI is matched against the certificate sent to the client, and only if there is a match the request is accepted.
|
||||
|
||||
When the request is accepted, there could be an additional check controlled by the following property:
|
||||
|
||||
`jetty.ssl.sniHostCheck`::
|
||||
Whether the certificate sent to the client matches the `Host` header, defaults to `true`.
|
||||
|
||||
Its behavior is explained by the following table:
|
||||
|
||||
.Behavior of the `jetty.ssl.sniHostCheck` property
|
||||
[cols="3*a"]
|
||||
|===
|
||||
|
||||
|
|
||||
| `sniHostCheck=false`
|
||||
| `sniHostCheck=true`
|
||||
|
||||
| certificate = `one.com` +
|
||||
`Host: wrong.org`
|
||||
| Accept
|
||||
| Reject: 400 Bad Request
|
||||
|
||||
| certificate = `one.com` +
|
||||
`Host: one.com`
|
||||
| Accept
|
||||
| Accept
|
||||
|
||||
|===
|
||||
|
||||
In the normal case with the default server configuration, for a TLS clients that sends SNI, and then sends an HTTP request with the correct `Host` header, Jetty will pick the correct certificate from the KeyStore based on the SNI received from the client, and accept the request.
|
||||
|
||||
Accepting the request does not mean that the request is responded with an HTTP 200 OK, but just that the request passed successfully the SNI checks and will be processed by the server.
|
||||
If the request URI is for a resource that does not exist, the response will likely be a 404 Not Found.
|
||||
|
||||
You may modify the default values of the SNI properties if you want stricter control over old/broken TLS clients or bad HTTP requests.
|
||||
|
|
|
@ -646,35 +646,21 @@ public class ForwardProxyTLSServerTest
|
|||
@Test
|
||||
public void testBothProxyAndServerNeedClientAuth() throws Exception
|
||||
{
|
||||
// Keystore server_keystore.p12 contains:
|
||||
// - alias "mykey": self-signed certificate with private key.
|
||||
// - alias "client_root": certificate from client_keystore.p12 under the "server" alias.
|
||||
// Keystore proxy_keystore.p12 contains:
|
||||
// - alias "mykey": self-signed certificate with private key.
|
||||
// - alias "client_root": certificate from client_keystore.p12 under the "proxy" alias.
|
||||
// Keystore client_keystore.p12 contains:
|
||||
// - alias "proxy": self-signed certificate with private key to send to the proxy.
|
||||
// - alias "server": self-signed certificate with private key to send to the server.
|
||||
// - alias "proxy_root": certificate from proxy_keystore under the "mykey" alias.
|
||||
// - alias "server_root": certificate from server_keystore under the "mykey" alias.
|
||||
|
||||
// We want setEndpointIdentificationAlgorithm(null) for all 3 SslContextFactory
|
||||
// because the certificate common names do not match the host names.
|
||||
// See src/test/resources/readme_keystores.txt.
|
||||
|
||||
SslContextFactory.Server serverTLS = newServerSslContextFactory();
|
||||
serverTLS.setEndpointIdentificationAlgorithm(null);
|
||||
serverTLS.setNeedClientAuth(true);
|
||||
startTLSServer(serverTLS, new ServerHandler());
|
||||
int serverPort = serverConnector.getLocalPort();
|
||||
String serverAlias = "server";
|
||||
|
||||
SslContextFactory.Server proxyTLS = newProxySslContextFactory();
|
||||
proxyTLS.setEndpointIdentificationAlgorithm(null);
|
||||
proxyTLS.setNeedClientAuth(true);
|
||||
startProxy(proxyTLS);
|
||||
int proxyPort = proxyConnector.getLocalPort();
|
||||
String proxyAlias = "proxy";
|
||||
|
||||
String proxyAlias = "client_to_proxy";
|
||||
String serverAlias = "client_to_server";
|
||||
SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client()
|
||||
{
|
||||
@Override
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
The keystores are generated differently from jetty-client's readme_keystores.txt.
|
||||
|
||||
Since SslContextFactory also loads the KeyStore as a TrustStore, rather than doing
|
||||
CSR for the client certificates and sign them with the server|proxy certificate,
|
||||
we just load the client certificates in the server|proxy KeyStores so that they
|
||||
are trusted.
|
||||
|
||||
Structure is the following:
|
||||
|
||||
server_keystore.p12:
|
||||
mykey: self-signed certificate with private key
|
||||
client: certificate from client_keystore.p12@client_to_server
|
||||
|
||||
proxy_keystore.p12:
|
||||
mykey: self-signed certificate with private key
|
||||
client: certificate from client_keystore.p12@client_to_proxy
|
||||
|
||||
client_keystore.p12
|
||||
client_to_proxy: self-signed certificate with private key (client certificate to send to proxy)
|
||||
client_to_server: self-signed certificate with private key (client certificate to send to server)
|
||||
proxy: certificate from proxy_keystore.p12@mykey (to trust proxy certificate)
|
||||
server: certificate from server_keystore.p12@mykey (to trust server certificate)
|
Binary file not shown.
|
@ -40,7 +40,6 @@ import org.eclipse.jetty.util.Attributes;
|
|||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.ssl.X509;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -63,7 +62,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
|
||||
private boolean _sniRequired;
|
||||
private boolean _sniHostCheck;
|
||||
private long _stsMaxAge = -1;
|
||||
private long _stsMaxAge;
|
||||
private boolean _stsIncludeSubDomains;
|
||||
private HttpField _stsField;
|
||||
|
||||
|
@ -247,26 +246,32 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
{
|
||||
SSLSession sslSession = sslEngine.getSession();
|
||||
|
||||
if (_sniHostCheck || _sniRequired)
|
||||
if (isSniRequired() || isSniHostCheck())
|
||||
{
|
||||
X509 x509 = (X509)sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509);
|
||||
String sniHost = (String)sslSession.getValue(SslContextFactory.Server.SNI_HOST);
|
||||
X509 cert = new X509(null, (X509Certificate)sslSession.getLocalCertificates()[0]);
|
||||
String serverName = request.getServerName();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Host {} with SNI {}", request.getServerName(), x509);
|
||||
LOG.debug("Host={}, SNI={}, SNI Certificate={}", serverName, sniHost, cert);
|
||||
|
||||
if (x509 == null)
|
||||
if (isSniRequired())
|
||||
{
|
||||
if (_sniRequired)
|
||||
throw new BadMessageException(400, "SNI required");
|
||||
if (sniHost == null)
|
||||
throw new BadMessageException(400, "Invalid SNI");
|
||||
if (!cert.matches(sniHost))
|
||||
throw new BadMessageException(400, "Invalid SNI");
|
||||
}
|
||||
else if (_sniHostCheck && !x509.matches(request.getServerName()))
|
||||
|
||||
if (isSniHostCheck())
|
||||
{
|
||||
throw new BadMessageException(400, "Host does not match SNI");
|
||||
if (!cert.matches(serverName))
|
||||
throw new BadMessageException(400, "Invalid SNI");
|
||||
}
|
||||
}
|
||||
|
||||
request.setAttributes(new SslAttributes(request, sslSession, request.getAttributes()));
|
||||
request.setAttributes(new SslAttributes(request, sslSession));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Customizes the request attributes for general secure settings.
|
||||
* The default impl calls {@link Request#setSecure(boolean)} with true
|
||||
|
@ -325,9 +330,9 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
private String _sessionId;
|
||||
private String _sessionAttribute;
|
||||
|
||||
public SslAttributes(Request request, SSLSession sslSession, Attributes attributes)
|
||||
private SslAttributes(Request request, SSLSession sslSession)
|
||||
{
|
||||
super(attributes);
|
||||
super(request.getAttributes());
|
||||
this._request = request;
|
||||
this._session = sslSession;
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
{
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
os.flush();
|
||||
|
@ -141,7 +141,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
byte[] contentB = content.getBytes("utf-8");
|
||||
os.write((
|
||||
"POST /echo HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: " + contentB.length + "\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
|
@ -189,7 +189,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
os.flush();
|
||||
|
@ -250,7 +250,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
byte[] contentB = content.getBytes("utf-8");
|
||||
os.write((
|
||||
"POST /echo HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: " + contentB.length + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
|
@ -295,7 +295,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
OutputStream os = client.getOutputStream();
|
||||
os.write(("GET / HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"Transfer-Encoding: chunked\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
|
@ -356,7 +356,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
os.write(("GET / HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"Transfer-Encoding: chunked\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
|
@ -413,7 +413,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
|
@ -456,7 +456,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
|
@ -561,7 +561,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Content-Length: 20\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
|
@ -600,7 +600,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Content-Length: 20\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
|
@ -643,7 +643,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
byte[] contentB = content.getBytes("utf-8");
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Content-Length: " + (contentB.length * 20) + "\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
|
@ -684,7 +684,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
|
@ -716,7 +716,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET / HTTP/1.0\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: keep-alive\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n").getBytes("utf-8"));
|
||||
|
|
|
@ -138,7 +138,11 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
{
|
||||
OutputStream os = client.getOutputStream();
|
||||
|
||||
os.write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
|
||||
String request = "GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
os.write(request.getBytes(StandardCharsets.ISO_8859_1));
|
||||
os.flush();
|
||||
|
||||
// Read the response.
|
||||
|
@ -159,7 +163,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
OutputStream os = client.getOutputStream();
|
||||
|
||||
os.write(("OPTIONS * HTTP/1.1\r\n" +
|
||||
"Host: " + _serverURI.getHost() + "\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n").getBytes(StandardCharsets.ISO_8859_1));
|
||||
os.flush();
|
||||
|
@ -666,7 +670,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
String test = encoding[e] + "x" + b + "x" + w + "x" + c;
|
||||
try
|
||||
{
|
||||
URL url = new URL(_scheme + "://" + _serverURI.getHost() + ":" + _serverURI.getPort() + "/?writes=" + w + "&block=" + b + (e == 0 ? "" : ("&encoding=" + encoding[e])) + (c == 0 ? "&chars=true" : ""));
|
||||
URL url = new URL(_scheme + "://localhost:" + _serverURI.getPort() + "/?writes=" + w + "&block=" + b + (e == 0 ? "" : ("&encoding=" + encoding[e])) + (c == 0 ? "&chars=true" : ""));
|
||||
|
||||
InputStream in = (InputStream)url.getContent();
|
||||
String response = IO.toString(in, e == 0 ? null : encoding[e]);
|
||||
|
@ -698,7 +702,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET /data?writes=1024&block=256 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"content-type: unknown\r\n" +
|
||||
"content-length: 30\r\n" +
|
||||
|
@ -758,7 +762,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET /data HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: unknown\r\n" +
|
||||
"transfer-encoding: chunked\r\n" +
|
||||
"\r\n"
|
||||
|
@ -807,7 +811,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET /data?writes=256&block=1024 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"content-type: unknown\r\n" +
|
||||
"\r\n"
|
||||
|
@ -848,7 +852,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET /data?encoding=iso-8859-1&writes=100&block=100000 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"content-type: unknown\r\n" +
|
||||
"\r\n"
|
||||
|
@ -875,7 +879,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
InputStream is = client.getInputStream();
|
||||
|
||||
os.write(("GET /data?writes=1&block=1024 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"content-type: unknown\r\n" +
|
||||
"\r\n"
|
||||
|
@ -901,10 +905,10 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET /r1 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"\r\n" +
|
||||
"GET /r2 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"\r\n"
|
||||
).getBytes());
|
||||
|
@ -1049,7 +1053,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
{
|
||||
request +=
|
||||
"GET /data?writes=1&block=16&id=" + i + " HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"user-agent: testharness/1.0 (blah foo/bar)\r\n" +
|
||||
"accept-encoding: nothing\r\n" +
|
||||
"cookie: aaa=1234567890\r\n" +
|
||||
|
@ -1058,7 +1062,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
request +=
|
||||
"GET /data?writes=1&block=16 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"user-agent: testharness/1.0 (blah foo/bar)\r\n" +
|
||||
"accept-encoding: nothing\r\n" +
|
||||
"cookie: aaa=bbbbbb\r\n" +
|
||||
|
@ -1095,7 +1099,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"POST /echo?charset=utf-8 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"\r\n").getBytes(StandardCharsets.ISO_8859_1));
|
||||
|
@ -1106,7 +1110,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"POST /echo?charset=utf-8 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"\r\n"
|
||||
|
@ -1120,7 +1124,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
byte[] contentB = content.getBytes("utf-8");
|
||||
os.write((
|
||||
"POST /echo?charset=utf-16 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: " + contentB.length + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
|
@ -1182,21 +1186,21 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
//@checkstyle-disable-check : IllegalTokenText
|
||||
os.write((
|
||||
"POST /R1 HTTP/1.1\r\n" +
|
||||
"Host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"Host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"\r\n" +
|
||||
"123456789\n" +
|
||||
|
||||
"HEAD /R2 HTTP/1.1\r\n" +
|
||||
"Host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"Host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"\r\n" +
|
||||
"ABCDEFGHI\n" +
|
||||
|
||||
"POST /R3 HTTP/1.1\r\n" +
|
||||
"Host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"Host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
|
@ -1224,7 +1228,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"POST /echo/0?charset=utf-8 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"\r\n").getBytes(StandardCharsets.ISO_8859_1));
|
||||
|
@ -1235,7 +1239,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"POST /echo/1?charset=utf-8 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-8\r\n" +
|
||||
"content-length: 10\r\n" +
|
||||
"\r\n"
|
||||
|
@ -1249,7 +1253,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
byte[] contentB = content.getBytes(StandardCharsets.UTF_16);
|
||||
os.write((
|
||||
"POST /echo/2?charset=utf-8 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"content-type: text/plain; charset=utf-16\r\n" +
|
||||
"content-length: " + contentB.length + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
|
@ -1279,7 +1283,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
// Send a request with chunked input and expect 100
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"Host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"Host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"Transfer-Encoding: chunked\r\n" +
|
||||
"Expect: 100-continue\r\n" +
|
||||
"Connection: Keep-Alive\r\n" +
|
||||
|
@ -1316,7 +1320,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
// Send a request
|
||||
os.write(("GET / HTTP/1.1\r\n" +
|
||||
"Host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"Host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"\r\n"
|
||||
).getBytes());
|
||||
os.flush();
|
||||
|
@ -1452,7 +1456,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
os.write((
|
||||
"GET /data?writes=1024&block=256 HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"content-type: unknown\r\n" +
|
||||
"content-length: 30\r\n" +
|
||||
|
@ -1608,7 +1612,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
{
|
||||
out.write(bytes, 0, bytes.length);
|
||||
}
|
||||
out.write("GET / HTTP/1.1\r\nHost: last\r\nConnection: close\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
|
||||
out.write("GET /last HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
|
||||
out.flush();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -1636,8 +1640,8 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
|
||||
final OutputStream out = client.getOutputStream();
|
||||
|
||||
out.write("GET / HTTP/1.1\r\nHost: test\r\n\r\n".getBytes());
|
||||
out.write("GET / HTTP/1.1\r\nHost: test\r\nConnection: close\r\n\r\n".getBytes());
|
||||
out.write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n".getBytes());
|
||||
out.write("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".getBytes());
|
||||
out.flush();
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
|
||||
|
@ -1679,7 +1683,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
while (line != null);
|
||||
}
|
||||
|
||||
private class WriteBodyAfterNoBodyResponseHandler extends AbstractHandler
|
||||
private static class WriteBodyAfterNoBodyResponseHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
|
@ -1722,7 +1726,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
// write an initial request
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"\r\n"
|
||||
).getBytes());
|
||||
os.flush();
|
||||
|
@ -1732,7 +1736,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
// write an pipelined request
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"connection: close\r\n" +
|
||||
"\r\n"
|
||||
).getBytes());
|
||||
|
|
|
@ -253,7 +253,11 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
|
|||
{
|
||||
OutputStream os = client.getOutputStream();
|
||||
|
||||
os.write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
|
||||
String request = "GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
os.write(request.getBytes(StandardCharsets.ISO_8859_1));
|
||||
os.flush();
|
||||
|
||||
// Read the response.
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
|
@ -59,11 +60,11 @@ import org.eclipse.jetty.server.SslConnectionFactory;
|
|||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -77,55 +78,39 @@ public class SniSslConnectionFactoryTest
|
|||
{
|
||||
private Server _server;
|
||||
private ServerConnector _connector;
|
||||
private HttpConfiguration _httpsConfiguration;
|
||||
private int _port;
|
||||
|
||||
@BeforeEach
|
||||
public void before()
|
||||
{
|
||||
_server = new Server();
|
||||
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
httpConfig.setSecureScheme("https");
|
||||
httpConfig.setSecurePort(8443);
|
||||
httpConfig.setOutputBufferSize(32768);
|
||||
_httpsConfiguration = new HttpConfiguration(httpConfig);
|
||||
SecureRequestCustomizer src = new SecureRequestCustomizer();
|
||||
src.setSniHostCheck(true);
|
||||
_httpsConfiguration.addCustomizer(src);
|
||||
_httpsConfiguration.addCustomizer((connector, hc, request) ->
|
||||
{
|
||||
EndPoint endp = request.getHttpChannel().getEndPoint();
|
||||
if (endp instanceof SslConnection.DecryptedEndPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
SslConnection.DecryptedEndPoint sslEndp = (SslConnection.DecryptedEndPoint)endp;
|
||||
SslConnection sslConnection = sslEndp.getSslConnection();
|
||||
SSLEngine sslEngine = sslConnection.getSSLEngine();
|
||||
SSLSession session = sslEngine.getSession();
|
||||
for (Certificate c : session.getLocalCertificates())
|
||||
{
|
||||
request.getResponse().getHttpFields().add("X-Cert", ((X509Certificate)c).getSubjectDN().toString());
|
||||
}
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
th.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void start(String keystorePath) throws Exception
|
||||
private void start(String keystorePath) throws Exception
|
||||
{
|
||||
start(ssl -> ssl.setKeyStorePath(keystorePath));
|
||||
}
|
||||
|
||||
protected void start(Consumer<SslContextFactory.Server> sslConfig) throws Exception
|
||||
private void start(Consumer<SslContextFactory.Server> sslConfig) throws Exception
|
||||
{
|
||||
start((ssl, customizer) -> sslConfig.accept(ssl));
|
||||
}
|
||||
|
||||
private void start(BiConsumer<SslContextFactory.Server, SecureRequestCustomizer> config) throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
|
||||
HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||
SecureRequestCustomizer secureRequestCustomizer = new SecureRequestCustomizer();
|
||||
httpConfiguration.addCustomizer(secureRequestCustomizer);
|
||||
httpConfiguration.addCustomizer((connector, httpConfig, request) ->
|
||||
{
|
||||
EndPoint endPoint = request.getHttpChannel().getEndPoint();
|
||||
SslConnection.DecryptedEndPoint sslEndPoint = (SslConnection.DecryptedEndPoint)endPoint;
|
||||
SslConnection sslConnection = sslEndPoint.getSslConnection();
|
||||
SSLEngine sslEngine = sslConnection.getSSLEngine();
|
||||
SSLSession session = sslEngine.getSession();
|
||||
for (Certificate c : session.getLocalCertificates())
|
||||
{
|
||||
request.getResponse().getHttpFields().add("X-CERT", ((X509Certificate)c).getSubjectDN().toString());
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||
sslConfig.accept(sslContextFactory);
|
||||
config.accept(sslContextFactory, secureRequestCustomizer);
|
||||
|
||||
File keystoreFile = sslContextFactory.getKeyStoreResource().getFile();
|
||||
if (!keystoreFile.exists())
|
||||
|
@ -133,10 +118,10 @@ public class SniSslConnectionFactoryTest
|
|||
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
|
||||
ServerConnector https = _connector = new ServerConnector(_server,
|
||||
_connector = new ServerConnector(_server,
|
||||
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
|
||||
new HttpConnectionFactory(_httpsConfiguration));
|
||||
_server.addConnector(https);
|
||||
new HttpConnectionFactory(httpConfiguration));
|
||||
_server.addConnector(_connector);
|
||||
|
||||
_server.setHandler(new AbstractHandler()
|
||||
{
|
||||
|
@ -151,36 +136,31 @@ public class SniSslConnectionFactoryTest
|
|||
});
|
||||
|
||||
_server.start();
|
||||
_port = https.getLocalPort();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() throws Exception
|
||||
public void after()
|
||||
{
|
||||
if (_server != null)
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnect() throws Exception
|
||||
{
|
||||
start("src/test/resources/keystore_sni.p12");
|
||||
String response = getResponse("127.0.0.1", null);
|
||||
assertThat(response, Matchers.containsString("X-HOST: 127.0.0.1"));
|
||||
LifeCycle.stop(_server);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSNIConnectNoWild() throws Exception
|
||||
{
|
||||
start("src/test/resources/keystore_sni_nowild.p12");
|
||||
start((ssl, customizer) ->
|
||||
{
|
||||
// Disable the host check because this keystore has no CN and no SAN.
|
||||
ssl.setKeyStorePath("src/test/resources/keystore_sni_nowild.p12");
|
||||
customizer.setSniHostCheck(false);
|
||||
});
|
||||
|
||||
String response = getResponse("www.acme.org", null);
|
||||
assertThat(response, Matchers.containsString("X-HOST: www.acme.org"));
|
||||
assertThat(response, Matchers.containsString("X-Cert: OU=default"));
|
||||
assertThat(response, Matchers.containsString("X-CERT: OU=default"));
|
||||
|
||||
response = getResponse("www.example.com", null);
|
||||
assertThat(response, Matchers.containsString("X-HOST: www.example.com"));
|
||||
assertThat(response, Matchers.containsString("X-Cert: OU=example"));
|
||||
assertThat(response, Matchers.containsString("X-CERT: OU=example"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -202,6 +182,9 @@ public class SniSslConnectionFactoryTest
|
|||
|
||||
response = getResponse("www.san.com", "san example");
|
||||
assertThat(response, Matchers.containsString("X-HOST: www.san.com"));
|
||||
|
||||
response = getResponse("wrongHost", "wrongHost", null);
|
||||
assertThat(response, Matchers.containsString("HTTP/1.1 400 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -226,7 +209,7 @@ public class SniSslConnectionFactoryTest
|
|||
|
||||
String response = getResponse("www.example.com", "some.other.com", "www.example.com");
|
||||
assertThat(response, Matchers.containsString("HTTP/1.1 400 "));
|
||||
assertThat(response, Matchers.containsString("Host does not match SNI"));
|
||||
assertThat(response, Matchers.containsString("Invalid SNI"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -249,15 +232,12 @@ public class SniSslConnectionFactoryTest
|
|||
@Test
|
||||
public void testWrongSNIRejectedBadRequest() throws Exception
|
||||
{
|
||||
start(ssl ->
|
||||
start((ssl, customizer) ->
|
||||
{
|
||||
ssl.setKeyStorePath("src/test/resources/keystore_sni.p12");
|
||||
// Do not allow unmatched SNI.
|
||||
ssl.setSniRequired(false);
|
||||
_httpsConfiguration.getCustomizers().stream()
|
||||
.filter(SecureRequestCustomizer.class::isInstance)
|
||||
.map(SecureRequestCustomizer.class::cast)
|
||||
.forEach(src -> src.setSniRequired(true));
|
||||
customizer.setSniRequired(true);
|
||||
});
|
||||
|
||||
// Wrong SNI host.
|
||||
|
@ -274,7 +254,7 @@ public class SniSslConnectionFactoryTest
|
|||
@Test
|
||||
public void testWrongSNIRejectedFunction() throws Exception
|
||||
{
|
||||
start(ssl ->
|
||||
start((ssl, customizer) ->
|
||||
{
|
||||
ssl.setKeyStorePath("src/test/resources/keystore_sni.p12");
|
||||
// Do not allow unmatched SNI.
|
||||
|
@ -285,10 +265,7 @@ public class SniSslConnectionFactoryTest
|
|||
return SniX509ExtendedKeyManager.SniSelector.DELEGATE;
|
||||
return ssl.sniSelect(keyType, issuers, session, sniHost, certificates);
|
||||
});
|
||||
_httpsConfiguration.getCustomizers().stream()
|
||||
.filter(SecureRequestCustomizer.class::isInstance)
|
||||
.map(SecureRequestCustomizer.class::cast)
|
||||
.forEach(src -> src.setSniRequired(true));
|
||||
customizer.setSniRequired(true);
|
||||
});
|
||||
|
||||
// Wrong SNI host.
|
||||
|
@ -318,7 +295,6 @@ public class SniSslConnectionFactoryTest
|
|||
|
||||
// Good SNI host.
|
||||
HttpTester.Response response = HttpTester.parseResponse(getResponse("localhost", "localhost", null));
|
||||
|
||||
assertNotNull(response);
|
||||
assertThat(response.getStatus(), is(200));
|
||||
}
|
||||
|
@ -332,7 +308,7 @@ public class SniSslConnectionFactoryTest
|
|||
SslContextFactory clientContextFactory = new SslContextFactory.Client(true);
|
||||
clientContextFactory.start();
|
||||
SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
|
||||
try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port))
|
||||
try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _connector.getLocalPort()))
|
||||
{
|
||||
SNIHostName serverName = new SNIHostName("m.san.com");
|
||||
SSLParameters params = sslSocket.getSSLParameters();
|
||||
|
@ -376,7 +352,7 @@ public class SniSslConnectionFactoryTest
|
|||
response = HttpTester.parseResponse(input);
|
||||
assertNotNull(response);
|
||||
assertThat(response.getStatus(), is(400));
|
||||
assertThat(response.getContent(), containsString("Host does not match SNI"));
|
||||
assertThat(response.getContent(), containsString("Invalid SNI"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -392,7 +368,7 @@ public class SniSslConnectionFactoryTest
|
|||
SslContextFactory clientContextFactory = new SslContextFactory.Client(true);
|
||||
clientContextFactory.start();
|
||||
SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
|
||||
try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port))
|
||||
try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _connector.getLocalPort()))
|
||||
{
|
||||
SNIHostName serverName = new SNIHostName("www.domain.com");
|
||||
SSLParameters params = sslSocket.getSSLParameters();
|
||||
|
@ -436,7 +412,7 @@ public class SniSslConnectionFactoryTest
|
|||
response = HttpTester.parseResponse(input);
|
||||
assertNotNull(response);
|
||||
assertThat(response.getStatus(), is(400));
|
||||
assertThat(response.getContent(), containsString("Host does not match SNI"));
|
||||
assertThat(response.getContent(), containsString("Invalid SNI"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -449,7 +425,7 @@ public class SniSslConnectionFactoryTest
|
|||
{
|
||||
start("src/test/resources/keystore_sni.p12");
|
||||
|
||||
final Queue<String> history = new LinkedBlockingQueue<>();
|
||||
Queue<String> history = new LinkedBlockingQueue<>();
|
||||
|
||||
_connector.addBean(new SocketCustomizationListener()
|
||||
{
|
||||
|
@ -478,8 +454,8 @@ public class SniSslConnectionFactoryTest
|
|||
}
|
||||
});
|
||||
|
||||
String response = getResponse("127.0.0.1", null);
|
||||
assertThat(response, Matchers.containsString("X-HOST: 127.0.0.1"));
|
||||
String response = getResponse("www.example.com", null);
|
||||
assertThat(response, Matchers.containsString("X-HOST: www.example.com"));
|
||||
|
||||
assertEquals("customize connector class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll());
|
||||
assertEquals("customize ssl class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll());
|
||||
|
@ -501,7 +477,7 @@ public class SniSslConnectionFactoryTest
|
|||
SslContextFactory clientContextFactory = new SslContextFactory.Client(true);
|
||||
clientContextFactory.start();
|
||||
SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory();
|
||||
try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port))
|
||||
try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _connector.getLocalPort()))
|
||||
{
|
||||
if (sniHost != null)
|
||||
{
|
||||
|
@ -521,7 +497,7 @@ public class SniSslConnectionFactoryTest
|
|||
assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn=" + cn));
|
||||
}
|
||||
|
||||
String response = "GET /ctx/path HTTP/1.0\r\nHost: " + reqHost + ":" + _port + "\r\n\r\n";
|
||||
String response = "GET /ctx/path HTTP/1.0\r\nHost: " + reqHost + ":" + _connector.getLocalPort() + "\r\n\r\n";
|
||||
sslSocket.getOutputStream().write(response.getBytes(StandardCharsets.ISO_8859_1));
|
||||
return IO.toString(sslSocket.getInputStream());
|
||||
}
|
||||
|
|
|
@ -75,7 +75,9 @@ public class SslContextFactoryReloadTest
|
|||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration();
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
SecureRequestCustomizer customizer = new SecureRequestCustomizer();
|
||||
customizer.setSniHostCheck(false);
|
||||
httpsConfig.addCustomizer(customizer);
|
||||
connector = new ServerConnector(server,
|
||||
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
|
||||
new HttpConnectionFactory(httpsConfig));
|
||||
|
|
Binary file not shown.
|
@ -30,6 +30,8 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.net.ssl.ExtendedSSLSession;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.SNIMatcher;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
@ -47,7 +49,6 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
|
||||
{
|
||||
public static final String SNI_X509 = "org.eclipse.jetty.util.ssl.snix509";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SniX509ExtendedKeyManager.class);
|
||||
|
||||
private final X509ExtendedKeyManager _delegate;
|
||||
|
@ -116,14 +117,29 @@ public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
|
|||
Arrays.stream(mangledAliases)
|
||||
.forEach(alias -> aliasMap.put(getAliasMapper().apply(alias), alias));
|
||||
|
||||
// Find our SNIMatcher. There should only be one and it always matches (always returns true
|
||||
// from AliasSNIMatcher.matches), but it will capture the SNI Host if one was presented.
|
||||
String host = matchers == null ? null : matchers.stream()
|
||||
.filter(SslContextFactory.AliasSNIMatcher.class::isInstance)
|
||||
.map(SslContextFactory.AliasSNIMatcher.class::cast)
|
||||
.findFirst()
|
||||
.map(SslContextFactory.AliasSNIMatcher::getHost)
|
||||
.orElse(null);
|
||||
String host = null;
|
||||
if (session instanceof ExtendedSSLSession)
|
||||
{
|
||||
host = ((ExtendedSSLSession)session).getRequestedServerNames().stream()
|
||||
.findAny()
|
||||
.filter(SNIHostName.class::isInstance)
|
||||
.map(SNIHostName.class::cast)
|
||||
.map(SNIHostName::getAsciiName)
|
||||
.orElse(null);
|
||||
}
|
||||
if (host == null)
|
||||
{
|
||||
// Find our SNIMatcher. There should only be one and it always matches (always returns true
|
||||
// from AliasSNIMatcher.matches), but it will capture the SNI Host if one was presented.
|
||||
host = matchers == null ? null : matchers.stream()
|
||||
.filter(SslContextFactory.AliasSNIMatcher.class::isInstance)
|
||||
.map(SslContextFactory.AliasSNIMatcher.class::cast)
|
||||
.findFirst()
|
||||
.map(SslContextFactory.AliasSNIMatcher::getHost)
|
||||
.orElse(null);
|
||||
}
|
||||
if (session != null && host != null)
|
||||
session.putValue(SslContextFactory.Server.SNI_HOST, host);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -152,9 +168,6 @@ public class SniX509ExtendedKeyManager extends X509ExtendedKeyManager
|
|||
return null;
|
||||
}
|
||||
|
||||
if (session != null)
|
||||
session.putValue(SNI_X509, x509);
|
||||
|
||||
// Convert the selected alias back to the original
|
||||
// value before the alias mapping performed above.
|
||||
String mangledAlias = aliasMap.get(alias);
|
||||
|
|
|
@ -1864,7 +1864,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
|
|||
sslParams.setEndpointIdentificationAlgorithm(getEndpointIdentificationAlgorithm());
|
||||
sslParams.setUseCipherSuitesOrder(isUseCipherSuitesOrder());
|
||||
if (!_certHosts.isEmpty() || !_certWilds.isEmpty())
|
||||
sslParams.setSNIMatchers(Collections.singletonList(new AliasSNIMatcher()));
|
||||
sslParams.setSNIMatchers(List.of(new AliasSNIMatcher()));
|
||||
if (_selectedCipherSuites != null)
|
||||
sslParams.setCipherSuites(_selectedCipherSuites);
|
||||
if (_selectedProtocols != null)
|
||||
|
@ -2032,7 +2032,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
|
|||
}
|
||||
}
|
||||
|
||||
class AliasSNIMatcher extends SNIMatcher
|
||||
static class AliasSNIMatcher extends SNIMatcher
|
||||
{
|
||||
private String _host;
|
||||
|
||||
|
@ -2095,6 +2095,8 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
|
|||
@ManagedObject
|
||||
public static class Server extends SslContextFactory implements SniX509ExtendedKeyManager.SniSelector
|
||||
{
|
||||
public static final String SNI_HOST = "org.eclipse.jetty.util.ssl.sniHost";
|
||||
|
||||
private boolean _needClientAuth;
|
||||
private boolean _wantClientAuth;
|
||||
private boolean _sniRequired;
|
||||
|
|
|
@ -18,14 +18,13 @@
|
|||
|
||||
package org.eclipse.jetty.util.ssl;
|
||||
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.net.InetAddress;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import javax.naming.ldap.Rdn;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
@ -37,17 +36,15 @@ import org.slf4j.LoggerFactory;
|
|||
public class X509
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(X509.class);
|
||||
|
||||
/*
|
||||
* @see {@link X509Certificate#getKeyUsage()}
|
||||
*/
|
||||
private static final int KEY_USAGE__KEY_CERT_SIGN = 5;
|
||||
|
||||
/*
|
||||
*
|
||||
* @see {@link X509Certificate#getSubjectAlternativeNames()}
|
||||
*/
|
||||
private static final int SUBJECT_ALTERNATIVE_NAMES__DNS_NAME = 2;
|
||||
private static final int SUBJECT_ALTERNATIVE_NAMES__IP_ADDRESS = 7;
|
||||
|
||||
public static boolean isCertSign(X509Certificate x509)
|
||||
{
|
||||
|
@ -63,39 +60,62 @@ public class X509
|
|||
private final String _alias;
|
||||
private final Set<String> _hosts = new LinkedHashSet<>();
|
||||
private final Set<String> _wilds = new LinkedHashSet<>();
|
||||
private final Set<InetAddress> _addresses = new LinkedHashSet<>();
|
||||
|
||||
public X509(String alias, X509Certificate x509) throws CertificateParsingException, InvalidNameException
|
||||
public X509(String alias, X509Certificate x509)
|
||||
{
|
||||
_alias = alias;
|
||||
_x509 = x509;
|
||||
|
||||
// Look for alternative name extensions
|
||||
Collection<List<?>> altNames = x509.getSubjectAlternativeNames();
|
||||
if (altNames != null)
|
||||
try
|
||||
{
|
||||
for (List<?> list : altNames)
|
||||
// Look for alternative name extensions
|
||||
Collection<List<?>> altNames = x509.getSubjectAlternativeNames();
|
||||
if (altNames != null)
|
||||
{
|
||||
if (((Number)list.get(0)).intValue() == SUBJECT_ALTERNATIVE_NAMES__DNS_NAME)
|
||||
for (List<?> list : altNames)
|
||||
{
|
||||
String cn = list.get(1).toString();
|
||||
int nameType = ((Number)list.get(0)).intValue();
|
||||
switch (nameType)
|
||||
{
|
||||
case SUBJECT_ALTERNATIVE_NAMES__DNS_NAME:
|
||||
{
|
||||
String name = list.get(1).toString();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Certificate alias={} SAN dns={} in {}", alias, name, this);
|
||||
addName(name);
|
||||
break;
|
||||
}
|
||||
case SUBJECT_ALTERNATIVE_NAMES__IP_ADDRESS:
|
||||
{
|
||||
String address = list.get(1).toString();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Certificate alias={} SAN ip={} in {}", alias, address, this);
|
||||
addAddress(address);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no names found, look up the CN from the subject
|
||||
LdapName name = new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253));
|
||||
for (Rdn rdn : name.getRdns())
|
||||
{
|
||||
if (rdn.getType().equalsIgnoreCase("CN"))
|
||||
{
|
||||
String cn = rdn.getValue().toString();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Certificate SAN alias={} CN={} in {}", alias, cn, this);
|
||||
LOG.debug("Certificate CN alias={} CN={} in {}", alias, cn, this);
|
||||
addName(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no names found, look up the CN from the subject
|
||||
LdapName name = new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253));
|
||||
for (Rdn rdn : name.getRdns())
|
||||
catch (Exception x)
|
||||
{
|
||||
if (rdn.getType().equalsIgnoreCase("CN"))
|
||||
{
|
||||
String cn = rdn.getValue().toString();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Certificate CN alias={} CN={} in {}", alias, cn, this);
|
||||
addName(cn);
|
||||
}
|
||||
throw new IllegalArgumentException(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +131,28 @@ public class X509
|
|||
}
|
||||
}
|
||||
|
||||
private void addAddress(String host)
|
||||
{
|
||||
// Class InetAddress handles IPV6 brackets and IPv6 short forms, so that [::1]
|
||||
// would match 0:0:0:0:0:0:0:1 as well as 0000:0000:0000:0000:0000:0000:0000:0001.
|
||||
InetAddress address = toInetAddress(host);
|
||||
if (address != null)
|
||||
_addresses.add(address);
|
||||
}
|
||||
|
||||
private InetAddress toInetAddress(String address)
|
||||
{
|
||||
try
|
||||
{
|
||||
return InetAddress.getByName(address);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.trace("IGNORED", x);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getAlias()
|
||||
{
|
||||
return _alias;
|
||||
|
@ -144,6 +186,11 @@ public class X509
|
|||
if (_wilds.contains(domain))
|
||||
return true;
|
||||
}
|
||||
|
||||
InetAddress address = toInetAddress(host);
|
||||
if (address != null)
|
||||
return _addresses.contains(address);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ public class WebSocketTester
|
|||
protected static HttpFields.Mutable newUpgradeRequest(String extensions)
|
||||
{
|
||||
HttpFields.Mutable fields = HttpFields.build()
|
||||
.add(HttpHeader.HOST, "127.0.0.1")
|
||||
.add(HttpHeader.HOST, "localhost")
|
||||
.add(HttpHeader.UPGRADE, "websocket")
|
||||
.add(HttpHeader.CONNECTION, "Upgrade")
|
||||
.add(HttpHeader.SEC_WEBSOCKET_KEY, NON_RANDOM_KEY)
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.eclipse.jetty.http.HttpMethod;
|
|||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http2.FlowControlStrategy;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.Net;
|
||||
|
@ -349,6 +350,10 @@ public class HttpClientTest extends AbstractTest<TransportScenario>
|
|||
Assumptions.assumeTrue(scenario.transport.isTlsBased());
|
||||
|
||||
scenario.startServer(new EmptyServerHandler());
|
||||
// Disable validations on the server to be sure
|
||||
// that the test failure happens during the
|
||||
// validation of the certificate on the client.
|
||||
scenario.httpConfig.getCustomizer(SecureRequestCustomizer.class).setSniHostCheck(false);
|
||||
|
||||
// Use a default SslContextFactory, requests should fail because the server certificate is unknown.
|
||||
SslContextFactory.Client clientTLS = scenario.newClientSslContextFactory();
|
||||
|
@ -361,9 +366,9 @@ public class HttpClientTest extends AbstractTest<TransportScenario>
|
|||
|
||||
assertThrows(ExecutionException.class, () ->
|
||||
{
|
||||
// Use IP address since the certificate contains a host name.
|
||||
// Use an IP address not present in the certificate.
|
||||
int serverPort = ((ServerConnector)scenario.connector).getLocalPort();
|
||||
scenario.client.newRequest("https://127.0.0.1:" + serverPort)
|
||||
scenario.client.newRequest("https://127.0.0.2:" + serverPort)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
});
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue