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