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.core5.annotation.Contract;
42  import org.apache.hc.core5.annotation.ThreadingBehavior;
43  import org.apache.hc.core5.http.HttpHost;
44  import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
45  import org.apache.hc.core5.http.ssl.TLS;
46  import org.apache.hc.core5.http.ssl.TlsCiphers;
47  import org.apache.hc.core5.http2.HttpVersionPolicy;
48  import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
49  import org.apache.hc.core5.http2.ssl.H2TlsSupport;
50  import org.apache.hc.core5.net.NamedEndpoint;
51  import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
52  import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
53  import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
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      @Override
89      public boolean upgrade(
90              final TransportSecurityLayer tlsSession,
91              final HttpHost host,
92              final SocketAddress localAddress,
93              final SocketAddress remoteAddress,
94              final Object attachment,
95              final Timeout handshakeTimeout) {
96          tlsSession.startTls(sslContext, host, sslBufferManagement, new SSLSessionInitializer() {
97  
98              @Override
99              public void initialize(final NamedEndpoint endpoint, final SSLEngine sslEngine) {
100 
101                 final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
102                         (HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
103 
104                 final SSLParameters sslParameters = sslEngine.getSSLParameters();
105                 if (supportedProtocols != null) {
106                     sslParameters.setProtocols(supportedProtocols);
107                 } else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
108                     sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols()));
109                 }
110                 if (supportedCipherSuites != null) {
111                     sslParameters.setCipherSuites(supportedCipherSuites);
112                 } else if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) {
113                     sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites()));
114                 }
115 
116                 if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
117                     H2TlsSupport.setEnableRetransmissions(sslParameters, false);
118                 }
119 
120                 applyParameters(sslEngine, sslParameters, H2TlsSupport.selectApplicationProtocols(attachment));
121 
122                 initializeEngine(sslEngine);
123 
124                 if (LOG.isDebugEnabled()) {
125                     LOG.debug("Enabled protocols: {}", Arrays.asList(sslEngine.getEnabledProtocols()));
126                     LOG.debug("Enabled cipher suites:{}", Arrays.asList(sslEngine.getEnabledCipherSuites()));
127                 }
128             }
129 
130         }, new SSLSessionVerifier() {
131 
132             @Override
133             public TlsDetails verify(final NamedEndpoint endpoint, final SSLEngine sslEngine) throws SSLException {
134                 verifySession(host.getHostName(), sslEngine.getSession());
135                 final TlsDetails tlsDetails = createTlsDetails(sslEngine);
136                 final String negotiatedCipherSuite = sslEngine.getSession().getCipherSuite();
137                 if (tlsDetails != null && ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
138                     if (TlsCiphers.isH2Blacklisted(negotiatedCipherSuite)) {
139                         throw new SSLHandshakeException("Cipher suite `" + negotiatedCipherSuite
140                             + "` does not provide adequate security for HTTP/2");
141                     }
142                 }
143                 return tlsDetails;
144             }
145 
146         }, handshakeTimeout);
147         return true;
148     }
149 
150     abstract void applyParameters(SSLEngine sslEngine, SSLParameters sslParameters, String[] appProtocols);
151 
152     abstract TlsDetails createTlsDetails(SSLEngine sslEngine);
153 
154     protected void initializeEngine(final SSLEngine sslEngine) {
155     }
156 
157     protected void verifySession(
158             final String hostname,
159             final SSLSession sslsession) throws SSLException {
160         tlsSessionValidator.verifySession(hostname, sslsession, hostnameVerifier);
161     }
162 
163 }