1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.client5.http.ssl;
29
30 import java.io.IOException;
31 import java.net.Socket;
32 import java.net.SocketAddress;
33 import java.security.cert.Certificate;
34 import java.security.cert.X509Certificate;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collection;
38 import java.util.List;
39 import java.util.Objects;
40
41 import javax.net.ssl.HostnameVerifier;
42 import javax.net.ssl.SSLContext;
43 import javax.net.ssl.SSLEngine;
44 import javax.net.ssl.SSLException;
45 import javax.net.ssl.SSLHandshakeException;
46 import javax.net.ssl.SSLParameters;
47 import javax.net.ssl.SSLPeerUnverifiedException;
48 import javax.net.ssl.SSLSession;
49 import javax.net.ssl.SSLSocket;
50 import javax.security.auth.x500.X500Principal;
51
52 import org.apache.hc.client5.http.config.TlsConfig;
53 import org.apache.hc.core5.annotation.Contract;
54 import org.apache.hc.core5.annotation.ThreadingBehavior;
55 import org.apache.hc.core5.concurrent.FutureCallback;
56 import org.apache.hc.core5.http.HttpHost;
57 import org.apache.hc.core5.http.URIScheme;
58 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
59 import org.apache.hc.core5.http.protocol.HttpContext;
60 import org.apache.hc.core5.http.ssl.TLS;
61 import org.apache.hc.core5.http.ssl.TlsCiphers;
62 import org.apache.hc.core5.http2.HttpVersionPolicy;
63 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
64 import org.apache.hc.core5.http2.ssl.H2TlsSupport;
65 import org.apache.hc.core5.net.NamedEndpoint;
66 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
67 import org.apache.hc.core5.reactor.ssl.TlsDetails;
68 import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
69 import org.apache.hc.core5.util.Args;
70 import org.apache.hc.core5.util.Timeout;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74 @Contract(threading = ThreadingBehavior.STATELESS)
75 abstract class AbstractClientTlsStrategy implements TlsStrategy, TlsSocketStrategy {
76
77 private static final Logger LOG = LoggerFactory.getLogger(AbstractClientTlsStrategy.class);
78
79 private final SSLContext sslContext;
80 private final String[] supportedProtocols;
81 private final String[] supportedCipherSuites;
82 private final SSLBufferMode sslBufferManagement;
83 private final HostnameVerificationPolicy hostnameVerificationPolicy;
84 private final HostnameVerifier hostnameVerifier;
85
86 AbstractClientTlsStrategy(
87 final SSLContext sslContext,
88 final String[] supportedProtocols,
89 final String[] supportedCipherSuites,
90 final SSLBufferMode sslBufferManagement,
91 final HostnameVerificationPolicy hostnameVerificationPolicy,
92 final HostnameVerifier hostnameVerifier) {
93 super();
94 this.sslContext = Args.notNull(sslContext, "SSL context");
95 this.supportedProtocols = supportedProtocols;
96 this.supportedCipherSuites = supportedCipherSuites;
97 this.sslBufferManagement = sslBufferManagement != null ? sslBufferManagement : SSLBufferMode.STATIC;
98 this.hostnameVerificationPolicy = hostnameVerificationPolicy != null ? hostnameVerificationPolicy : HostnameVerificationPolicy.BOTH;
99 this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier :
100 (this.hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN ? NoopHostnameVerifier.INSTANCE : HttpsSupport.getDefaultHostnameVerifier());
101 }
102
103
104
105
106 @Deprecated
107 @Override
108 public boolean upgrade(
109 final TransportSecurityLayer tlsSession,
110 final HttpHost host,
111 final SocketAddress localAddress,
112 final SocketAddress remoteAddress,
113 final Object attachment,
114 final Timeout handshakeTimeout) {
115 upgrade(tlsSession, host, attachment, handshakeTimeout, null);
116 return true;
117 }
118
119 @Override
120 public void upgrade(
121 final TransportSecurityLayer tlsSession,
122 final NamedEndpoint endpoint,
123 final Object attachment,
124 final Timeout handshakeTimeout,
125 final FutureCallback<TransportSecurityLayer> callback) {
126 tlsSession.startTls(sslContext, endpoint, sslBufferManagement, (e, sslEngine) -> {
127
128 final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
129 final HttpVersionPolicy versionPolicy = tlsConfig.getHttpVersionPolicy();
130
131 final SSLParameters sslParameters = sslEngine.getSSLParameters();
132 final String[] supportedProtocols = tlsConfig.getSupportedProtocols();
133 if (supportedProtocols != null) {
134 sslParameters.setProtocols(supportedProtocols);
135 } else if (this.supportedProtocols != null) {
136 sslParameters.setProtocols(this.supportedProtocols);
137 } else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
138 sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols()));
139 }
140 final String[] supportedCipherSuites = tlsConfig.getSupportedCipherSuites();
141 if (supportedCipherSuites != null) {
142 sslParameters.setCipherSuites(supportedCipherSuites);
143 } else if (this.supportedCipherSuites != null) {
144 sslParameters.setCipherSuites(this.supportedCipherSuites);
145 } else if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) {
146 sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites()));
147 }
148
149 if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
150 H2TlsSupport.setEnableRetransmissions(sslParameters, false);
151 }
152
153 applyParameters(sslEngine, sslParameters, H2TlsSupport.selectApplicationProtocols(versionPolicy));
154
155 if (hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH) {
156 sslParameters.setEndpointIdentificationAlgorithm(URIScheme.HTTPS.id);
157 }
158
159 initializeEngine(sslEngine);
160
161 if (LOG.isDebugEnabled()) {
162 LOG.debug("Enabled protocols: {}", Arrays.asList(sslEngine.getEnabledProtocols()));
163 LOG.debug("Enabled cipher suites:{}", Arrays.asList(sslEngine.getEnabledCipherSuites()));
164 LOG.debug("Starting handshake ({})", handshakeTimeout);
165 }
166 }, (e, sslEngine) -> {
167 verifySession(endpoint.getHostName(), sslEngine.getSession());
168 final TlsDetails tlsDetails = createTlsDetails(sslEngine);
169 final String negotiatedCipherSuite = sslEngine.getSession().getCipherSuite();
170 if (tlsDetails != null && ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
171 if (TlsCiphers.isH2Blacklisted(negotiatedCipherSuite)) {
172 throw new SSLHandshakeException("Cipher suite `" + negotiatedCipherSuite
173 + "` does not provide adequate security for HTTP/2");
174 }
175 }
176 return tlsDetails;
177 }, handshakeTimeout, callback);
178 }
179
180 abstract void applyParameters(SSLEngine sslEngine, SSLParameters sslParameters, String[] appProtocols);
181
182 abstract TlsDetails createTlsDetails(SSLEngine sslEngine);
183
184 protected void initializeEngine(final SSLEngine sslEngine) {
185 }
186
187 protected void initializeSocket(final SSLSocket socket) {
188 }
189
190 protected void verifySession(
191 final String hostname,
192 final SSLSession sslsession) throws SSLException {
193 verifySession(hostname, sslsession,
194 hostnameVerificationPolicy == HostnameVerificationPolicy.CLIENT || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH ? hostnameVerifier : null);
195 }
196
197 @Override
198 public SSLSocket upgrade(final Socket socket,
199 final String target,
200 final int port,
201 final Object attachment,
202 final HttpContext context) throws IOException {
203 final SSLSocket upgradedSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(
204 socket,
205 target,
206 port,
207 true);
208 executeHandshake(upgradedSocket, target, attachment);
209 return upgradedSocket;
210 }
211
212 private void executeHandshake(
213 final SSLSocket upgradedSocket,
214 final String target,
215 final Object attachment) throws IOException {
216 final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
217
218 final SSLParameters sslParameters = upgradedSocket.getSSLParameters();
219 if (supportedProtocols != null) {
220 sslParameters.setProtocols(supportedProtocols);
221 } else {
222 sslParameters.setProtocols((TLS.excludeWeak(upgradedSocket.getEnabledProtocols())));
223 }
224 if (supportedCipherSuites != null) {
225 sslParameters.setCipherSuites(supportedCipherSuites);
226 } else {
227 sslParameters.setCipherSuites(TlsCiphers.excludeWeak(upgradedSocket.getEnabledCipherSuites()));
228 }
229 if (hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH) {
230 sslParameters.setEndpointIdentificationAlgorithm(URIScheme.HTTPS.id);
231 }
232 upgradedSocket.setSSLParameters(sslParameters);
233
234 final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
235 if (handshakeTimeout != null) {
236 upgradedSocket.setSoTimeout(handshakeTimeout.toMillisecondsIntBound());
237 }
238
239 initializeSocket(upgradedSocket);
240
241 if (LOG.isDebugEnabled()) {
242 LOG.debug("Enabled protocols: {}", (Object) upgradedSocket.getEnabledProtocols());
243 LOG.debug("Enabled cipher suites: {}", (Object) upgradedSocket.getEnabledCipherSuites());
244 LOG.debug("Starting handshake ({})", handshakeTimeout);
245 }
246 upgradedSocket.startHandshake();
247 verifySession(target, upgradedSocket.getSession());
248 }
249
250 void verifySession(
251 final String hostname,
252 final SSLSession sslsession,
253 final HostnameVerifier hostnameVerifier) throws SSLException {
254
255 if (LOG.isDebugEnabled()) {
256 LOG.debug("Secure session established");
257 LOG.debug(" negotiated protocol: {}", sslsession.getProtocol());
258 LOG.debug(" negotiated cipher suite: {}", sslsession.getCipherSuite());
259
260 try {
261
262 final Certificate[] certs = sslsession.getPeerCertificates();
263 final Certificate cert = certs[0];
264 if (cert instanceof X509Certificate) {
265 final X509Certificate x509 = (X509Certificate) cert;
266 final X500Principal peer = x509.getSubjectX500Principal();
267
268 LOG.debug(" peer principal: {}", peer);
269 final Collection<List<?>> altNames1 = x509.getSubjectAlternativeNames();
270 if (altNames1 != null) {
271 final List<String> altNames = new ArrayList<>();
272 for (final List<?> aC : altNames1) {
273 if (!aC.isEmpty()) {
274 altNames.add(Objects.toString(aC.get(1), null));
275 }
276 }
277 LOG.debug(" peer alternative names: {}", altNames);
278 }
279
280 final X500Principal issuer = x509.getIssuerX500Principal();
281 LOG.debug(" issuer principal: {}", issuer);
282 final Collection<List<?>> altNames2 = x509.getIssuerAlternativeNames();
283 if (altNames2 != null) {
284 final List<String> altNames = new ArrayList<>();
285 for (final List<?> aC : altNames2) {
286 if (!aC.isEmpty()) {
287 altNames.add(Objects.toString(aC.get(1), null));
288 }
289 }
290 LOG.debug(" issuer alternative names: {}", altNames);
291 }
292 }
293 } catch (final Exception ignore) {
294 }
295 }
296
297 if (hostnameVerifier != null) {
298 final Certificate[] certs = sslsession.getPeerCertificates();
299 if (certs.length < 1) {
300 throw new SSLPeerUnverifiedException("Peer certificate chain is empty");
301 }
302 final Certificate peerCertificate = certs[0];
303 final X509Certificate x509Certificate;
304 if (peerCertificate instanceof X509Certificate) {
305 x509Certificate = (X509Certificate) peerCertificate;
306 } else {
307 throw new SSLPeerUnverifiedException("Unexpected certificate type: " + peerCertificate.getType());
308 }
309 if (hostnameVerifier instanceof HttpClientHostnameVerifier) {
310 ((HttpClientHostnameVerifier) hostnameVerifier).verify(hostname, x509Certificate);
311 } else if (!hostnameVerifier.verify(hostname, sslsession)) {
312 final List<SubjectName> subjectAlts = DefaultHostnameVerifier.getSubjectAltNames(x509Certificate);
313 throw new SSLPeerUnverifiedException("Certificate for <" + hostname + "> doesn't match any " +
314 "of the subject alternative names: " + subjectAlts);
315 }
316 }
317 }
318
319 }