Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.
This commit is contained in:
commit
6d44b13945
|
@ -33,11 +33,10 @@ import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
|
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
|
||||||
import org.eclipse.jetty.io.ssl.ALPNProcessor.Client;
|
import org.eclipse.jetty.io.ssl.ALPNProcessor.Client;
|
||||||
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
|
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
|
||||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory implements SslHandshakeListener
|
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class);
|
private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
@ -325,7 +326,6 @@ public class HttpClientTLSTest
|
||||||
@Test
|
@Test
|
||||||
public void testHandshakeSucceededWithSessionResumption() throws Exception
|
public void testHandshakeSucceededWithSessionResumption() throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||||
|
|
||||||
|
@ -405,7 +405,6 @@ public class HttpClientTLSTest
|
||||||
@Test
|
@Test
|
||||||
public void testClientRawCloseDoesNotInvalidateSession() throws Exception
|
public void testClientRawCloseDoesNotInvalidateSession() throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||||
|
|
||||||
|
@ -527,4 +526,30 @@ public class HttpClientTLSTest
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHostNameVerificationFailure() throws Exception
|
||||||
|
{
|
||||||
|
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||||
|
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||||
|
|
||||||
|
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||||
|
// Make sure the host name is not verified at the TLS level.
|
||||||
|
clientTLSFactory.setEndpointIdentificationAlgorithm(null);
|
||||||
|
// Add host name verification after the TLS handshake.
|
||||||
|
clientTLSFactory.setHostnameVerifier((host, session) -> false);
|
||||||
|
startClient(clientTLSFactory);
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.scheme(HttpScheme.HTTPS.asString())
|
||||||
|
.send(result ->
|
||||||
|
{
|
||||||
|
Throwable failure = result.getFailure();
|
||||||
|
if (failure instanceof SSLPeerUnverifiedException)
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,10 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||||
|
@ -98,6 +101,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
|
||||||
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
|
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
|
||||||
appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
|
appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
|
||||||
|
|
||||||
|
sslConnection.addHandshakeListener(new HTTPSHandshakeListener(context));
|
||||||
customize(sslConnection, context);
|
customize(sslConnection, context);
|
||||||
|
|
||||||
return sslConnection;
|
return sslConnection;
|
||||||
|
@ -123,4 +127,37 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
|
||||||
}
|
}
|
||||||
return ClientConnectionFactory.super.customize(connection, context);
|
return ClientConnectionFactory.super.customize(connection, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class HTTPSHandshakeListener implements SslHandshakeListener
|
||||||
|
{
|
||||||
|
private final Map<String, Object> context;
|
||||||
|
|
||||||
|
private HTTPSHandshakeListener(Map<String, Object> context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handshakeSucceeded(Event event) throws SSLException
|
||||||
|
{
|
||||||
|
HostnameVerifier verifier = sslContextFactory.getHostnameVerifier();
|
||||||
|
if (verifier != null)
|
||||||
|
{
|
||||||
|
String host = (String)context.get(SSL_PEER_HOST_CONTEXT_KEY);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!verifier.verify(host, event.getSSLEngine().getSession()))
|
||||||
|
throw new SSLPeerUnverifiedException("Host name verification failed for host: " + host);
|
||||||
|
}
|
||||||
|
catch (SSLException x)
|
||||||
|
{
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
throw (SSLException)new SSLPeerUnverifiedException("Host name verification failed for host: " + host).initCause(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -759,7 +759,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handshakeSucceeded()
|
private void handshakeSucceeded() throws SSLException
|
||||||
{
|
{
|
||||||
if (_handshake.compareAndSet(Handshake.INITIAL, Handshake.SUCCEEDED))
|
if (_handshake.compareAndSet(Handshake.INITIAL, Handshake.SUCCEEDED))
|
||||||
{
|
{
|
||||||
|
@ -1182,7 +1182,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyHandshakeSucceeded(SSLEngine sslEngine)
|
private void notifyHandshakeSucceeded(SSLEngine sslEngine) throws SSLException
|
||||||
{
|
{
|
||||||
SslHandshakeListener.Event event = null;
|
SslHandshakeListener.Event event = null;
|
||||||
for (SslHandshakeListener listener : handshakeListeners)
|
for (SslHandshakeListener listener : handshakeListeners)
|
||||||
|
@ -1193,6 +1193,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
{
|
{
|
||||||
listener.handshakeSucceeded(event);
|
listener.handshakeSucceeded(event);
|
||||||
}
|
}
|
||||||
|
catch (SSLException x)
|
||||||
|
{
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
LOG.info("Exception while notifying listener " + listener, x);
|
LOG.info("Exception while notifying listener " + listener, x);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.EventListener;
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Implementations of this interface are notified of TLS handshake events.</p>
|
* <p>Implementations of this interface are notified of TLS handshake events.</p>
|
||||||
|
@ -36,7 +37,7 @@ public interface SslHandshakeListener extends EventListener
|
||||||
*
|
*
|
||||||
* @param event the event object carrying information about the TLS handshake event
|
* @param event the event object carrying information about the TLS handshake event
|
||||||
*/
|
*/
|
||||||
default void handshakeSucceeded(Event event)
|
default void handshakeSucceeded(Event event) throws SSLException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.net.ssl.CertPathTrustManagerParameters;
|
import javax.net.ssl.CertPathTrustManagerParameters;
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SNIHostName;
|
import javax.net.ssl.SNIHostName;
|
||||||
|
@ -194,6 +195,7 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
|
||||||
private int _renegotiationLimit = 5;
|
private int _renegotiationLimit = 5;
|
||||||
private Factory _factory;
|
private Factory _factory;
|
||||||
private PKIXCertPathChecker _pkixCertPathChecker;
|
private PKIXCertPathChecker _pkixCertPathChecker;
|
||||||
|
private HostnameVerifier _hostnameVerifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an instance of SslContextFactory
|
* Construct an instance of SslContextFactory
|
||||||
|
@ -1565,6 +1567,31 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
|
||||||
_sslSessionTimeout = sslSessionTimeout;
|
_sslSessionTimeout = sslSessionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the HostnameVerifier used by a client to verify host names in the server certificate
|
||||||
|
*/
|
||||||
|
public HostnameVerifier getHostnameVerifier()
|
||||||
|
{
|
||||||
|
return _hostnameVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets a {@code HostnameVerifier} used by a client to verify host names in the server certificate.</p>
|
||||||
|
* <p>The {@code HostnameVerifier} works in conjunction with {@link #setEndpointIdentificationAlgorithm(String)}.</p>
|
||||||
|
* <p>When {@code endpointIdentificationAlgorithm=="HTTPS"} (the default) the JDK TLS implementation
|
||||||
|
* checks that the host name indication set by the client matches the host names in the server certificate.
|
||||||
|
* If this check passes successfully, the {@code HostnameVerifier} is invoked and the application
|
||||||
|
* can perform additional checks and allow/deny the connection to the server.</p>
|
||||||
|
* <p>When {@code endpointIdentificationAlgorithm==null} the JDK TLS implementation will not check
|
||||||
|
* the host names, and any check is therefore performed only by the {@code HostnameVerifier.}</p>
|
||||||
|
*
|
||||||
|
* @param hostnameVerifier the HostnameVerifier used by a client to verify host names in the server certificate
|
||||||
|
*/
|
||||||
|
public void setHostnameVerifier(HostnameVerifier hostnameVerifier)
|
||||||
|
{
|
||||||
|
_hostnameVerifier = hostnameVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the password object for the given realm.
|
* Returns the password object for the given realm.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue