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 }