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 sslCtxt.getSocketFactory().getDefaultCipherSuites();
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 sslCtxt.getSocketFactory().getSupportedCipherSuites();
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 
129         return ss;
130     }
131 
132     /**
133      * Creates a new SSL Socket.
134      *
135      * @param address the address to connect to
136      * @param port the port number
137      * @param localAddress the local address
138      * @param localPort the local port
139      * @return the SSL Socket
140      * @throws IOException thrown if the creation fails
141      */
142     @Override
143     public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
144         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
145         final SSLSocket ss = (SSLSocket) factory.createSocket(address, port, localAddress, localPort);
146 
147         ss.setEnabledProtocols(protocols);
148 
149         return ss;
150     }
151 
152     /**
153      * Creates a new SSL Socket.
154      *
155      * @param host the host to connect to
156      * @param port the port to connect to
157      * @param localHost the local host
158      * @param localPort the local port
159      * @return the SSL Socket
160      * @throws IOException thrown if the creation fails
161      */
162     @Override
163     public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
164         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
165         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port, localHost, localPort);
166 
167         ss.setEnabledProtocols(protocols);
168 
169         return ss;
170     }
171 
172     /**
173      * Creates a new SSL Socket.
174      *
175      * @param host the host to connect to
176      * @param port the port to connect to
177      * @return the SSL Socket
178      * @throws IOException thrown if the creation fails
179      */
180     @Override
181     public Socket createSocket(InetAddress host, int port) throws IOException {
182         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
183         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
184 
185         ss.setEnabledProtocols(protocols);
186 
187         return ss;
188     }
189 
190     /**
191      * Creates a new SSL Socket.
192      *
193      * @param host the host to connect to
194      * @param port the port to connect to
195      * @return the SSL Socket
196      * @throws IOException thrown if the creation fails
197      */
198     @Override
199     public Socket createSocket(String host, int port) throws IOException {
200         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
201         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
202 
203         ss.setEnabledProtocols(protocols);
204 
205         return ss;
206     }
207 
208     /**
209      * Initializes the SSL Socket Factory Extension.
210      *
211      * @param km the key managers
212      * @param tm the trust managers
213      * @param random the secure random number generator
214      * @throws NoSuchAlgorithmException thrown when an algorithm is not
215      * supported
216      * @throws KeyManagementException thrown if initialization fails
217      */
218     private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random)
219             throws NoSuchAlgorithmException, KeyManagementException {
220         sslCtxt = SSLContext.getInstance("TLS");
221         sslCtxt.init(km, tm, random);
222 
223         protocols = getProtocolList();
224     }
225 
226     /**
227      * Initializes the SSL Socket Factory Extension.
228      *
229      * @param ctx the SSL context
230      * @throws NoSuchAlgorithmException thrown when an algorithm is not
231      * supported
232      * @throws KeyManagementException thrown if initialization fails
233      */
234     private void initSSLSocketFactoryEx(SSLContext ctx)
235             throws NoSuchAlgorithmException, KeyManagementException {
236         sslCtxt = ctx;
237         protocols = getProtocolList();
238     }
239 
240     /**
241      * Returns the protocol list.
242      *
243      * @return the protocol list
244      */
245     protected String[] getProtocolList() {
246         SSLSocket socket = null;
247         String[] availableProtocols = null;
248         final String[] preferredProtocols = Settings.getString(
249                 Settings.KEYS.DOWNLOADER_TLS_PROTOCOL_LIST,
250                 "TLSv1,TLSv1.1,TLSv1.2,TLSv1.3")
251                 .split(",");
252         try {
253             final SSLSocketFactory factory = sslCtxt.getSocketFactory();
254             socket = (SSLSocket) factory.createSocket();
255 
256             availableProtocols = socket.getSupportedProtocols();
257             Arrays.sort(availableProtocols);
258             if (LOGGER.isDebugEnabled()) {
259                 LOGGER.debug("Available Protocols:");
260                 for (String p : availableProtocols) {
261                     LOGGER.debug(p);
262                 }
263             }
264         } catch (Exception ex) {
265             LOGGER.debug("Error getting protocol list, using TLSv1", ex);
266             return new String[]{"TLSv1"};
267         } finally {
268             if (socket != null) {
269                 try {
270                     socket.close();
271                 } catch (IOException ex) {
272                     LOGGER.trace("Error closing socket", ex);
273                 }
274             }
275         }
276 
277         final List<String> aa = new ArrayList<String>();
278         for (String preferredProtocol : preferredProtocols) {
279             final int idx = Arrays.binarySearch(availableProtocols, preferredProtocol);
280             if (idx >= 0) {
281                 aa.add(preferredProtocol);
282             }
283         }
284 
285         return aa.toArray(new String[0]);
286     }
287 
288     /**
289      * The SSL context.
290      */
291     private SSLContext sslCtxt;
292     /**
293      * The protocols.
294      */
295     private String[] protocols;
296 }