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 =
  49             "../../../../sun/security/ssl/etc";
  50     private static final String keyStoreFile = "keystore";
  51     private static final String trustStoreFile = "truststore";
  52     private static final String passwd = "passphrase";
  53 
  54     private static final String keyFilename =
  55             System.getProperty("test.src", "./") + "/" + pathToStores +
  56                 "/" + keyStoreFile;
  57 
  58     private static final String trustFilename =
  59             System.getProperty("test.src", "./") + "/" + pathToStores +
  60                 "/" + trustStoreFile;
  61 
  62     // supported RC4 cipher suites
  63     // it does not contain KRB5 cipher suites because they need a KDC
  64     private static final String[] rc4_ciphersuites = new String[] {
  65         "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
  66         "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
  67         "SSL_RSA_WITH_RC4_128_SHA",
  68         "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
  69         "TLS_ECDH_RSA_WITH_RC4_128_SHA",
  70         "SSL_RSA_WITH_RC4_128_MD5",
  71         "TLS_ECDH_anon_WITH_RC4_128_SHA",
  72         "SSL_DH_anon_WITH_RC4_128_MD5"
  73     };
  74 
  75     public static void main(String[] args) throws Exception {
  76         if (args.length < 1) {
  77             throw new RuntimeException("No parameters specified");
  78         }
  79 
  80         System.setProperty("javax.net.ssl.keyStore", keyFilename);
  81         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
  82         System.setProperty("javax.net.ssl.trustStore", trustFilename);
  83         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
  84 
  85         switch (args[0]) {
  86             case "default":
  87                 // use default jdk.tls.disabledAlgorithms
  88                 System.out.println("jdk.tls.disabledAlgorithms = "
  89                         + Security.getProperty("jdk.tls.disabledAlgorithms"));
  90 
  91                 // check if RC4 cipher suites can't be used by default
  92                 checkFailure(rc4_ciphersuites);
  93                 break;
  94             case "empty":
  95                 // reset jdk.tls.disabledAlgorithms
  96                 Security.setProperty("jdk.tls.disabledAlgorithms", "");
  97                 System.out.println("jdk.tls.disabledAlgorithms = "
  98                         + Security.getProperty("jdk.tls.disabledAlgorithms"));
  99 
 100                 // check if RC4 cipher suites can be used
 101                 // if jdk.tls.disabledAlgorithms is empty
 102                 checkSuccess(rc4_ciphersuites);
 103                 break;
 104             default:
 105                 throw new RuntimeException("Wrong parameter: " + args[0]);
 106         }
 107     }
 108 
 109     /*
 110      * Checks if that specified cipher suites cannot be used.
 111      */
 112     private static void checkFailure(String[] ciphersuites) throws Exception {
 113         try (SSLServer server = SSLServer.init(ciphersuites)) {
 114             startNewThread(server);
 115             while (!server.isRunning()) {
 116                 sleep();
 117             }
 118 
 119             int port = server.getPort();
 120             for (String ciphersuite : ciphersuites) {
 121                 try (SSLClient client = SSLClient.init(port, ciphersuite)) {
 122                     client.connect();
 123                     throw new RuntimeException("Expected SSLHandshakeException "
 124                             + "not thrown");
 125                 } catch (SSLHandshakeException e) {
 126                     System.out.println("Expected exception on client side: "
 127                             + e);
 128                 }
 129             }
 130 
 131             server.stop();
 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                 } catch (IOException e) {
 228                     if (!stopped) {
 229                         System.out.println("Server: run: " + e);
 230                         e.printStackTrace();
 231                         otherError = true;
 232                     }
 233                 }
 234             }
 235 
 236             System.out.println("Server: finished");
 237             running = false;
 238         }
 239 
 240         int getPort() {
 241             return ssocket.getLocalPort();
 242         }
 243 
 244         String[] getEnabledCiperSuites() {
 245             return ssocket.getEnabledCipherSuites();
 246         }
 247 
 248         boolean isRunning() {
 249             return running;
 250         }
 251 
 252         boolean sslError() {
 253             return sslError;
 254         }
 255 
 256         boolean error() {
 257             return sslError || otherError;
 258         }
 259 
 260         void stop() {
 261             stopped = true;
 262             if (!ssocket.isClosed()) {
 263                 try {
 264                     ssocket.close();
 265                 } catch (IOException e) {
 266                     System.out.println("Server: close: " + e);
 267                 }
 268             }
 269         }
 270 
 271         @Override
 272         public void close() {
 273             stop();
 274         }
 275 
 276         static SSLServer init(String[] ciphersuites)
 277                 throws IOException {
 278             SSLServerSocketFactory ssf = (SSLServerSocketFactory)
 279                     SSLServerSocketFactory.getDefault();
 280             SSLServerSocket ssocket = (SSLServerSocket)
 281                     ssf.createServerSocket(0);
 282 
 283             if (ciphersuites != null) {
 284                 System.out.println("Server: enable cipher suites: "
 285                         + java.util.Arrays.toString(ciphersuites));
 286                 ssocket.setEnabledCipherSuites(ciphersuites);
 287             }
 288 
 289             return new SSLServer(ssocket);
 290         }
 291     }
 292 
 293     static class SSLClient implements AutoCloseable {
 294 
 295         private final SSLSocket socket;
 296 
 297         private SSLClient(SSLSocket socket) {
 298             this.socket = socket;
 299         }
 300 
 301         void connect() throws IOException {
 302             System.out.println("Client: connect to server");
 303             try (
 304                     BufferedInputStream bis = new BufferedInputStream(
 305                             socket.getInputStream());
 306                     BufferedOutputStream bos = new BufferedOutputStream(
 307                             socket.getOutputStream())) {
 308                 bos.write('x');
 309                 bos.flush();
 310 
 311                 int read = bis.read();
 312                 if (read < 0) {
 313                     throw new IOException("Client: couldn't read a response");
 314                 }
 315                 socket.getSession().invalidate();
 316             }
 317         }
 318 
 319         String[] getEnabledCiperSuites() {
 320             return socket.getEnabledCipherSuites();
 321         }
 322 
 323         String getNegotiatedCipherSuite() {
 324             return socket.getSession().getCipherSuite();
 325         }
 326 
 327         @Override
 328         public void close() throws Exception {
 329             if (!socket.isClosed()) {
 330                 try {
 331                     socket.close();
 332                 } catch (IOException e) {
 333                     System.out.println("Client: close: " + e);
 334                 }
 335             }
 336         }
 337 
 338         static SSLClient init(int port)
 339                 throws NoSuchAlgorithmException, IOException {
 340             return init(port, null);
 341         }
 342 
 343         static SSLClient init(int port, String ciphersuite)
 344                 throws NoSuchAlgorithmException, IOException {
 345             SSLContext context = SSLContext.getDefault();
 346             SSLSocketFactory ssf = (SSLSocketFactory)
 347                     context.getSocketFactory();
 348             SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", port);
 349 
 350             if (ciphersuite != null) {
 351                 System.out.println("Client: enable cipher suite: "
 352                         + ciphersuite);
 353                 socket.setEnabledCipherSuites(new String[] { ciphersuite });
 354             }
 355 
 356             return new SSLClient(socket);
 357         }
 358 
 359     }
 360 
 361 
 362 }