Added optional dependency on conscrypt-openjdk-uber 1.4.1; support for advanced TLS functions (such as ALPN extension) on Java 1.7 and Java 1.8 through Conscrypt TLS library

This commit is contained in:
Oleg Kalnichevski 2018-12-10 13:18:41 +01:00
parent 5797e2475a
commit 836e953da0
6 changed files with 302 additions and 105 deletions

View File

@ -52,6 +52,12 @@
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.conscrypt</groupId>
<artifactId>conscrypt-openjdk-uber</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5-reactive</artifactId>

View File

@ -29,11 +29,13 @@ package org.apache.hc.client5.http.impl.nio;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.ssl.ConscryptClientTlsStrategy;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
import org.apache.hc.core5.pool.PoolReusePolicy;
import org.apache.hc.core5.util.ReflectionUtils;
import org.apache.hc.core5.util.TimeValue;
/**
@ -176,10 +178,18 @@ public class PoolingAsyncClientConnectionManagerBuilder {
if (tlsStrategy != null) {
tlsStrategyCopy = tlsStrategy;
} else {
if (systemProperties) {
tlsStrategyCopy = DefaultClientTlsStrategy.getSystemDefault();
if (ReflectionUtils.determineJRELevel() <= 8 && ConscryptClientTlsStrategy.isSupported()) {
if (systemProperties) {
tlsStrategyCopy = ConscryptClientTlsStrategy.getSystemDefault();
} else {
tlsStrategyCopy = ConscryptClientTlsStrategy.getDefault();
}
} else {
tlsStrategyCopy = DefaultClientTlsStrategy.getDefault();
if (systemProperties) {
tlsStrategyCopy = DefaultClientTlsStrategy.getSystemDefault();
} else {
tlsStrategyCopy = DefaultClientTlsStrategy.getDefault();
}
}
}
final PoolingAsyncClientConnectionManager poolingmgr = new PoolingAsyncClientConnectionManager(

View File

@ -0,0 +1,149 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.ssl;
import java.net.SocketAddress;
import java.util.Arrays;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.ssl.H2TlsSupport;
import org.apache.hc.core5.net.NamedEndpoint;
import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
import org.apache.hc.core5.reactor.ssl.TlsDetails;
import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
import org.apache.hc.core5.util.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Contract(threading = ThreadingBehavior.STATELESS)
abstract class AbstractClientTlsStrategy implements TlsStrategy {
private final Logger log = LoggerFactory.getLogger(getClass());
private final SSLContext sslContext;
private final String[] supportedProtocols;
private final String[] supportedCipherSuites;
private final SSLBufferMode sslBufferManagement;
private final HostnameVerifier hostnameVerifier;
private final TlsSessionValidator tlsSessionValidator;
AbstractClientTlsStrategy(
final SSLContext sslContext,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final SSLBufferMode sslBufferManagement,
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.tlsSessionValidator = new TlsSessionValidator(log);
}
@Override
public boolean upgrade(
final TransportSecurityLayer tlsSession,
final HttpHost host,
final SocketAddress localAddress,
final SocketAddress remoteAddress,
final Object attachment) {
tlsSession.startTls(sslContext, host, sslBufferManagement, new SSLSessionInitializer() {
@Override
public void initialize(final NamedEndpoint endpoint, final SSLEngine sslEngine) {
final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
(HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
final SSLParameters sslParameters = sslEngine.getSSLParameters();
if (supportedProtocols != null) {
sslParameters.setProtocols(supportedProtocols);
} else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
sslParameters.setProtocols(H2TlsSupport.excludeBlacklistedProtocols(sslParameters.getProtocols()));
}
if (supportedCipherSuites != null) {
sslParameters.setCipherSuites(supportedCipherSuites);
} else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
sslParameters.setCipherSuites(H2TlsSupport.excludeBlacklistedCiphers(sslParameters.getCipherSuites()));
}
if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
H2TlsSupport.setEnableRetransmissions(sslParameters, false);
}
applyParameters(sslEngine, sslParameters, H2TlsSupport.selectApplicationProtocols(attachment));
initializeEngine(sslEngine);
if (log.isDebugEnabled()) {
log.debug("Enabled protocols: " + Arrays.asList(sslEngine.getEnabledProtocols()));
log.debug("Enabled cipher suites:" + Arrays.asList(sslEngine.getEnabledCipherSuites()));
}
}
}, new SSLSessionVerifier() {
@Override
public TlsDetails verify(final NamedEndpoint endpoint, final SSLEngine sslEngine) throws SSLException {
verifySession(host.getHostName(), sslEngine.getSession());
return createTlsDetails(sslEngine);
}
});
return true;
}
abstract void applyParameters(SSLEngine sslEngine, SSLParameters sslParameters, String[] appProtocols);
abstract TlsDetails createTlsDetails(SSLEngine sslEngine);
protected void initializeEngine(final SSLEngine sslEngine) {
}
protected void verifySession(
final String hostname,
final SSLSession sslsession) throws SSLException {
tlsSessionValidator.verifySession(hostname, sslsession, hostnameVerifier);
}
}

View File

@ -0,0 +1,119 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.ssl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http2.ssl.H2TlsSupport;
import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
import org.apache.hc.core5.reactor.ssl.TlsDetails;
import org.apache.hc.core5.ssl.SSLContexts;
import org.conscrypt.Conscrypt;
/**
* TLS upgrade strategy for non-blocking client connections using Conscrypt TLS library.
*
* @since 5.0
*/
@Contract(threading = ThreadingBehavior.STATELESS)
public class ConscryptClientTlsStrategy extends AbstractClientTlsStrategy {
public static TlsStrategy getDefault() {
return new ConscryptClientTlsStrategy(
SSLContexts.createDefault(),
HttpsSupport.getDefaultHostnameVerifier());
}
public static TlsStrategy getSystemDefault() {
return new ConscryptClientTlsStrategy(
SSLContexts.createSystemDefault(),
HttpsSupport.getSystemProtocols(),
HttpsSupport.getSystemCipherSuits(),
SSLBufferMode.STATIC,
HttpsSupport.getDefaultHostnameVerifier());
}
public ConscryptClientTlsStrategy(
final SSLContext sslContext,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final SSLBufferMode sslBufferManagement,
final HostnameVerifier hostnameVerifier) {
super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerifier);
}
public ConscryptClientTlsStrategy(
final SSLContext sslcontext,
final HostnameVerifier hostnameVerifier) {
this(sslcontext, null, null, SSLBufferMode.STATIC, hostnameVerifier);
}
public ConscryptClientTlsStrategy(final SSLContext sslcontext) {
this(sslcontext, HttpsSupport.getDefaultHostnameVerifier());
}
@Override
void applyParameters(final SSLEngine sslEngine, final SSLParameters sslParameters, final String[] appProtocols) {
if (Conscrypt.isConscrypt(sslEngine)) {
sslEngine.setSSLParameters(sslParameters);
Conscrypt.setApplicationProtocols(sslEngine, appProtocols);
} else {
H2TlsSupport.setApplicationProtocols(sslParameters, appProtocols);
sslEngine.setSSLParameters(sslParameters);
}
}
@Override
TlsDetails createTlsDetails(final SSLEngine sslEngine) {
if (Conscrypt.isConscrypt(sslEngine)) {
return new TlsDetails(sslEngine.getSession(), Conscrypt.getApplicationProtocol(sslEngine));
} else {
return null;
}
}
public static boolean isSupported() {
try {
final Class<?> clazz = Class.forName("org.conscrypt.Conscrypt");
final Method method = clazz.getMethod("isAvailable");
return (Boolean) method.invoke(null);
} catch (final ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
return false;
}
}
}

View File

@ -27,42 +27,27 @@
package org.apache.hc.client5.http.ssl;
import java.net.SocketAddress;
import java.util.Arrays;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.function.Factory;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.ssl.ApplicationProtocols;
import org.apache.hc.core5.http2.ssl.H2TlsSupport;
import org.apache.hc.core5.net.NamedEndpoint;
import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
import org.apache.hc.core5.reactor.ssl.TlsDetails;
import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default SSL upgrade strategy for non-blocking client connections.
* TLS upgrade strategy for non-blocking client connections.
*
* @since 5.0
*/
@Contract(threading = ThreadingBehavior.STATELESS)
public class DefaultClientTlsStrategy implements TlsStrategy {
public class DefaultClientTlsStrategy extends AbstractClientTlsStrategy {
public static TlsStrategy getDefault() {
return new DefaultClientTlsStrategy(
@ -79,15 +64,7 @@ public class DefaultClientTlsStrategy implements TlsStrategy {
HttpsSupport.getDefaultHostnameVerifier());
}
private final Logger log = LoggerFactory.getLogger(getClass());
private final SSLContext sslContext;
private final String[] supportedProtocols;
private final String[] supportedCipherSuites;
private final SSLBufferMode sslBufferManagement;
private final HostnameVerifier hostnameVerifier;
private final Factory<SSLEngine, TlsDetails> tlsDetailsFactory;
private final TlsSessionValidator tlsSessionValidator;
public DefaultClientTlsStrategy(
final SSLContext sslContext,
@ -96,14 +73,8 @@ public class DefaultClientTlsStrategy implements TlsStrategy {
final SSLBufferMode sslBufferManagement,
final HostnameVerifier hostnameVerifier,
final Factory<SSLEngine, TlsDetails> tlsDetailsFactory) {
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();
super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerifier);
this.tlsDetailsFactory = tlsDetailsFactory;
this.tlsSessionValidator = new TlsSessionValidator(log);
}
public DefaultClientTlsStrategy(
@ -126,78 +97,14 @@ public class DefaultClientTlsStrategy implements TlsStrategy {
}
@Override
public boolean upgrade(
final TransportSecurityLayer tlsSession,
final HttpHost host,
final SocketAddress localAddress,
final SocketAddress remoteAddress,
final Object attachment) {
tlsSession.startTls(sslContext, host, sslBufferManagement, new SSLSessionInitializer() {
@Override
public void initialize(final NamedEndpoint endpoint, final SSLEngine sslEngine) {
final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
(HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
final SSLParameters sslParameters = sslEngine.getSSLParameters();
if (supportedProtocols != null) {
sslParameters.setProtocols(supportedProtocols);
} else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
sslParameters.setProtocols(H2TlsSupport.excludeBlacklistedProtocols(sslParameters.getProtocols()));
}
if (supportedCipherSuites != null) {
sslParameters.setCipherSuites(supportedCipherSuites);
} else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
sslParameters.setCipherSuites(H2TlsSupport.excludeBlacklistedCiphers(sslParameters.getCipherSuites()));
}
switch (versionPolicy) {
case FORCE_HTTP_1:
H2TlsSupport.setApplicationProtocols(sslParameters, new String[] {
ApplicationProtocols.HTTP_1_1.id });
break;
case FORCE_HTTP_2:
H2TlsSupport.setEnableRetransmissions(sslParameters, false);
H2TlsSupport.setApplicationProtocols(sslParameters, new String[] {
ApplicationProtocols.HTTP_2.id });
break;
case NEGOTIATE:
H2TlsSupport.setEnableRetransmissions(sslParameters, false);
H2TlsSupport.setApplicationProtocols(sslParameters, new String[] {
ApplicationProtocols.HTTP_2.id, ApplicationProtocols.HTTP_1_1.id });
break;
}
sslEngine.setSSLParameters(sslParameters);
initializeEngine(sslEngine);
if (log.isDebugEnabled()) {
log.debug("Enabled protocols: " + Arrays.asList(sslEngine.getEnabledProtocols()));
log.debug("Enabled cipher suites:" + Arrays.asList(sslEngine.getEnabledCipherSuites()));
}
}
}, new SSLSessionVerifier() {
@Override
public TlsDetails verify(final NamedEndpoint endpoint, final SSLEngine sslEngine) throws SSLException {
verifySession(host.getHostName(), sslEngine.getSession());
return tlsDetailsFactory != null ? tlsDetailsFactory.create(sslEngine) : null;
}
});
return true;
void applyParameters(final SSLEngine sslEngine, final SSLParameters sslParameters, final String[] appProtocols) {
H2TlsSupport.setApplicationProtocols(sslParameters, appProtocols);
sslEngine.setSSLParameters(sslParameters);
}
protected void initializeEngine(final SSLEngine sslEngine) {
}
protected void verifySession(
final String hostname,
final SSLSession sslsession) throws SSLException {
tlsSessionValidator.verifySession(hostname, sslsession, hostnameVerifier);
@Override
TlsDetails createTlsDetails(final SSLEngine sslEngine) {
return tlsDetailsFactory != null ? tlsDetailsFactory.create(sslEngine) : null;
}
}

View File

@ -70,6 +70,7 @@
<httpcore.version>5.0-beta6</httpcore.version>
<log4j.version>2.9.1</log4j.version>
<commons-codec.version>1.11</commons-codec.version>
<conscrypt.version>1.4.1</conscrypt.version>
<ehcache.version>3.4.0</ehcache.version>
<memcached.version>2.12.3</memcached.version>
<slf4j.version>1.7.25</slf4j.version>
@ -123,6 +124,11 @@
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.conscrypt</groupId>
<artifactId>conscrypt-openjdk-uber</artifactId>
<version>${conscrypt.version}</version>
</dependency>
<dependency>
<groupId>org.ehcache.modules</groupId>
<artifactId>ehcache-api</artifactId>