diff --git a/httpclient5/pom.xml b/httpclient5/pom.xml index ca48f350e..c7f8ee608 100644 --- a/httpclient5/pom.xml +++ b/httpclient5/pom.xml @@ -52,6 +52,12 @@ slf4j-api compile + + org.conscrypt + conscrypt-openjdk-uber + compile + true + org.apache.httpcomponents.core5 httpcore5-reactive diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java index 610d9ab70..9273282d7 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java @@ -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( diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java new file mode 100644 index 000000000..c4cf48b96 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java @@ -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 + * . + * + */ + +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); + } + +} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/ConscryptClientTlsStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/ConscryptClientTlsStrategy.java new file mode 100644 index 000000000..9ec8dbf75 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/ConscryptClientTlsStrategy.java @@ -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 + * . + * + */ + +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; + } + } + +} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultClientTlsStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultClientTlsStrategy.java index 9e491fa53..9a089db33 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultClientTlsStrategy.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultClientTlsStrategy.java @@ -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 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 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; } } diff --git a/pom.xml b/pom.xml index 5e439a613..36fbc7a3c 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,7 @@ 5.0-beta6 2.9.1 1.11 + 1.4.1 3.4.0 2.12.3 1.7.25 @@ -123,6 +124,11 @@ commons-codec ${commons-codec.version} + + org.conscrypt + conscrypt-openjdk-uber + ${conscrypt.version} + org.ehcache.modules ehcache-api