View Javadoc
1   package org.owasp.dependencycheck.utils;
2   
3   import java.io.IOException;
4   import java.net.InetAddress;
5   import java.net.Socket;
6   import java.security.KeyManagementException;
7   import java.security.NoSuchAlgorithmException;
8   import java.security.SecureRandom;
9   import java.util.ArrayList;
10  import java.util.Arrays;
11  import java.util.List;
12  import javax.net.ssl.KeyManager;
13  import javax.net.ssl.SSLContext;
14  import javax.net.ssl.SSLSocket;
15  import javax.net.ssl.SSLSocketFactory;
16  import javax.net.ssl.TrustManager;
17  import org.slf4j.Logger;
18  import org.slf4j.LoggerFactory;
19  
20  /**
21   * This class is used to enable additional ciphers used by the SSL Socket. This
22   * is specifically because the NVD stopped supporting TLS 1.0 and Java 6 and 7
23   * clients by default were unable to connect to download the NVD data feeds.
24   *
25   * The following code was copied from
26   * http://stackoverflow.com/questions/1037590/which-cipher-suites-to-enable-for-ssl-socket/23365536#23365536
27   *
28   * @author <a href="http://stackoverflow.com/users/608639/jww">jww</a>
29   */
30  public class SSLSocketFactoryEx extends SSLSocketFactory {
31  
32      /**
33       * The Logger for use throughout the class.
34       */
35      private static final Logger LOGGER = LoggerFactory.getLogger(SSLSocketFactoryEx.class);
36  
37      /**
38       * Constructs a new SSLSocketFactory.
39       *
40       * @throws NoSuchAlgorithmException thrown when an algorithm is not
41       * supported
42       * @throws KeyManagementException thrown if initialization fails
43       */
44      public SSLSocketFactoryEx() throws NoSuchAlgorithmException, KeyManagementException {
45          initSSLSocketFactoryEx(null, null, null);
46      }
47  
48      /**
49       * Constructs a new SSLSocketFactory.
50       *
51       * @param km the key manager
52       * @param tm the trust manager
53       * @param random secure random
54       * @throws NoSuchAlgorithmException thrown when an algorithm is not
55       * supported
56       * @throws KeyManagementException thrown if initialization fails
57       */
58      public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException {
59          initSSLSocketFactoryEx(km, tm, random);
60      }
61  
62      /**
63       * Constructs a new SSLSocketFactory.
64       *
65       * @param ctx the SSL context
66       * @throws NoSuchAlgorithmException thrown when an algorithm is not
67       * supported
68       * @throws KeyManagementException thrown if initialization fails
69       */
70      public SSLSocketFactoryEx(SSLContext ctx) throws NoSuchAlgorithmException, KeyManagementException {
71          initSSLSocketFactoryEx(ctx);
72      }
73  
74      /**
75       * Returns the default cipher suites.
76       *
77       * @return the default cipher suites
78       */
79      @Override
80      public String[] getDefaultCipherSuites() {
81          return Arrays.copyOf(ciphers, ciphers.length);
82      }
83  
84      /**
85       * Returns the supported cipher suites.
86       *
87       * @return the supported cipher suites
88       */
89      @Override
90      public String[] getSupportedCipherSuites() {
91          return Arrays.copyOf(ciphers, ciphers.length);
92      }
93  
94      /**
95       * Returns the default protocols.
96       *
97       * @return the default protocols
98       */
99      public String[] getDefaultProtocols() {
100         return Arrays.copyOf(protocols, protocols.length);
101     }
102 
103     /**
104      * Returns the supported protocols.
105      *
106      * @return the supported protocols
107      */
108     public String[] getSupportedProtocols() {
109         return Arrays.copyOf(protocols, protocols.length);
110     }
111 
112     /**
113      * Creates an SSL Socket.
114      *
115      * @param s the base socket
116      * @param host the host
117      * @param port the port
118      * @param autoClose if the socket should auto-close
119      * @return the SSL Socket
120      * @throws IOException thrown if the creation fails
121      */
122     @Override
123     public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
124         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
125         final SSLSocket ss = (SSLSocket) factory.createSocket(s, host, port, autoClose);
126 
127         ss.setEnabledProtocols(protocols);
128         ss.setEnabledCipherSuites(ciphers);
129 
130         return ss;
131     }
132 
133     /**
134      * Creates a new SSL Socket.
135      *
136      * @param address the address to connect to
137      * @param port the port number
138      * @param localAddress the local address
139      * @param localPort the local port
140      * @return the SSL Socket
141      * @throws IOException thrown if the creation fails
142      */
143     @Override
144     public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
145         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
146         final SSLSocket ss = (SSLSocket) factory.createSocket(address, port, localAddress, localPort);
147 
148         ss.setEnabledProtocols(protocols);
149         ss.setEnabledCipherSuites(ciphers);
150 
151         return ss;
152     }
153 
154     /**
155      * Creates a new SSL Socket.
156      *
157      * @param host the host to connect to
158      * @param port the port to connect to
159      * @param localHost the local host
160      * @param localPort the local port
161      * @return the SSL Socket
162      * @throws IOException thrown if the creation fails
163      */
164     @Override
165     public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
166         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
167         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port, localHost, localPort);
168 
169         ss.setEnabledProtocols(protocols);
170         ss.setEnabledCipherSuites(ciphers);
171 
172         return ss;
173     }
174 
175     /**
176      * Creates a new SSL Socket.
177      *
178      * @param host the host to connect to
179      * @param port the port to connect to
180      * @return the SSL Socket
181      * @throws IOException thrown if the creation fails
182      */
183     @Override
184     public Socket createSocket(InetAddress host, int port) throws IOException {
185         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
186         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
187 
188         ss.setEnabledProtocols(protocols);
189         ss.setEnabledCipherSuites(ciphers);
190 
191         return ss;
192     }
193 
194     /**
195      * Creates a new SSL Socket.
196      *
197      * @param host the host to connect to
198      * @param port the port to connect to
199      * @return the SSL Socket
200      * @throws IOException thrown if the creation fails
201      */
202     @Override
203     public Socket createSocket(String host, int port) throws IOException {
204         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
205         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
206 
207         ss.setEnabledProtocols(protocols);
208         ss.setEnabledCipherSuites(ciphers);
209 
210         return ss;
211     }
212 
213     /**
214      * Initializes the SSL Socket Factory Extension.
215      *
216      * @param km the key managers
217      * @param tm the trust managers
218      * @param random the secure random number generator
219      * @throws NoSuchAlgorithmException thrown when an algorithm is not
220      * supported
221      * @throws KeyManagementException thrown if initialization fails
222      */
223     private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random)
224             throws NoSuchAlgorithmException, KeyManagementException {
225         sslCtxt = SSLContext.getInstance("TLS");
226         sslCtxt.init(km, tm, random);
227 
228         protocols = getProtocolList();
229         ciphers = getCipherList();
230     }
231 
232     /**
233      * Initializes the SSL Socket Factory Extension.
234      *
235      * @param ctx the SSL context
236      * @throws NoSuchAlgorithmException thrown when an algorithm is not
237      * supported
238      * @throws KeyManagementException thrown if initialization fails
239      */
240     private void initSSLSocketFactoryEx(SSLContext ctx)
241             throws NoSuchAlgorithmException, KeyManagementException {
242         sslCtxt = ctx;
243 
244         protocols = getProtocolList();
245         ciphers = getCipherList();
246     }
247 
248     /**
249      * Returns the protocol list.
250      *
251      * @return the protocol list
252      */
253     protected String[] getProtocolList() {
254         final String[] preferredProtocols = {"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
255         String[] availableProtocols = null;
256 
257         SSLSocket socket = null;
258 
259         try {
260             final SSLSocketFactory factory = sslCtxt.getSocketFactory();
261             socket = (SSLSocket) factory.createSocket();
262 
263             availableProtocols = socket.getSupportedProtocols();
264             Arrays.sort(availableProtocols);
265         } catch (Exception ex) {
266             LOGGER.debug("Error getting protocol list, using TLSv1", ex);
267             return new String[]{"TLSv1"};
268         } finally {
269             if (socket != null) {
270                 try {
271                     socket.close();
272                 } catch (IOException ex) {
273                     LOGGER.trace("Error closing socket", ex);
274                 }
275             }
276         }
277 
278         final List<String> aa = new ArrayList<String>();
279         for (String preferredProtocol : preferredProtocols) {
280             final int idx = Arrays.binarySearch(availableProtocols, preferredProtocol);
281             if (idx >= 0) {
282                 aa.add(preferredProtocol);
283             }
284         }
285 
286         return aa.toArray(new String[0]);
287     }
288 
289     /**
290      * Returns the cipher list.
291      *
292      * @return the cipher list
293      */
294     protected String[] getCipherList() {
295         final String[] preferredCiphers = {
296             // *_CHACHA20_POLY1305 are 3x to 4x faster than existing cipher suites.
297             //   http://googleonlinesecurity.blogspot.com/2014/04/speeding-up-and-strengthening-https.html
298             // Use them if available. Normative names can be found at (TLS spec depends on IPSec spec):
299             //   http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-01
300             //   http://tools.ietf.org/html/draft-mavrogiannopoulos-chacha-tls-02
301             "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
302             "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
303             "TLS_ECDHE_ECDSA_WITH_CHACHA20_SHA",
304             "TLS_ECDHE_RSA_WITH_CHACHA20_SHA",
305             "TLS_DHE_RSA_WITH_CHACHA20_POLY1305",
306             "TLS_RSA_WITH_CHACHA20_POLY1305",
307             "TLS_DHE_RSA_WITH_CHACHA20_SHA",
308             "TLS_RSA_WITH_CHACHA20_SHA",
309             // Done with bleeding edge, back to TLS v1.2 and below
310             "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
311             "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
312             "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
313             "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
314             "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
315             "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
316             "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
317             "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
318             // TLS v1.0 (with some SSLv3 interop)
319             "TLS_DHE_RSA_WITH_AES_256_CBC_SHA384",
320             "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
321             "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
322             "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
323             "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
324             "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
325             "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
326             "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
327             // RSA key transport sucks, but they are needed as a fallback.
328             // For example, microsoft.com fails under all versions of TLS
329             // if they are not included. If only TLS 1.0 is available at
330             // the client, then google.com will fail too. TLS v1.3 is
331             // trying to deprecate them, so it will be interesteng to see
332             // what happens.
333             "TLS_RSA_WITH_AES_256_CBC_SHA256",
334             "TLS_RSA_WITH_AES_256_CBC_SHA",
335             "TLS_RSA_WITH_AES_128_CBC_SHA256",
336             "TLS_RSA_WITH_AES_128_CBC_SHA",
337         };
338 
339         String[] availableCiphers;
340 
341         try {
342             final SSLSocketFactory factory = sslCtxt.getSocketFactory();
343             availableCiphers = factory.getSupportedCipherSuites();
344             Arrays.sort(availableCiphers);
345         } catch (Exception e) {
346             LOGGER.debug("Error retrieving ciphers", e);
347             return new String[]{
348                 "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
349                 "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
350                 "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
351                 "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
352                 "TLS_RSA_WITH_AES_256_CBC_SHA256",
353                 "TLS_RSA_WITH_AES_256_CBC_SHA",
354                 "TLS_RSA_WITH_AES_128_CBC_SHA256",
355                 "TLS_RSA_WITH_AES_128_CBC_SHA",
356                 "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
357             };
358         }
359 
360         final List<String> aa = new ArrayList<String>();
361         for (String preferredCipher : preferredCiphers) {
362             final int idx = Arrays.binarySearch(availableCiphers, preferredCipher);
363             if (idx >= 0) {
364                 aa.add(preferredCipher);
365             }
366         }
367 
368         aa.add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV");
369 
370         return aa.toArray(new String[0]);
371     }
372 
373     /**
374      * The SSL context.
375      */
376     private SSLContext sslCtxt;
377     /**
378      * The cipher suites.
379      */
380     private String[] ciphers;
381     /**
382      * The protocols.
383      */
384     private String[] protocols;
385 }