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.http.conn.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.cert.Certificate;
35 import java.security.cert.X509Certificate;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.regex.Pattern;
42
43 import javax.net.SocketFactory;
44 import javax.net.ssl.HostnameVerifier;
45 import javax.net.ssl.SSLContext;
46 import javax.net.ssl.SSLHandshakeException;
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.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54 import org.apache.http.HttpHost;
55 import org.apache.http.annotation.Contract;
56 import org.apache.http.annotation.ThreadingBehavior;
57 import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
58 import org.apache.http.conn.util.PublicSuffixMatcherLoader;
59 import org.apache.http.protocol.HttpContext;
60 import org.apache.http.ssl.SSLContexts;
61 import org.apache.http.util.Args;
62 import org.apache.http.util.TextUtils;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139 @Contract(threading = ThreadingBehavior.SAFE)
140 @SuppressWarnings("deprecation")
141 public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactory {
142
143 public static final String TLS = "TLS";
144 public static final String SSL = "SSL";
145 public static final String SSLV2 = "SSLv2";
146
147
148
149
150 @Deprecated
151 public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
152 = AllowAllHostnameVerifier.INSTANCE;
153
154
155
156
157 @Deprecated
158 public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
159 = BrowserCompatHostnameVerifier.INSTANCE;
160
161
162
163
164 @Deprecated
165 public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
166 = StrictHostnameVerifier.INSTANCE;
167
168 private static final String WEAK_KEY_EXCHANGES
169 = "^(TLS|SSL)_(NULL|ECDH_anon|DH_anon|DH_anon_EXPORT|DHE_RSA_EXPORT|DHE_DSS_EXPORT|"
170 + "DSS_EXPORT|DH_DSS_EXPORT|DH_RSA_EXPORT|RSA_EXPORT|KRB5_EXPORT)_(.*)";
171 private static final String WEAK_CIPHERS
172 = "^(TLS|SSL)_(.*)_WITH_(NULL|DES_CBC|DES40_CBC|DES_CBC_40|3DES_EDE_CBC|RC4_128|RC4_40|RC2_CBC_40)_(.*)";
173 private static final List<Pattern> WEAK_CIPHER_SUITE_PATTERNS = Collections.unmodifiableList(Arrays.asList(
174 Pattern.compile(WEAK_KEY_EXCHANGES, Pattern.CASE_INSENSITIVE),
175 Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE)));
176
177 private final Log log = LogFactory.getLog(getClass());
178
179
180
181
182 public static HostnameVerifier getDefaultHostnameVerifier() {
183 return new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault());
184 }
185
186
187
188
189
190
191
192
193 public static SSLConnectionSocketFactory getSocketFactory() throws SSLInitializationException {
194 return new SSLConnectionSocketFactory(SSLContexts.createDefault(), getDefaultHostnameVerifier());
195 }
196
197 static boolean isWeakCipherSuite(final String cipherSuite) {
198 for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) {
199 if (pattern.matcher(cipherSuite).matches()) {
200 return true;
201 }
202 }
203 return false;
204 }
205
206 private static String[] split(final String s) {
207 if (TextUtils.isBlank(s)) {
208 return null;
209 }
210 return s.split(" *, *");
211 }
212
213
214
215
216
217
218
219
220
221 public static SSLConnectionSocketFactory getSystemSocketFactory() throws SSLInitializationException {
222 return new SSLConnectionSocketFactory(
223 (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(),
224 split(System.getProperty("https.protocols")),
225 split(System.getProperty("https.cipherSuites")),
226 getDefaultHostnameVerifier());
227 }
228
229 private final javax.net.ssl.SSLSocketFactory socketfactory;
230 private final HostnameVerifier hostnameVerifier;
231 private final String[] supportedProtocols;
232 private final String[] supportedCipherSuites;
233
234 public SSLConnectionSocketFactory(final SSLContext sslContext) {
235 this(sslContext, getDefaultHostnameVerifier());
236 }
237
238
239
240
241
242 @Deprecated
243 public SSLConnectionSocketFactory(
244 final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
245 this(Args.notNull(sslContext, "SSL context").getSocketFactory(),
246 null, null, hostnameVerifier);
247 }
248
249
250
251
252
253 @Deprecated
254 public SSLConnectionSocketFactory(
255 final SSLContext sslContext,
256 final String[] supportedProtocols,
257 final String[] supportedCipherSuites,
258 final X509HostnameVerifier hostnameVerifier) {
259 this(Args.notNull(sslContext, "SSL context").getSocketFactory(),
260 supportedProtocols, supportedCipherSuites, hostnameVerifier);
261 }
262
263
264
265
266
267 @Deprecated
268 public SSLConnectionSocketFactory(
269 final javax.net.ssl.SSLSocketFactory socketfactory,
270 final X509HostnameVerifier hostnameVerifier) {
271 this(socketfactory, null, null, hostnameVerifier);
272 }
273
274
275
276
277
278 @Deprecated
279 public SSLConnectionSocketFactory(
280 final javax.net.ssl.SSLSocketFactory socketfactory,
281 final String[] supportedProtocols,
282 final String[] supportedCipherSuites,
283 final X509HostnameVerifier hostnameVerifier) {
284 this(socketfactory, supportedProtocols, supportedCipherSuites, (HostnameVerifier) hostnameVerifier);
285 }
286
287
288
289
290 public SSLConnectionSocketFactory(
291 final SSLContext sslContext, final HostnameVerifier hostnameVerifier) {
292 this(Args.notNull(sslContext, "SSL context").getSocketFactory(),
293 null, null, hostnameVerifier);
294 }
295
296
297
298
299 public SSLConnectionSocketFactory(
300 final SSLContext sslContext,
301 final String[] supportedProtocols,
302 final String[] supportedCipherSuites,
303 final HostnameVerifier hostnameVerifier) {
304 this(Args.notNull(sslContext, "SSL context").getSocketFactory(),
305 supportedProtocols, supportedCipherSuites, hostnameVerifier);
306 }
307
308
309
310
311 public SSLConnectionSocketFactory(
312 final javax.net.ssl.SSLSocketFactory socketfactory,
313 final HostnameVerifier hostnameVerifier) {
314 this(socketfactory, null, null, hostnameVerifier);
315 }
316
317
318
319
320 public SSLConnectionSocketFactory(
321 final javax.net.ssl.SSLSocketFactory socketfactory,
322 final String[] supportedProtocols,
323 final String[] supportedCipherSuites,
324 final HostnameVerifier hostnameVerifier) {
325 this.socketfactory = Args.notNull(socketfactory, "SSL socket factory");
326 this.supportedProtocols = supportedProtocols;
327 this.supportedCipherSuites = supportedCipherSuites;
328 this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : getDefaultHostnameVerifier();
329 }
330
331
332
333
334
335
336
337
338
339 protected void prepareSocket(final SSLSocket socket) throws IOException {
340 }
341
342 @Override
343 public Socket createSocket(final HttpContext context) throws IOException {
344 return SocketFactory.getDefault().createSocket();
345 }
346
347 @Override
348 public Socket connectSocket(
349 final int connectTimeout,
350 final Socket socket,
351 final HttpHost host,
352 final InetSocketAddress remoteAddress,
353 final InetSocketAddress localAddress,
354 final HttpContext context) throws IOException {
355 Args.notNull(host, "HTTP host");
356 Args.notNull(remoteAddress, "Remote address");
357 final Socket sock = socket != null ? socket : createSocket(context);
358 if (localAddress != null) {
359 sock.bind(localAddress);
360 }
361 try {
362 if (connectTimeout > 0 && sock.getSoTimeout() == 0) {
363 sock.setSoTimeout(connectTimeout);
364 }
365 if (this.log.isDebugEnabled()) {
366 this.log.debug("Connecting socket to " + remoteAddress + " with timeout " + connectTimeout);
367 }
368 sock.connect(remoteAddress, connectTimeout);
369 } catch (final IOException ex) {
370 try {
371 sock.close();
372 } catch (final IOException ignore) {
373 }
374 throw ex;
375 }
376
377 if (sock instanceof SSLSocket) {
378 final SSLSocket sslsock = (SSLSocket) sock;
379 this.log.debug("Starting handshake");
380 sslsock.startHandshake();
381 verifyHostname(sslsock, host.getHostName());
382 return sock;
383 }
384 return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), context);
385 }
386
387 @Override
388 public Socket createLayeredSocket(
389 final Socket socket,
390 final String target,
391 final int port,
392 final HttpContext context) throws IOException {
393 final SSLSocket sslsock = (SSLSocket) this.socketfactory.createSocket(
394 socket,
395 target,
396 port,
397 true);
398 if (supportedProtocols != null) {
399 sslsock.setEnabledProtocols(supportedProtocols);
400 } else {
401
402 final String[] allProtocols = sslsock.getEnabledProtocols();
403 final List<String> enabledProtocols = new ArrayList<String>(allProtocols.length);
404 for (final String protocol: allProtocols) {
405 if (!protocol.startsWith("SSL")) {
406 enabledProtocols.add(protocol);
407 }
408 }
409 if (!enabledProtocols.isEmpty()) {
410 sslsock.setEnabledProtocols(enabledProtocols.toArray(new String[enabledProtocols.size()]));
411 }
412 }
413 if (supportedCipherSuites != null) {
414 sslsock.setEnabledCipherSuites(supportedCipherSuites);
415 } else {
416
417 final String[] allCipherSuites = sslsock.getEnabledCipherSuites();
418 final List<String> enabledCipherSuites = new ArrayList<String>(allCipherSuites.length);
419 for (final String cipherSuite : allCipherSuites) {
420 if (!isWeakCipherSuite(cipherSuite)) {
421 enabledCipherSuites.add(cipherSuite);
422 }
423 }
424 if (!enabledCipherSuites.isEmpty()) {
425 sslsock.setEnabledCipherSuites(enabledCipherSuites.toArray(new String[enabledCipherSuites.size()]));
426 }
427 }
428
429 if (this.log.isDebugEnabled()) {
430 this.log.debug("Enabled protocols: " + Arrays.asList(sslsock.getEnabledProtocols()));
431 this.log.debug("Enabled cipher suites:" + Arrays.asList(sslsock.getEnabledCipherSuites()));
432 }
433
434 prepareSocket(sslsock);
435 this.log.debug("Starting handshake");
436 sslsock.startHandshake();
437 verifyHostname(sslsock, target);
438 return sslsock;
439 }
440
441 private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException {
442 try {
443 SSLSession session = sslsock.getSession();
444 if (session == null) {
445
446
447
448 final InputStream in = sslsock.getInputStream();
449 in.available();
450
451
452 session = sslsock.getSession();
453 if (session == null) {
454
455
456 sslsock.startHandshake();
457 session = sslsock.getSession();
458 }
459 }
460 if (session == null) {
461 throw new SSLHandshakeException("SSL session not available");
462 }
463
464 if (this.log.isDebugEnabled()) {
465 this.log.debug("Secure session established");
466 this.log.debug(" negotiated protocol: " + session.getProtocol());
467 this.log.debug(" negotiated cipher suite: " + session.getCipherSuite());
468
469 try {
470
471 final Certificate[] certs = session.getPeerCertificates();
472 final X509Certificate x509 = (X509Certificate) certs[0];
473 final X500Principal peer = x509.getSubjectX500Principal();
474
475 this.log.debug(" peer principal: " + peer.toString());
476 final Collection<List<?>> altNames1 = x509.getSubjectAlternativeNames();
477 if (altNames1 != null) {
478 final List<String> altNames = new ArrayList<String>();
479 for (final List<?> aC : altNames1) {
480 if (!aC.isEmpty()) {
481 altNames.add((String) aC.get(1));
482 }
483 }
484 this.log.debug(" peer alternative names: " + altNames);
485 }
486
487 final X500Principal issuer = x509.getIssuerX500Principal();
488 this.log.debug(" issuer principal: " + issuer.toString());
489 final Collection<List<?>> altNames2 = x509.getIssuerAlternativeNames();
490 if (altNames2 != null) {
491 final List<String> altNames = new ArrayList<String>();
492 for (final List<?> aC : altNames2) {
493 if (!aC.isEmpty()) {
494 altNames.add((String) aC.get(1));
495 }
496 }
497 this.log.debug(" issuer alternative names: " + altNames);
498 }
499 } catch (final Exception ignore) {
500 }
501 }
502
503 if (!this.hostnameVerifier.verify(hostname, session)) {
504 final Certificate[] certs = session.getPeerCertificates();
505 final X509Certificate x509 = (X509Certificate) certs[0];
506 final List<SubjectName> subjectAlts = DefaultHostnameVerifier.getSubjectAltNames(x509);
507 throw new SSLPeerUnverifiedException("Certificate for <" + hostname + "> doesn't match any " +
508 "of the subject alternative names: " + subjectAlts);
509 }
510
511 } catch (final IOException iox) {
512
513 try { sslsock.close(); } catch (final Exception x) { }
514 throw iox;
515 }
516 }
517
518 }