Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPCLIENT-2151: Support for JSSE in-built endpoint identification #545

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,24 @@
import static org.hamcrest.MatcherAssert.assertThat;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.client5.http.ssl.HostnameVerificationPolicy;
import org.apache.hc.client5.http.ssl.HttpsSupport;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
import org.apache.hc.client5.testing.SSLTestContexts;
Expand All @@ -54,6 +60,8 @@
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -345,4 +353,111 @@ private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Ex
context);
}
}

@Test
public void testHostnameVerificationClient() throws Exception {
// @formatter:off
this.server = ServerBootstrap.bootstrap()
.setSslContext(SSLTestContexts.createServerSSLContext())
.create();
// @formatter:on
this.server.start();

final HttpHost target1 = new HttpHost("https", "localhost", server.getLocalPort());

try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
SSLTestContexts.createClientSSLContext(),
HostnameVerificationPolicy.CLIENT,
HttpsSupport.getDefaultHostnameVerifier());
final HttpContext context = new BasicHttpContext();
final SSLSocket upgradedSocket = tlsStrategy.upgrade(
socket,
target1.getHostName(),
target1.getPort(),
null,
context);
final SSLSession session = upgradedSocket.getSession();
MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
}

final HttpHost target2 = new HttpHost("https", "some-other-host", server.getLocalPort());

try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
SSLTestContexts.createClientSSLContext(),
HostnameVerificationPolicy.CLIENT,
HttpsSupport.getDefaultHostnameVerifier());
final HttpContext context = new BasicHttpContext();
Assertions.assertThrows(SSLPeerUnverifiedException.class, () ->
tlsStrategy.upgrade(
socket,
target2.getHostName(),
target2.getPort(),
null,
context));
}

try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
SSLTestContexts.createClientSSLContext(),
HostnameVerificationPolicy.CLIENT,
NoopHostnameVerifier.INSTANCE);
final HttpContext context = new BasicHttpContext();
final SSLSocket upgradedSocket = tlsStrategy.upgrade(
socket,
target1.getHostName(),
target1.getPort(),
null,
context);
final SSLSession session = upgradedSocket.getSession();
MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
}
}

@Test
public void testHostnameVerificationBuiltIn() throws Exception {
// @formatter:off
this.server = ServerBootstrap.bootstrap()
.setSslContext(SSLTestContexts.createServerSSLContext())
.create();
// @formatter:on
this.server.start();

final HttpHost target1 = new HttpHost("https", "localhost", server.getLocalPort());

try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
SSLTestContexts.createClientSSLContext(),
HostnameVerificationPolicy.BUILTIN,
NoopHostnameVerifier.INSTANCE);
final HttpContext context = new BasicHttpContext();
final SSLSocket upgradedSocket = tlsStrategy.upgrade(
socket,
target1.getHostName(),
target1.getPort(),
null,
context);
final SSLSession session = upgradedSocket.getSession();
MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
}

final HttpHost target2 = new HttpHost("https", "some-other-host", server.getLocalPort());

try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
SSLTestContexts.createClientSSLContext(),
HostnameVerificationPolicy.BUILTIN,
NoopHostnameVerifier.INSTANCE);
final HttpContext context = new BasicHttpContext();
Assertions.assertThrows(SSLHandshakeException.class, () ->
tlsStrategy.upgrade(
socket,
target2.getHostName(),
target2.getPort(),
null,
context));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.ssl.TLS;
Expand All @@ -79,20 +80,24 @@ abstract class AbstractClientTlsStrategy implements TlsStrategy, TlsSocketStrate
private final String[] supportedProtocols;
private final String[] supportedCipherSuites;
private final SSLBufferMode sslBufferManagement;
private final HostnameVerificationPolicy hostnameVerificationPolicy;
private final HostnameVerifier hostnameVerifier;

AbstractClientTlsStrategy(
final SSLContext sslContext,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final SSLBufferMode sslBufferManagement,
final HostnameVerificationPolicy hostnameVerificationPolicy,
final HostnameVerifier hostnameVerifier) {
super();
this.sslContext = Args.notNull(sslContext, "SSL context");
this.supportedProtocols = supportedProtocols;
this.supportedCipherSuites = supportedCipherSuites;
this.sslBufferManagement = sslBufferManagement != null ? sslBufferManagement : SSLBufferMode.STATIC;
this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier();
this.hostnameVerificationPolicy = hostnameVerificationPolicy != null ? hostnameVerificationPolicy : HostnameVerificationPolicy.BOTH;
this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier :
(this.hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN ? NoopHostnameVerifier.INSTANCE : HttpsSupport.getDefaultHostnameVerifier());
}

/**
Expand Down Expand Up @@ -147,6 +152,10 @@ public void upgrade(

applyParameters(sslEngine, sslParameters, H2TlsSupport.selectApplicationProtocols(versionPolicy));

if (hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH) {
sslParameters.setEndpointIdentificationAlgorithm(URIScheme.HTTPS.id);
}

initializeEngine(sslEngine);

if (LOG.isDebugEnabled()) {
Expand Down Expand Up @@ -181,7 +190,8 @@ protected void initializeSocket(final SSLSocket socket) {
protected void verifySession(
final String hostname,
final SSLSession sslsession) throws SSLException {
verifySession(hostname, sslsession, hostnameVerifier);
verifySession(hostname, sslsession,
hostnameVerificationPolicy == HostnameVerificationPolicy.CLIENT || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH ? hostnameVerifier : null);
}

@Override
Expand All @@ -204,16 +214,23 @@ private void executeHandshake(
final String target,
final Object attachment) throws IOException {
final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;

final SSLParameters sslParameters = upgradedSocket.getSSLParameters();
if (supportedProtocols != null) {
upgradedSocket.setEnabledProtocols(supportedProtocols);
sslParameters.setProtocols(supportedProtocols);
} else {
upgradedSocket.setEnabledProtocols((TLS.excludeWeak(upgradedSocket.getEnabledProtocols())));
sslParameters.setProtocols((TLS.excludeWeak(upgradedSocket.getEnabledProtocols())));
}
if (supportedCipherSuites != null) {
upgradedSocket.setEnabledCipherSuites(supportedCipherSuites);
sslParameters.setCipherSuites(supportedCipherSuites);
} else {
upgradedSocket.setEnabledCipherSuites(TlsCiphers.excludeWeak(upgradedSocket.getEnabledCipherSuites()));
sslParameters.setCipherSuites(TlsCiphers.excludeWeak(upgradedSocket.getEnabledCipherSuites()));
}
if (hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH) {
sslParameters.setEndpointIdentificationAlgorithm(URIScheme.HTTPS.id);
}
upgradedSocket.setSSLParameters(sslParameters);

final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
if (handshakeTimeout != null) {
upgradedSocket.setSoTimeout(handshakeTimeout.toMillisecondsIntBound());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,8 @@ public static ClientTlsStrategyBuilder create() {
private String[] tlsVersions;
private String[] ciphers;
private SSLBufferMode sslBufferMode;
private HostnameVerificationPolicy hostnameVerificationPolicy;
private HostnameVerifier hostnameVerifier;
/**
* @deprecated To be removed.
*/
@Deprecated
private Factory<SSLEngine, TlsDetails> tlsDetailsFactory;
private boolean systemProperties;

/**
Expand Down Expand Up @@ -125,6 +121,13 @@ public ClientTlsStrategyBuilder setSslBufferMode(final SSLBufferMode sslBufferMo
return this;
}

/**
* Assigns {@link HostnameVerificationPolicy} value.
*/
public void setHostnameVerificationPolicy(final HostnameVerificationPolicy hostnameVerificationPolicy) {
this.hostnameVerificationPolicy = hostnameVerificationPolicy;
}

/**
* Assigns {@link HostnameVerifier} instance.
*/
Expand All @@ -136,11 +139,10 @@ public ClientTlsStrategyBuilder setHostnameVerifier(final HostnameVerifier hostn
/**
* Assigns {@link TlsDetails} {@link Factory} instance.
*
* @deprecated Do not use.
* @deprecated Do not use. This method has no effect.
*/
@Deprecated
public ClientTlsStrategyBuilder setTlsDetailsFactory(final Factory<SSLEngine, TlsDetails> tlsDetailsFactory) {
this.tlsDetailsFactory = tlsDetailsFactory;
return this;
}

Expand All @@ -153,7 +155,6 @@ public final ClientTlsStrategyBuilder useSystemProperties() {
return this;
}

@SuppressWarnings("deprecation")
public TlsStrategy build() {
final SSLContext sslContextCopy;
if (sslContext != null) {
Expand All @@ -173,13 +174,18 @@ public TlsStrategy build() {
} else {
ciphersCopy = systemProperties ? HttpsSupport.getSystemCipherSuits() : null;
}
final HostnameVerificationPolicy hostnameVerificationPolicyCopy = hostnameVerificationPolicy != null ? hostnameVerificationPolicy :
(hostnameVerifier == null ? HostnameVerificationPolicy.BUILTIN : HostnameVerificationPolicy.BOTH);
final HostnameVerifier hostnameVerifierCopy = hostnameVerifier != null ? hostnameVerifier :
(hostnameVerificationPolicyCopy == HostnameVerificationPolicy.CLIENT || hostnameVerificationPolicyCopy == HostnameVerificationPolicy.BOTH ?
HttpsSupport.getDefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE);
return new DefaultClientTlsStrategy(
sslContextCopy,
tlsVersionsCopy,
ciphersCopy,
sslBufferMode != null ? sslBufferMode : SSLBufferMode.STATIC,
hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier(),
tlsDetailsFactory);
hostnameVerificationPolicyCopy,
hostnameVerifierCopy);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class ConscryptClientTlsStrategy extends AbstractClientTlsStrategy {
public static TlsStrategy getDefault() {
return new ConscryptClientTlsStrategy(
SSLContexts.createDefault(),
HostnameVerificationPolicy.BOTH,
HttpsSupport.getDefaultHostnameVerifier());
}

Expand All @@ -63,6 +64,7 @@ public static TlsStrategy getSystemDefault() {
HttpsSupport.getSystemProtocols(),
HttpsSupport.getSystemCipherSuits(),
SSLBufferMode.STATIC,
HostnameVerificationPolicy.BOTH,
HttpsSupport.getDefaultHostnameVerifier());
}

Expand All @@ -72,7 +74,20 @@ public ConscryptClientTlsStrategy(
final String[] supportedCipherSuites,
final SSLBufferMode sslBufferManagement,
final HostnameVerifier hostnameVerifier) {
super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerifier);
this(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, HostnameVerificationPolicy.CLIENT, hostnameVerifier);
}

/**
* @since 5.4
*/
public ConscryptClientTlsStrategy(
final SSLContext sslContext,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final SSLBufferMode sslBufferManagement,
final HostnameVerificationPolicy hostnameVerificationPolicy,
final HostnameVerifier hostnameVerifier) {
super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerificationPolicy, hostnameVerifier);
}

public ConscryptClientTlsStrategy(
Expand All @@ -81,6 +96,16 @@ public ConscryptClientTlsStrategy(
this(sslContext, null, null, SSLBufferMode.STATIC, hostnameVerifier);
}

/**
* @since 5.4
*/
public ConscryptClientTlsStrategy(
final SSLContext sslContext,
final HostnameVerificationPolicy hostnameVerificationPolicy,
final HostnameVerifier hostnameVerifier) {
this(sslContext, null, null, SSLBufferMode.STATIC, hostnameVerificationPolicy, hostnameVerifier);
}

public ConscryptClientTlsStrategy(final SSLContext sslContext) {
this(sslContext, HttpsSupport.getDefaultHostnameVerifier());
}
Expand Down
Loading
Loading