1 /*
2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8076221 8211883
27  * @summary Check if weak cipher suites are disabled
28  * @modules jdk.crypto.ec
29  * @run main/othervm DisabledAlgorithms default
30  * @run main/othervm DisabledAlgorithms empty
31  */
32 
33 import java.io.BufferedInputStream;
34 import java.io.BufferedOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.security.NoSuchAlgorithmException;
39 import java.security.Security;
40 import java.util.concurrent.TimeUnit;
41 import javax.net.ssl.SSLContext;
42 import javax.net.ssl.SSLHandshakeException;
43 import javax.net.ssl.SSLServerSocket;
44 import javax.net.ssl.SSLServerSocketFactory;
45 import javax.net.ssl.SSLSocket;
46 import javax.net.ssl.SSLSocketFactory;
47 
48 public class DisabledAlgorithms {
49 
50     private static final String pathToStores = "../etc";
51     private static final String keyStoreFile = "keystore";
52     private static final String trustStoreFile = "truststore";
53     private static final String passwd = "passphrase";
54 
55     private static final String keyFilename =
56             System.getProperty("test.src", "./") + "/" + pathToStores +
57                 "/" + keyStoreFile;
58 
59     private static final String trustFilename =
60             System.getProperty("test.src", "./") + "/" + pathToStores +
61                 "/" + trustStoreFile;
62 
63     // supported RC4, NULL, and anon cipher suites
64     // it does not contain KRB5 cipher suites because they need a KDC
65     private static final String[] rc4_null_anon_ciphersuites = new String[] {
66         "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
67         "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
68         "SSL_RSA_WITH_RC4_128_SHA",
69         "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
70         "TLS_ECDH_RSA_WITH_RC4_128_SHA",
71         "SSL_RSA_WITH_RC4_128_MD5",
72         "TLS_ECDH_anon_WITH_RC4_128_SHA",
73         "SSL_DH_anon_WITH_RC4_128_MD5",
74         "SSL_RSA_WITH_NULL_MD5",
75         "SSL_RSA_WITH_NULL_SHA",
76         "TLS_RSA_WITH_NULL_SHA256",
77         "TLS_ECDH_ECDSA_WITH_NULL_SHA",
78         "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
79         "TLS_ECDH_RSA_WITH_NULL_SHA",
80         "TLS_ECDHE_RSA_WITH_NULL_SHA",
81         "TLS_ECDH_anon_WITH_NULL_SHA",
82         "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
83         "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
84         "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
85         "SSL_DH_anon_WITH_DES_CBC_SHA",
86         "SSL_DH_anon_WITH_RC4_128_MD5",
87         "TLS_DH_anon_WITH_AES_128_CBC_SHA",
88         "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
89         "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
90         "TLS_DH_anon_WITH_AES_256_CBC_SHA",
91         "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
92         "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
93         "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
94         "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
95         "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
96         "TLS_ECDH_anon_WITH_NULL_SHA",
97         "TLS_ECDH_anon_WITH_RC4_128_SHA"
98     };
99 
main(String[] args)100     public static void main(String[] args) throws Exception {
101         if (args.length < 1) {
102             throw new RuntimeException("No parameters specified");
103         }
104 
105         System.setProperty("javax.net.ssl.keyStore", keyFilename);
106         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
107         System.setProperty("javax.net.ssl.trustStore", trustFilename);
108         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
109 
110         switch (args[0]) {
111             case "default":
112                 // use default jdk.tls.disabledAlgorithms
113                 System.out.println("jdk.tls.disabledAlgorithms = "
114                         + Security.getProperty("jdk.tls.disabledAlgorithms"));
115 
116                 // check if RC4, NULL, and anon cipher suites
117                 // can't be used by default
118                 checkFailure(rc4_null_anon_ciphersuites);
119                 break;
120             case "empty":
121                 // reset jdk.tls.disabledAlgorithms
122                 Security.setProperty("jdk.tls.disabledAlgorithms", "");
123                 System.out.println("jdk.tls.disabledAlgorithms = "
124                         + Security.getProperty("jdk.tls.disabledAlgorithms"));
125 
126                 // check if RC4, NULL, and anon cipher suites can be used
127                 // if jdk.tls.disabledAlgorithms is empty
128                 checkSuccess(rc4_null_anon_ciphersuites);
129                 break;
130             default:
131                 throw new RuntimeException("Wrong parameter: " + args[0]);
132         }
133 
134         System.out.println("Test passed");
135     }
136 
137     /*
138      * Checks if that specified cipher suites cannot be used.
139      */
checkFailure(String[] ciphersuites)140     private static void checkFailure(String[] ciphersuites) throws Exception {
141         try (SSLServer server = SSLServer.init(ciphersuites)) {
142             startNewThread(server);
143             while (!server.isRunning()) {
144                 sleep();
145             }
146 
147             int port = server.getPort();
148             for (String ciphersuite : ciphersuites) {
149                 try (SSLClient client = SSLClient.init(port, ciphersuite)) {
150                     client.connect();
151                     throw new RuntimeException("Expected SSLHandshakeException "
152                             + "not thrown");
153                 } catch (SSLHandshakeException e) {
154                     System.out.println("Expected exception on client side: "
155                             + e);
156                 }
157             }
158 
159             while (server.isRunning()) {
160                 sleep();
161             }
162 
163             if (!server.sslError()) {
164                 throw new RuntimeException("Expected SSL exception "
165                         + "not thrown on server side");
166             }
167         }
168 
169     }
170 
171     /*
172      * Checks if specified cipher suites can be used.
173      */
checkSuccess(String[] ciphersuites)174     private static void checkSuccess(String[] ciphersuites) throws Exception {
175         try (SSLServer server = SSLServer.init(ciphersuites)) {
176             startNewThread(server);
177             while (!server.isRunning()) {
178                 sleep();
179             }
180 
181             int port = server.getPort();
182             for (String ciphersuite : ciphersuites) {
183                 try (SSLClient client = SSLClient.init(port, ciphersuite)) {
184                     client.connect();
185                     String negotiated = client.getNegotiatedCipherSuite();
186                     System.out.println("Negotiated cipher suite: "
187                             + negotiated);
188                     if (!negotiated.equals(ciphersuite)) {
189                         throw new RuntimeException("Unexpected cipher suite: "
190                                 + negotiated);
191                     }
192                 }
193             }
194 
195             server.stop();
196             while (server.isRunning()) {
197                 sleep();
198             }
199 
200             if (server.error()) {
201                 throw new RuntimeException("Unexpected error on server side");
202             }
203         }
204 
205     }
206 
startNewThread(SSLServer server)207     private static Thread startNewThread(SSLServer server) {
208         Thread serverThread = new Thread(server, "SSL server thread");
209         serverThread.setDaemon(true);
210         serverThread.start();
211         return serverThread;
212     }
213 
sleep()214     private static void sleep() {
215         try {
216             TimeUnit.MILLISECONDS.sleep(50);
217         } catch (InterruptedException e) {
218             // do nothing
219         }
220     }
221 
222     static class SSLServer implements Runnable, AutoCloseable {
223 
224         private final SSLServerSocket ssocket;
225         private volatile boolean stopped = false;
226         private volatile boolean running = false;
227         private volatile boolean sslError = false;
228         private volatile boolean otherError = false;
229 
SSLServer(SSLServerSocket ssocket)230         private SSLServer(SSLServerSocket ssocket) {
231             this.ssocket = ssocket;
232         }
233 
234         @Override
run()235         public void run() {
236             System.out.println("Server: started");
237             running = true;
238             while (!stopped) {
239                 try (SSLSocket socket = (SSLSocket) ssocket.accept()) {
240                     System.out.println("Server: accepted client connection");
241                     InputStream in = socket.getInputStream();
242                     OutputStream out = socket.getOutputStream();
243                     int b = in.read();
244                     if (b < 0) {
245                         throw new IOException("Unexpected EOF");
246                     }
247                     System.out.println("Server: send data: " + b);
248                     out.write(b);
249                     out.flush();
250                     socket.getSession().invalidate();
251                 } catch (SSLHandshakeException e) {
252                     System.out.println("Server: run: " + e);
253                     sslError = true;
254                     stopped = true;
255                 } catch (IOException e) {
256                     if (!stopped) {
257                         System.out.println("Server: run: unexpected exception: "
258                                 + e);
259                         e.printStackTrace();
260                         otherError = true;
261                         stopped = true;
262                     } else {
263                         System.out.println("Server: run: " + e);
264                         System.out.println("The exception above occurred "
265                                     + "because socket was closed, "
266                                     + "please ignore it");
267                     }
268                 }
269             }
270 
271             System.out.println("Server: finished");
272             running = false;
273         }
274 
getPort()275         int getPort() {
276             return ssocket.getLocalPort();
277         }
278 
getEnabledCiperSuites()279         String[] getEnabledCiperSuites() {
280             return ssocket.getEnabledCipherSuites();
281         }
282 
isRunning()283         boolean isRunning() {
284             return running;
285         }
286 
sslError()287         boolean sslError() {
288             return sslError;
289         }
290 
error()291         boolean error() {
292             return sslError || otherError;
293         }
294 
stop()295         void stop() {
296             stopped = true;
297             if (!ssocket.isClosed()) {
298                 try {
299                     System.out.println("Server: close socket");
300                     ssocket.close();
301                 } catch (IOException e) {
302                     System.out.println("Server: close: " + e);
303                 }
304             }
305         }
306 
307         @Override
close()308         public void close() {
309             stop();
310         }
311 
init(String[] ciphersuites)312         static SSLServer init(String[] ciphersuites)
313                 throws IOException {
314             SSLServerSocketFactory ssf = (SSLServerSocketFactory)
315                     SSLServerSocketFactory.getDefault();
316             SSLServerSocket ssocket = (SSLServerSocket)
317                     ssf.createServerSocket(0);
318 
319             if (ciphersuites != null) {
320                 System.out.println("Server: enable cipher suites: "
321                         + java.util.Arrays.toString(ciphersuites));
322                 ssocket.setEnabledCipherSuites(ciphersuites);
323             }
324 
325             return new SSLServer(ssocket);
326         }
327     }
328 
329     static class SSLClient implements AutoCloseable {
330 
331         private final SSLSocket socket;
332 
SSLClient(SSLSocket socket)333         private SSLClient(SSLSocket socket) {
334             this.socket = socket;
335         }
336 
connect()337         void connect() throws IOException {
338             System.out.println("Client: connect to server");
339             try (
340                     BufferedInputStream bis = new BufferedInputStream(
341                             socket.getInputStream());
342                     BufferedOutputStream bos = new BufferedOutputStream(
343                             socket.getOutputStream())) {
344                 bos.write('x');
345                 bos.flush();
346 
347                 int read = bis.read();
348                 if (read < 0) {
349                     throw new IOException("Client: couldn't read a response");
350                 }
351                 socket.getSession().invalidate();
352             }
353         }
354 
getEnabledCiperSuites()355         String[] getEnabledCiperSuites() {
356             return socket.getEnabledCipherSuites();
357         }
358 
getNegotiatedCipherSuite()359         String getNegotiatedCipherSuite() {
360             return socket.getSession().getCipherSuite();
361         }
362 
363         @Override
close()364         public void close() throws Exception {
365             if (!socket.isClosed()) {
366                 try {
367                     socket.close();
368                 } catch (IOException e) {
369                     System.out.println("Client: close: " + e);
370                 }
371             }
372         }
373 
init(int port)374         static SSLClient init(int port)
375                 throws NoSuchAlgorithmException, IOException {
376             return init(port, null);
377         }
378 
init(int port, String ciphersuite)379         static SSLClient init(int port, String ciphersuite)
380                 throws NoSuchAlgorithmException, IOException {
381             SSLContext context = SSLContext.getDefault();
382             SSLSocketFactory ssf = (SSLSocketFactory)
383                     context.getSocketFactory();
384             SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", port);
385 
386             if (ciphersuite != null) {
387                 System.out.println("Client: enable cipher suite: "
388                         + ciphersuite);
389                 socket.setEnabledCipherSuites(new String[] { ciphersuite });
390             }
391 
392             return new SSLClient(socket);
393         }
394 
395     }
396 
397 
398 }
399