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.io.InputStream;
32 import java.net.InetSocketAddress;
33 import java.net.Socket;
34 import java.security.AccessController;
35 import java.security.PrivilegedActionException;
36 import java.security.PrivilegedExceptionAction;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.regex.Pattern;
41
42 import javax.net.SocketFactory;
43 import javax.net.ssl.HostnameVerifier;
44 import javax.net.ssl.SSLContext;
45 import javax.net.ssl.SSLException;
46 import javax.net.ssl.SSLHandshakeException;
47 import javax.net.ssl.SSLSession;
48 import javax.net.ssl.SSLSocket;
49
50 import org.apache.hc.client5.http.config.TlsConfig;
51 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
52 import org.apache.hc.core5.annotation.Contract;
53 import org.apache.hc.core5.annotation.ThreadingBehavior;
54 import org.apache.hc.core5.http.HttpHost;
55 import org.apache.hc.core5.http.protocol.HttpContext;
56 import org.apache.hc.core5.http.ssl.TLS;
57 import org.apache.hc.core5.http.ssl.TlsCiphers;
58 import org.apache.hc.core5.io.Closer;
59 import org.apache.hc.core5.ssl.SSLContexts;
60 import org.apache.hc.core5.ssl.SSLInitializationException;
61 import org.apache.hc.core5.util.Args;
62 import org.apache.hc.core5.util.Asserts;
63 import org.apache.hc.core5.util.TimeValue;
64 import org.apache.hc.core5.util.Timeout;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68
69
70
71
72
73
74
75
76 @Contract(threading = ThreadingBehavior.STATELESS)
77 public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactory {
78
79 private static final String WEAK_KEY_EXCHANGES
80 = "^(TLS|SSL)_(NULL|ECDH_anon|DH_anon|DH_anon_EXPORT|DHE_RSA_EXPORT|DHE_DSS_EXPORT|"
81 + "DSS_EXPORT|DH_DSS_EXPORT|DH_RSA_EXPORT|RSA_EXPORT|KRB5_EXPORT)_(.*)";
82 private static final String WEAK_CIPHERS
83 = "^(TLS|SSL)_(.*)_WITH_(NULL|DES_CBC|DES40_CBC|DES_CBC_40|3DES_EDE_CBC|RC4_128|RC4_40|RC2_CBC_40)_(.*)";
84 private static final List<Pattern> WEAK_CIPHER_SUITE_PATTERNS = Collections.unmodifiableList(Arrays.asList(
85 Pattern.compile(WEAK_KEY_EXCHANGES, Pattern.CASE_INSENSITIVE),
86 Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE)));
87
88 private static final Logger LOG = LoggerFactory.getLogger(SSLConnectionSocketFactory.class);
89
90
91
92
93
94
95
96
97 public static SSLConnectionSocketFactory getSocketFactory() throws SSLInitializationException {
98 return new SSLConnectionSocketFactory(SSLContexts.createDefault(), HttpsSupport.getDefaultHostnameVerifier());
99 }
100
101
102
103
104
105
106
107
108
109 public static SSLConnectionSocketFactory getSystemSocketFactory() throws SSLInitializationException {
110 return new SSLConnectionSocketFactory(
111 (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(),
112 HttpsSupport.getSystemProtocols(),
113 HttpsSupport.getSystemCipherSuits(),
114 HttpsSupport.getDefaultHostnameVerifier());
115 }
116
117 static boolean isWeakCipherSuite(final String cipherSuite) {
118 for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) {
119 if (pattern.matcher(cipherSuite).matches()) {
120 return true;
121 }
122 }
123 return false;
124 }
125
126 private final javax.net.ssl.SSLSocketFactory socketFactory;
127 private final HostnameVerifier hostnameVerifier;
128 private final String[] supportedProtocols;
129 private final String[] supportedCipherSuites;
130 private final TlsSessionValidator tlsSessionValidator;
131
132 public SSLConnectionSocketFactory(final SSLContext sslContext) {
133 this(sslContext, HttpsSupport.getDefaultHostnameVerifier());
134 }
135
136
137
138
139 public SSLConnectionSocketFactory(
140 final SSLContext sslContext, final HostnameVerifier hostnameVerifier) {
141 this(Args.notNull(sslContext, "SSL context").getSocketFactory(),
142 null, null, hostnameVerifier);
143 }
144
145
146
147
148 public SSLConnectionSocketFactory(
149 final SSLContext sslContext,
150 final String[] supportedProtocols,
151 final String[] supportedCipherSuites,
152 final HostnameVerifier hostnameVerifier) {
153 this(Args.notNull(sslContext, "SSL context").getSocketFactory(),
154 supportedProtocols, supportedCipherSuites, hostnameVerifier);
155 }
156
157
158
159
160 public SSLConnectionSocketFactory(
161 final javax.net.ssl.SSLSocketFactory socketFactory,
162 final HostnameVerifier hostnameVerifier) {
163 this(socketFactory, null, null, hostnameVerifier);
164 }
165
166
167
168
169 public SSLConnectionSocketFactory(
170 final javax.net.ssl.SSLSocketFactory socketFactory,
171 final String[] supportedProtocols,
172 final String[] supportedCipherSuites,
173 final HostnameVerifier hostnameVerifier) {
174 this.socketFactory = Args.notNull(socketFactory, "SSL socket factory");
175 this.supportedProtocols = supportedProtocols;
176 this.supportedCipherSuites = supportedCipherSuites;
177 this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier();
178 this.tlsSessionValidator = new TlsSessionValidator(LOG);
179 }
180
181
182
183
184 @Deprecated
185 protected void prepareSocket(final SSLSocket socket) throws IOException {
186 }
187
188
189
190
191
192
193
194
195
196 @SuppressWarnings("deprecation")
197 protected void prepareSocket(final SSLSocket socket, final HttpContext context) throws IOException {
198 prepareSocket(socket);
199 }
200
201 @Override
202 public Socket createSocket(final HttpContext context) throws IOException {
203 return SocketFactory.getDefault().createSocket();
204 }
205
206 @Override
207 public Socket connectSocket(
208 final TimeValue connectTimeout,
209 final Socket socket,
210 final HttpHost host,
211 final InetSocketAddress remoteAddress,
212 final InetSocketAddress localAddress,
213 final HttpContext context) throws IOException {
214 final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null;
215 return connectSocket(socket, host, remoteAddress, localAddress, timeout, timeout, context);
216 }
217
218 @Override
219 public Socket connectSocket(
220 final Socket socket,
221 final HttpHost host,
222 final InetSocketAddress remoteAddress,
223 final InetSocketAddress localAddress,
224 final Timeout connectTimeout,
225 final Object attachment,
226 final HttpContext context) throws IOException {
227 Args.notNull(host, "HTTP host");
228 Args.notNull(remoteAddress, "Remote address");
229 final Socket sock = socket != null ? socket : createSocket(context);
230 if (localAddress != null) {
231 sock.bind(localAddress);
232 }
233 try {
234 if (LOG.isDebugEnabled()) {
235 LOG.debug("Connecting socket to {} with timeout {}", remoteAddress, connectTimeout);
236 }
237
238
239 try {
240 AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
241 sock.connect(remoteAddress, Timeout.defaultsToDisabled(connectTimeout).toMillisecondsIntBound());
242 return null;
243 });
244 } catch (final PrivilegedActionException e) {
245 Asserts.check(e.getCause() instanceof IOException,
246 "method contract violation only checked exceptions are wrapped: " + e.getCause());
247
248 throw (IOException) e.getCause();
249 }
250 } catch (final IOException ex) {
251 Closer.closeQuietly(sock);
252 throw ex;
253 }
254
255 if (sock instanceof SSLSocket) {
256 final SSLSocket sslsock = (SSLSocket) sock;
257 executeHandshake(sslsock, host.getHostName(), attachment, context);
258 return sock;
259 }
260 return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), attachment, context);
261 }
262
263 @Override
264 public Socket createLayeredSocket(
265 final Socket socket,
266 final String target,
267 final int port,
268 final HttpContext context) throws IOException {
269 return createLayeredSocket(socket, target, port, null, context);
270 }
271
272 @Override
273 public Socket createLayeredSocket(
274 final Socket socket,
275 final String target,
276 final int port,
277 final Object attachment,
278 final HttpContext context) throws IOException {
279 final SSLSocket sslsock = (SSLSocket) this.socketFactory.createSocket(
280 socket,
281 target,
282 port,
283 true);
284 executeHandshake(sslsock, target, attachment, context);
285 return sslsock;
286 }
287
288 private void executeHandshake(
289 final SSLSocket sslsock,
290 final String target,
291 final Object attachment,
292 final HttpContext context) throws IOException {
293 final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
294 if (supportedProtocols != null) {
295 sslsock.setEnabledProtocols(supportedProtocols);
296 } else {
297 sslsock.setEnabledProtocols((TLS.excludeWeak(sslsock.getEnabledProtocols())));
298 }
299 if (supportedCipherSuites != null) {
300 sslsock.setEnabledCipherSuites(supportedCipherSuites);
301 } else {
302 sslsock.setEnabledCipherSuites(TlsCiphers.excludeWeak(sslsock.getEnabledCipherSuites()));
303 }
304 final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
305 if (handshakeTimeout != null) {
306 sslsock.setSoTimeout(handshakeTimeout.toMillisecondsIntBound());
307 }
308
309 prepareSocket(sslsock, context);
310
311 if (LOG.isDebugEnabled()) {
312 LOG.debug("Enabled protocols: {}", (Object) sslsock.getEnabledProtocols());
313 LOG.debug("Enabled cipher suites: {}", (Object) sslsock.getEnabledCipherSuites());
314 LOG.debug("Starting handshake ({})", handshakeTimeout);
315 }
316 sslsock.startHandshake();
317 verifyHostname(sslsock, target);
318 }
319
320 private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException {
321 try {
322 SSLSession session = sslsock.getSession();
323 if (session == null) {
324
325
326
327 final InputStream in = sslsock.getInputStream();
328 in.available();
329
330
331 session = sslsock.getSession();
332 if (session == null) {
333
334
335 sslsock.startHandshake();
336 session = sslsock.getSession();
337 }
338 }
339 if (session == null) {
340 throw new SSLHandshakeException("SSL session not available");
341 }
342 verifySession(hostname, session);
343 } catch (final IOException iox) {
344
345 Closer.closeQuietly(sslsock);
346 throw iox;
347 }
348 }
349
350 protected void verifySession(
351 final String hostname,
352 final SSLSession sslSession) throws SSLException {
353 tlsSessionValidator.verifySession(hostname, sslSession, hostnameVerifier);
354 }
355
356 }