View Javadoc
1   package org.owasp.dependencycheck.utils;
2   
3   import java.io.FileInputStream;
4   import java.io.IOException;
5   import java.net.InetAddress;
6   import java.net.Socket;
7   import java.security.KeyManagementException;
8   import java.security.KeyStore;
9   import java.security.KeyStoreException;
10  import java.security.NoSuchAlgorithmException;
11  import java.security.SecureRandom;
12  import java.security.UnrecoverableKeyException;
13  import java.security.cert.CertificateException;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.List;
17  import javax.net.ssl.KeyManager;
18  import javax.net.ssl.KeyManagerFactory;
19  import javax.net.ssl.SSLContext;
20  import javax.net.ssl.SSLSocket;
21  import javax.net.ssl.SSLSocketFactory;
22  import javax.net.ssl.TrustManager;
23  import javax.net.ssl.TrustManagerFactory;
24  import org.apache.commons.lang3.StringUtils;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  /**
30   * TODO - is this class needed anymore as we do not support Java 6 and 7.
31   *
32   * This class is used to enable additional ciphers used by the SSL Socket. This
33   * is specifically because the NVD stopped supporting TLS 1.0 and Java 6 and 7
34   * clients by default were unable to connect to download the NVD data feeds.
35   * <p>
36   * The following code was copied from
37   * http://stackoverflow.com/questions/1037590/which-cipher-suites-to-enable-for-ssl-socket/23365536#23365536
38   *
39   * @author <a href="http://stackoverflow.com/users/608639/jww">jww</a>
40   * @version $Id: $Id
41   */
42  public class SSLSocketFactoryEx extends SSLSocketFactory {
43  
44      /**
45       * The Logger for use throughout the class.
46       */
47      private static final Logger LOGGER = LoggerFactory.getLogger(SSLSocketFactoryEx.class);
48  
49      /**
50       * The SSL context.
51       */
52      private SSLContext sslCtxt;
53      /**
54       * The protocols.
55       */
56      private String[] protocols;
57      /**
58       * The configured settings.
59       */
60      private final Settings settings;
61  
62      /**
63       * Simple boolean flag to prevent logging the protocols repeatedly.
64       */
65      private static boolean protocolsLogged = false;
66  
67      /**
68       * Constructs a new SSLSocketFactory.
69       *
70       * @param settings reference to the configured settings
71       * @throws java.security.NoSuchAlgorithmException thrown when an algorithm
72       * is not supported
73       * @throws java.security.KeyManagementException thrown if initialization
74       * fails
75       */
76      public SSLSocketFactoryEx(Settings settings) throws NoSuchAlgorithmException, KeyManagementException {
77          this.settings = settings;
78          final KeyManager[] km = getKeyManagers();
79          final TrustManager[] tm = getTrustManagers();
80  
81          initSSLSocketFactoryEx(km, tm, null);
82      }
83  
84      /**
85       * Constructs a new SSLSocketFactory.
86       *
87       * @param km the key manager
88       * @param tm the trust manager
89       * @param random secure random
90       * @param settings reference to the configured settings
91       * @throws java.security.NoSuchAlgorithmException thrown when an algorithm
92       * is not supported
93       * @throws java.security.KeyManagementException thrown if initialization
94       * fails
95       */
96      public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random, Settings settings)
97              throws NoSuchAlgorithmException, KeyManagementException {
98          this.settings = settings;
99          initSSLSocketFactoryEx(km, tm, random);
100     }
101 
102     /**
103      * Constructs a new SSLSocketFactory.
104      *
105      * @param ctx the SSL context
106      * @param settings reference to the configured settings
107      * @throws java.security.NoSuchAlgorithmException thrown when an algorithm
108      * is not supported
109      * @throws java.security.KeyManagementException thrown if initialization
110      * fails
111      */
112     public SSLSocketFactoryEx(SSLContext ctx, Settings settings) throws NoSuchAlgorithmException, KeyManagementException {
113         this.settings = settings;
114         initSSLSocketFactoryEx(ctx);
115     }
116 
117     /**
118      * {@inheritDoc}
119      * <p>
120      * Returns the default cipher suites.
121      */
122     @Override
123     public String[] getDefaultCipherSuites() {
124         return sslCtxt.getSocketFactory().getDefaultCipherSuites();
125     }
126 
127     /**
128      * {@inheritDoc}
129      * <p>
130      * Returns the supported cipher suites.
131      */
132     @Override
133     public String[] getSupportedCipherSuites() {
134         return sslCtxt.getSocketFactory().getSupportedCipherSuites();
135     }
136 
137     /**
138      * Returns the default protocols.
139      *
140      * @return the default protocols
141      */
142     public String[] getDefaultProtocols() {
143         return Arrays.copyOf(protocols, protocols.length);
144     }
145 
146     /**
147      * Returns the supported protocols.
148      *
149      * @return the supported protocols
150      */
151     public String[] getSupportedProtocols() {
152         return Arrays.copyOf(protocols, protocols.length);
153     }
154 
155     /**
156      * {@inheritDoc}
157      * <p>
158      * Creates an SSL Socket.
159      */
160     @Override
161     public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
162         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
163         final SSLSocket ss = (SSLSocket) factory.createSocket(s, host, port, autoClose);
164 
165         ss.setEnabledProtocols(protocols);
166 
167         return ss;
168     }
169 
170     /**
171      * {@inheritDoc}
172      * <p>
173      * Creates a new SSL Socket.
174      */
175     @Override
176     public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
177         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
178         final SSLSocket ss = (SSLSocket) factory.createSocket(address, port, localAddress, localPort);
179 
180         ss.setEnabledProtocols(protocols);
181 
182         return ss;
183     }
184 
185     /**
186      * {@inheritDoc}
187      * <p>
188      * Creates a new SSL Socket.
189      */
190     @Override
191     public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
192         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
193         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port, localHost, localPort);
194 
195         ss.setEnabledProtocols(protocols);
196 
197         return ss;
198     }
199 
200     /**
201      * {@inheritDoc}
202      * <p>
203      * Creates a new SSL Socket.
204      */
205     @Override
206     public Socket createSocket(InetAddress host, int port) throws IOException {
207         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
208         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
209 
210         ss.setEnabledProtocols(protocols);
211 
212         return ss;
213     }
214 
215     /**
216      * {@inheritDoc}
217      * <p>
218      * Creates a new SSL Socket.
219      */
220     @Override
221     public Socket createSocket(String host, int port) throws IOException {
222         final SSLSocketFactory factory = sslCtxt.getSocketFactory();
223         final SSLSocket ss = (SSLSocket) factory.createSocket(host, port);
224 
225         ss.setEnabledProtocols(protocols);
226 
227         return ss;
228     }
229 
230     /**
231      * Initializes the SSL Socket Factory Extension.
232      *
233      * @param km the key managers
234      * @param tm the trust managers
235      * @param random the secure random number generator
236      * @throws NoSuchAlgorithmException thrown when an algorithm is not
237      * supported
238      * @throws KeyManagementException thrown if initialization fails
239      */
240     private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random)
241             throws NoSuchAlgorithmException, KeyManagementException {
242         sslCtxt = SSLContext.getInstance("TLS");
243         sslCtxt.init(km, tm, random);
244 
245         protocols = getProtocolList();
246     }
247 
248     /**
249      * Initializes the SSL Socket Factory Extension.
250      *
251      * @param ctx the SSL context
252      * @throws NoSuchAlgorithmException thrown when an algorithm is not
253      * supported
254      * @throws KeyManagementException thrown if initialization fails
255      */
256     private void initSSLSocketFactoryEx(SSLContext ctx)
257             throws NoSuchAlgorithmException, KeyManagementException {
258         sslCtxt = ctx;
259         protocols = getProtocolList();
260     }
261 
262     /**
263      * Returns the protocol list.
264      *
265      * @return the protocol list
266      */
267     @SuppressWarnings("StringSplitter")
268     protected String[] getProtocolList() {
269         SSLSocket socket = null;
270         final String[] availableProtocols;
271         final String[] preferredProtocols = settings.getString(
272                 Settings.KEYS.DOWNLOADER_TLS_PROTOCOL_LIST,
273                 "TLSv1.1,TLSv1.2,TLSv1.3")
274                 .split(",");
275         try {
276             final SSLSocketFactory factory = sslCtxt.getSocketFactory();
277             socket = (SSLSocket) factory.createSocket();
278 
279             availableProtocols = socket.getSupportedProtocols();
280             Arrays.sort(availableProtocols);
281             if (LOGGER.isDebugEnabled() && !protocolsLogged) {
282                 protocolsLogged = true;
283                 LOGGER.debug("Available Protocols:");
284                 for (String p : availableProtocols) {
285                     LOGGER.debug(p);
286                 }
287             }
288         } catch (Exception ex) {
289             LOGGER.debug("Error getting protocol list, using TLSv1.1-1.3", ex);
290             return new String[]{"TLSv1.1", "TLSv1.2", "TLSv1.3"};
291         } finally {
292             if (socket != null) {
293                 try {
294                     socket.close();
295                 } catch (IOException ex) {
296                     LOGGER.trace("Error closing socket", ex);
297                 }
298             }
299         }
300 
301         final List<String> aa = new ArrayList<>();
302         for (String preferredProtocol : preferredProtocols) {
303             final int idx = Arrays.binarySearch(availableProtocols, preferredProtocol);
304             if (idx >= 0) {
305                 aa.add(preferredProtocol);
306             }
307         }
308 
309         return aa.toArray(new String[0]);
310     }
311 
312     private KeyManager[] getKeyManagers() {
313         KeyManager[] km = null;
314         final String ksPath = System.getProperty("javax.net.ssl.keyStore");
315         final String ksType = System.getProperty("javax.net.ssl.keyStoreType");
316         final String ksPass = System.getProperty("javax.net.ssl.keyStorePassword");
317 
318         if (!StringUtils.isAnyEmpty(ksPath, ksType, ksPass)) {
319             try (FileInputStream fis = new FileInputStream(ksPath)) {
320                 final KeyStore ks = KeyStore.getInstance(ksType);
321                 ks.load(fis, ksPass.toCharArray());
322                 final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
323                 kmf.init(ks, ksPass.toCharArray());
324                 km = kmf.getKeyManagers();
325             } catch (KeyStoreException | IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException ex) {
326                 throw new RuntimeException(ex);
327             }
328         }
329         return km;
330     }
331 
332     private TrustManager[] getTrustManagers() {
333         TrustManager[] tm = null;
334         final String ksType = System.getProperty("javax.net.ssl.keyStoreType");
335         final String tsPath = System.getProperty("javax.net.ssl.trustStore");
336         final String tsPass = System.getProperty("javax.net.ssl.trustStorePassword");
337 
338         if (!StringUtils.isAnyEmpty(tsPath, ksType, tsPass)) {
339             try (FileInputStream fis = new FileInputStream(tsPath)) {
340                 final KeyStore ts = KeyStore.getInstance(ksType);
341                 ts.load(fis, tsPass.toCharArray());
342                 final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
343                 tmf.init(ts);
344                 tm = tmf.getTrustManagers();
345             } catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException ex) {
346                 throw new RuntimeException(ex);
347             }
348         }
349         return tm;
350     }
351 }