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