1 /*
   2  * Copyright (c) 2015, 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 import java.io.BufferedInputStream;
  25 import java.io.BufferedOutputStream;
  26 import java.io.IOException;
  27 import java.io.InputStream;
  28 import java.io.OutputStream;
  29 import java.security.NoSuchAlgorithmException;
  30 import java.security.Security;
  31 import java.util.concurrent.TimeUnit;
  32 import javax.net.ssl.SSLContext;
  33 import javax.net.ssl.SSLHandshakeException;
  34 import javax.net.ssl.SSLServerSocket;
  35 import javax.net.ssl.SSLServerSocketFactory;
  36 import javax.net.ssl.SSLSocket;
  37 import javax.net.ssl.SSLSocketFactory;
  38 
  39 /**
  40  * @test
  41  * @bug 8076221
  42  * @summary Check if weak cipher suites are disabled
  43  * @run main/othervm DisabledAlgorithms default
  44  * @run main/othervm DisabledAlgorithms empty
  45  */
  46 public class DisabledAlgorithms {
  47 
  48     private static final String pathToStores = "../etc";
  49     private static final String keyStoreFile = "keystore";
  50     private static final String trustStoreFile = "truststore";
  51     private static final String passwd = "passphrase";
  52 
  53     private static final String keyFilename =
  54             System.getProperty("test.src", "./") + "/" + pathToStores +
  55                 "/" + keyStoreFile;
  56 
  57     private static final String trustFilename =
  58             System.getProperty("test.src", "./") + "/" + pathToStores +
  59                 "/" + trustStoreFile;
  60 
  61     // supported RC4 cipher suites
  62     // it does not contain KRB5 cipher suites because they need a KDC
  63     private static final String[] rc4_ciphersuites = new String[] {
  64         "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
  65         "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
  66         "SSL_RSA_WITH_RC4_128_SHA",
  67         "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
  68         "TLS_ECDH_RSA_WITH_RC4_128_SHA",
  69         "SSL_RSA_WITH_RC4_128_MD5",
  70         "TLS_ECDH_anon_WITH_RC4_128_SHA",
  71         "SSL_DH_anon_WITH_RC4_128_MD5"
  72     };
  73 
  74     public static void main(String[] args) throws Exception {
  75         if (args.length < 1) {
  76             throw new RuntimeException("No parameters specified");
  77         }
  78 
  79         System.setProperty("javax.net.ssl.keyStore", keyFilename);
  80         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
  81         System.setProperty("javax.net.ssl.trustStore", trustFilename);
  82         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
  83 
  84         switch (args[0]) {
  85             case "default":
  86                 // use default jdk.tls.disabledAlgorithms
  87                 System.out.println("jdk.tls.disabledAlgorithms = "
  88                         + Security.getProperty("jdk.tls.disabledAlgorithms"));
  89 
  90                 // check if RC4 cipher suites can't be used by default
  91                 checkFailure(rc4_ciphersuites);
  92                 break;
  93             case "empty":
  94                 // reset jdk.tls.disabledAlgorithms
  95                 Security.setProperty("jdk.tls.disabledAlgorithms", "");
  96                 System.out.println("jdk.tls.disabledAlgorithms = "
  97                         + Security.getProperty("jdk.tls.disabledAlgorithms"));
  98 
  99                 // check if RC4 cipher suites can be used
 100                 // if jdk.tls.disabledAlgorithms is empty
 101                 checkSuccess(rc4_ciphersuites);
 102                 break;
 103             default:
 104                 throw new RuntimeException("Wrong parameter: " + args[0]);
 105         }
 106 
 107         System.out.println("Test passed");
 108     }
 109 
 110     /*
 111      * Checks if that specified cipher suites cannot be used.
 112      */
 113     private static void checkFailure(String[] ciphersuites) throws Exception {
 114         try (SSLServer server = SSLServer.init(ciphersuites)) {
 115             startNewThread(server);
 116             while (!server.isRunning()) {
 117                 sleep();
 118             }
 119 
 120             int port = server.getPort();
 121             for (String ciphersuite : ciphersuites) {
 122                 try (SSLClient client = SSLClient.init(port, ciphersuite)) {
 123                     client.connect();
 124                     throw new RuntimeException("Expected SSLHandshakeException "
 125                             + "not thrown");
 126                 } catch (SSLHandshakeException e) {
 127                     System.out.println("Expected exception on client side: "
 128                             + e);
 129                 }
 130             }
 131 
 132             while (server.isRunning()) {
 133                 sleep();
 134             }
 135 
 136             if (!server.sslError()) {
 137                 throw new RuntimeException("Expected SSL exception "
 138                         + "not thrown on server side");
 139             }
 140         }
 141 
 142     }
 143 
 144     /*
 145      * Checks if specified cipher suites can be used.
 146      */
 147     private static void checkSuccess(String[] ciphersuites) throws Exception {
 148         try (SSLServer server = SSLServer.init(ciphersuites)) {
 149             startNewThread(server);
 150             while (!server.isRunning()) {
 151                 sleep();
 152             }
 153 
 154             int port = server.getPort();
 155             for (String ciphersuite : ciphersuites) {
 156                 try (SSLClient client = SSLClient.init(port, ciphersuite)) {
 157                     client.connect();
 158                     String negotiated = client.getNegotiatedCipherSuite();
 159                     System.out.println("Negotiated cipher suite: "
 160                             + negotiated);
 161                     if (!negotiated.equals(ciphersuite)) {
 162                         throw new RuntimeException("Unexpected cipher suite: "
 163                                 + negotiated);
 164                     }
 165                 }
 166             }
 167 
 168             server.stop();
 169             while (server.isRunning()) {
 170                 sleep();
 171             }
 172 
 173             if (server.error()) {
 174                 throw new RuntimeException("Unexpected error on server side");
 175             }
 176         }
 177 
 178     }
 179 
 180     private static Thread startNewThread(SSLServer server) {
 181         Thread serverThread = new Thread(server, "SSL server thread");
 182         serverThread.setDaemon(true);
 183         serverThread.start();
 184         return serverThread;
 185     }
 186 
 187     private static void sleep() {
 188         try {
 189             TimeUnit.MILLISECONDS.sleep(50);
 190         } catch (InterruptedException e) {
 191             // do nothing
 192         }
 193     }
 194 
 195     static class SSLServer implements Runnable, AutoCloseable {
 196 
 197         private final SSLServerSocket ssocket;
 198         private volatile boolean stopped = false;
 199         private volatile boolean running = false;
 200         private volatile boolean sslError = false;
 201         private volatile boolean otherError = false;
 202 
 203         private SSLServer(SSLServerSocket ssocket) {
 204             this.ssocket = ssocket;
 205         }
 206 
 207         @Override
 208         public void run() {
 209             System.out.println("Server: started");
 210             running = true;
 211             while (!stopped) {
 212                 try (SSLSocket socket = (SSLSocket) ssocket.accept()) {
 213                     System.out.println("Server: accepted client connection");
 214                     InputStream in = socket.getInputStream();
 215                     OutputStream out = socket.getOutputStream();
 216                     int b = in.read();
 217                     if (b < 0) {
 218                         throw new IOException("Unexpected EOF");
 219                     }
 220                     System.out.println("Server: send data: " + b);
 221                     out.write(b);
 222                     out.flush();
 223                     socket.getSession().invalidate();
 224                 } catch (SSLHandshakeException e) {
 225                     System.out.println("Server: run: " + e);
 226                     sslError = true;
 227                     stopped = true;
 228                 } catch (IOException e) {
 229                     if (!stopped) {
 230                         System.out.println("Server: run: unexpected exception: "
 231                                 + e);
 232                         e.printStackTrace();
 233                         otherError = true;
 234                         stopped = true;
 235                     } else {
 236                         System.out.println("Server: run: " + e);
 237                         System.out.println("The exception above occurred "
 238                                     + "because socket was closed, "
 239                                     + "please ignore it");
 240                     }
 241                 }
 242             }
 243 
 244             System.out.println("Server: finished");
 245             running = false;
 246         }
 247 
 248         int getPort() {
 249             return ssocket.getLocalPort();
 250         }
 251 
 252         String[] getEnabledCiperSuites() {
 253             return ssocket.getEnabledCipherSuites();
 254         }
 255 
 256         boolean isRunning() {
 257             return running;
 258         }
 259 
 260         boolean sslError() {
 261             return sslError;
 262         }
 263 
 264         boolean error() {
 265             return sslError || otherError;
 266         }
 267 
 268         void stop() {
 269             stopped = true;
 270             if (!ssocket.isClosed()) {
 271                 try {
 272                     System.out.println("Server: close socket");
 273                     ssocket.close();
 274                 } catch (IOException e) {
 275                     System.out.println("Server: close: " + e);
 276                 }
 277             }
 278         }
 279 
 280         @Override
 281         public void close() {
 282             stop();
 283         }
 284 
 285         static SSLServer init(String[] ciphersuites)
 286                 throws IOException {
 287             SSLServerSocketFactory ssf = (SSLServerSocketFactory)
 288                     SSLServerSocketFactory.getDefault();
 289             SSLServerSocket ssocket = (SSLServerSocket)
 290                     ssf.createServerSocket(0);
 291 
 292             if (ciphersuites != null) {
 293                 System.out.println("Server: enable cipher suites: "
 294                         + java.util.Arrays.toString(ciphersuites));
 295                 ssocket.setEnabledCipherSuites(ciphersuites);
 296             }
 297 
 298             return new SSLServer(ssocket);
 299         }
 300     }
 301 
 302     static class SSLClient implements AutoCloseable {
 303 
 304         private final SSLSocket socket;
 305 
 306         private SSLClient(SSLSocket socket) {
 307             this.socket = socket;
 308         }
 309 
 310         void connect() throws IOException {
 311             System.out.println("Client: connect to server");
 312             try (
 313                     BufferedInputStream bis = new BufferedInputStream(
 314                             socket.getInputStream());
 315                     BufferedOutputStream bos = new BufferedOutputStream(
 316                             socket.getOutputStream())) {
 317                 bos.write('x');
 318                 bos.flush();
 319 
 320                 int read = bis.read();
 321                 if (read < 0) {
 322                     throw new IOException("Client: couldn't read a response");
 323                 }
 324                 socket.getSession().invalidate();
 325             }
 326         }
 327 
 328         String[] getEnabledCiperSuites() {
 329             return socket.getEnabledCipherSuites();
 330         }
 331 
 332         String getNegotiatedCipherSuite() {
 333             return socket.getSession().getCipherSuite();
 334         }
 335 
 336         @Override
 337         public void close() throws Exception {
 338             if (!socket.isClosed()) {
 339                 try {
 340                     socket.close();
 341                 } catch (IOException e) {
 342                     System.out.println("Client: close: " + e);
 343                 }
 344             }
 345         }
 346 
 347         static SSLClient init(int port)
 348                 throws NoSuchAlgorithmException, IOException {
 349             return init(port, null);
 350         }
 351 
 352         static SSLClient init(int port, String ciphersuite)
 353                 throws NoSuchAlgorithmException, IOException {
 354             SSLContext context = SSLContext.getDefault();
 355             SSLSocketFactory ssf = (SSLSocketFactory)
 356                     context.getSocketFactory();
 357             SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", port);
 358 
 359             if (ciphersuite != null) {
 360                 System.out.println("Client: enable cipher suite: "
 361                         + ciphersuite);
 362                 socket.setEnabledCipherSuites(new String[] { ciphersuite });
 363             }
 364 
 365             return new SSLClient(socket);
 366         }
 367 
 368     }
 369 
 370 
 371 }