View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.ssl;
29  
30  import java.net.SocketAddress;
31  import java.util.Arrays;
32  
33  import javax.net.ssl.HostnameVerifier;
34  import javax.net.ssl.SSLContext;
35  import javax.net.ssl.SSLEngine;
36  import javax.net.ssl.SSLException;
37  import javax.net.ssl.SSLHandshakeException;
38  import javax.net.ssl.SSLParameters;
39  import javax.net.ssl.SSLSession;
40  
41  import org.apache.hc.client5.http.config.TlsConfig;
42  import org.apache.hc.core5.annotation.Contract;
43  import org.apache.hc.core5.annotation.ThreadingBehavior;
44  import org.apache.hc.core5.concurrent.FutureCallback;
45  import org.apache.hc.core5.http.HttpHost;
46  import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
47  import org.apache.hc.core5.http.ssl.TLS;
48  import org.apache.hc.core5.http.ssl.TlsCiphers;
49  import org.apache.hc.core5.http2.HttpVersionPolicy;
50  import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
51  import org.apache.hc.core5.http2.ssl.H2TlsSupport;
52  import org.apache.hc.core5.net.NamedEndpoint;
53  import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
54  import org.apache.hc.core5.reactor.ssl.TlsDetails;
55  import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
56  import org.apache.hc.core5.util.Args;
57  import org.apache.hc.core5.util.Timeout;
58  import org.slf4j.Logger;
59  import org.slf4j.LoggerFactory;
60  
61  @Contract(threading = ThreadingBehavior.STATELESS)
62  abstract class AbstractClientTlsStrategy implements TlsStrategy {
63  
64      private static final Logger LOG = LoggerFactory.getLogger(AbstractClientTlsStrategy.class);
65  
66      private final SSLContext sslContext;
67      private final String[] supportedProtocols;
68      private final String[] supportedCipherSuites;
69      private final SSLBufferMode sslBufferManagement;
70      private final HostnameVerifier hostnameVerifier;
71      private final TlsSessionValidator tlsSessionValidator;
72  
73      AbstractClientTlsStrategy(
74              final SSLContext sslContext,
75              final String[] supportedProtocols,
76              final String[] supportedCipherSuites,
77              final SSLBufferMode sslBufferManagement,
78              final HostnameVerifier hostnameVerifier) {
79          super();
80          this.sslContext = Args.notNull(sslContext, "SSL context");
81          this.supportedProtocols = supportedProtocols;
82          this.supportedCipherSuites = supportedCipherSuites;
83          this.sslBufferManagement = sslBufferManagement != null ? sslBufferManagement : SSLBufferMode.STATIC;
84          this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier();
85          this.tlsSessionValidator = new TlsSessionValidator(LOG);
86      }
87  
88      /**
89       * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
90       */
91      @Deprecated
92      @Override
93      public boolean upgrade(
94              final TransportSecurityLayer tlsSession,
95              final HttpHost host,
96              final SocketAddress localAddress,
97              final SocketAddress remoteAddress,
98              final Object attachment,
99              final Timeout handshakeTimeout) {
100         upgrade(tlsSession, host, attachment, handshakeTimeout, null);
101         return true;
102     }
103 
104     @Override
105     public void upgrade(
106             final TransportSecurityLayer tlsSession,
107             final NamedEndpoint endpoint,
108             final Object attachment,
109             final Timeout handshakeTimeout,
110             final FutureCallback<TransportSecurityLayer> callback) {
111         tlsSession.startTls(sslContext, endpoint, sslBufferManagement, (e, sslEngine) -> {
112 
113             final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
114             final HttpVersionPolicy versionPolicy = tlsConfig.getHttpVersionPolicy();
115 
116             final SSLParameters sslParameters = sslEngine.getSSLParameters();
117             final String[] supportedProtocols = tlsConfig.getSupportedProtocols();
118             if (supportedProtocols != null) {
119                 sslParameters.setProtocols(supportedProtocols);
120             } else if (this.supportedProtocols != null) {
121                 sslParameters.setProtocols(this.supportedProtocols);
122             } else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
123                 sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols()));
124             }
125             final String[] supportedCipherSuites = tlsConfig.getSupportedCipherSuites();
126             if (supportedCipherSuites != null) {
127                 sslParameters.setCipherSuites(supportedCipherSuites);
128             } else if (this.supportedCipherSuites != null) {
129                 sslParameters.setCipherSuites(this.supportedCipherSuites);
130             } else if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) {
131                 sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites()));
132             }
133 
134             if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
135                 H2TlsSupport.setEnableRetransmissions(sslParameters, false);
136             }
137 
138             applyParameters(sslEngine, sslParameters, H2TlsSupport.selectApplicationProtocols(versionPolicy));
139 
140             initializeEngine(sslEngine);
141 
142             if (LOG.isDebugEnabled()) {
143                 LOG.debug("Enabled protocols: {}", Arrays.asList(sslEngine.getEnabledProtocols()));
144                 LOG.debug("Enabled cipher suites:{}", Arrays.asList(sslEngine.getEnabledCipherSuites()));
145                 LOG.debug("Starting handshake ({})", handshakeTimeout);
146             }
147         }, (e, sslEngine) -> {
148             verifySession(endpoint.getHostName(), sslEngine.getSession());
149             final TlsDetails tlsDetails = createTlsDetails(sslEngine);
150             final String negotiatedCipherSuite = sslEngine.getSession().getCipherSuite();
151             if (tlsDetails != null && ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
152                 if (TlsCiphers.isH2Blacklisted(negotiatedCipherSuite)) {
153                     throw new SSLHandshakeException("Cipher suite `" + negotiatedCipherSuite
154                         + "` does not provide adequate security for HTTP/2");
155                 }
156             }
157             return tlsDetails;
158         }, handshakeTimeout, callback);
159     }
160 
161     abstract void applyParameters(SSLEngine sslEngine, SSLParameters sslParameters, String[] appProtocols);
162 
163     abstract TlsDetails createTlsDetails(SSLEngine sslEngine);
164 
165     protected void initializeEngine(final SSLEngine sslEngine) {
166     }
167 
168     protected void verifySession(
169             final String hostname,
170             final SSLSession sslsession) throws SSLException {
171         tlsSessionValidator.verifySession(hostname, sslsession, hostnameVerifier);
172     }
173 
174 }