1 /*
   2  * Copyright (c) 2016, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 // SunJSSE does not support dynamic system properties, no way to re-use
  27 // system properties in samevm/agentvm mode.
  28 
  29 /*
  30  * @test
  31  * @bug 8139565
  32  * @summary Restrict certificates with DSA keys less than 1024 bits
  33  *
  34  * @run main/othervm DisabledShortDSAKeys PKIX TLSv1.2
  35  * @run main/othervm DisabledShortDSAKeys SunX509 TLSv1.2
  36  * @run main/othervm DisabledShortDSAKeys PKIX TLSv1.1
  37  * @run main/othervm DisabledShortDSAKeys SunX509 TLSv1.1
  38  * @run main/othervm DisabledShortDSAKeys PKIX TLSv1
  39  * @run main/othervm DisabledShortDSAKeys SunX509 TLSv1
  40  * @run main/othervm DisabledShortDSAKeys PKIX SSLv3
  41  * @run main/othervm DisabledShortDSAKeys SunX509 SSLv3
  42  */
  43 
  44 import java.net.*;
  45 import java.util.*;
  46 import java.io.*;
  47 import javax.net.ssl.*;
  48 import java.security.Security;
  49 import java.security.KeyStore;
  50 import java.security.KeyFactory;
  51 import java.security.cert.Certificate;
  52 import java.security.cert.CertificateFactory;
  53 import java.security.spec.*;
  54 import java.security.interfaces.*;
  55 import java.util.Base64;
  56 
  57 
  58 public class DisabledShortDSAKeys {
  59 
  60     /*
  61      * =============================================================
  62      * Set the various variables needed for the tests, then
  63      * specify what tests to run on each side.
  64      */
  65 
  66     /*
  67      * Should we run the client or server in a separate thread?
  68      * Both sides can throw exceptions, but do you have a preference
  69      * as to which side should be the main thread.
  70      */
  71     static boolean separateServerThread = true;
  72 
  73     /*
  74      * Where do we find the keystores?
  75      */
  76     // Certificates and key used in the test.
  77     static String trustedCertStr =
  78         "-----BEGIN CERTIFICATE-----\n" +
  79         "MIIDDjCCAs2gAwIBAgIJAO5/hbm1ByJOMAkGByqGSM44BAMwHzELMAkGA1UEBhMC\n" +
  80         "VVMxEDAOBgNVBAoTB0V4YW1wbGUwHhcNMTYwMjE2MDQzNTQ2WhcNMzcwMTI2MDQz\n" +
  81         "NTQ2WjAfMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXhhbXBsZTCCAbgwggEsBgcq\n" +
  82         "hkjOOAQBMIIBHwKBgQC4aSK8nBYdWJtuBkz6yoDyjZnNuGFSpDmx1ggKpLpcnPuw\n" +
  83         "YKAbUhqdYhZtaIqQ4aO0T1ZS/HuOM0zvddnMUidFNX3RUvDkvdD/JYOnjqzCm+xW\n" +
  84         "U0NFuPHZdapQY5KFk3ugkqZpHLY1StZbu0qugZOZjbBOMwB7cHAbMDuVpEr8DQIV\n" +
  85         "AOi+ig+h3okFbWEE9MztiI2+DqNrAoGBAKh2EZbuWU9NoHglhVzfDUoz8CeyW6W6\n" +
  86         "rUZuIOQsjWaYOeRPWX0UVAGq9ykIOfamEpurKt4H8ge/pHaL9iazJjonMHOXG12A\n" +
  87         "0lALsMDGv22zVaJzXjOBvdPzc87opr0LIVgHASKOcDYjsICKNYPlS2cL3MJoD+bj\n" +
  88         "NAR67b90VBbEA4GFAAKBgQCGrkRp2tdj2mZF7Qz0tO6p3xSysbEfN6QZxOJYPTvM\n" +
  89         "yIYfLV9Yoy7XaRd/mCpJo/dqmsZMzowtyi+u+enuVpOLKiq/lyCktL+xUzZAjLT+\n" +
  90         "9dafHlS1wR3pDSa1spo9xTEi4Ff/DQDHcdGalBxSXX/UdRtSecIYAp5/fkt3QZ5v\n" +
  91         "0aOBkTCBjjAdBgNVHQ4EFgQUX4qbP5PgBx1J8BJ8qEgfoKVLSnQwTwYDVR0jBEgw\n" +
  92         "RoAUX4qbP5PgBx1J8BJ8qEgfoKVLSnShI6QhMB8xCzAJBgNVBAYTAlVTMRAwDgYD\n" +
  93         "VQQKEwdFeGFtcGxlggkA7n+FubUHIk4wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8E\n" +
  94         "BAMCAgQwCQYHKoZIzjgEAwMwADAtAhUAkr5bINXyy/McAx6qwhb6r0/QJUgCFFUP\n" +
  95         "CZokA4/NqJIgq8ThpTQAE8SB\n" +
  96         "-----END CERTIFICATE-----";
  97 
  98     static String targetCertStr =
  99         "-----BEGIN CERTIFICATE-----\n" +
 100         "MIICUjCCAhGgAwIBAgIJAIiDrs/4W8rtMAkGByqGSM44BAMwHzELMAkGA1UEBhMC\n" +
 101         "VVMxEDAOBgNVBAoTB0V4YW1wbGUwHhcNMTYwMjE2MDQzNTQ2WhcNMzUxMTAzMDQz\n" +
 102         "NTQ2WjA5MQswCQYDVQQGEwJVUzEQMA4GA1UECgwHRXhhbXBsZTEYMBYGA1UEAwwP\n" +
 103         "d3d3LmV4YW1wbGUuY29tMIHwMIGoBgcqhkjOOAQBMIGcAkEAs6A0p3TysTtVXGSv\n" +
 104         "ThR/8GHpbL49KyWRJBMIlmLc5jl/wxJgnL1t07p4YTOEa6ecyTFos04Z8n2GARmp\n" +
 105         "zYlUywIVAJLDcf4JXhZbguRFSQdWwWhZkh+LAkBLCzh3Xvpmc/5CDqU+QHqDcuSk\n" +
 106         "5B8+ZHaHRi2KQ00ejilpF2qZpW5JdHe4m3Pggh0MIuaAGX+leM4JKlnObj14A0MA\n" +
 107         "AkAYb+DYlFgStFhF1ip7rFzY8K6i/3ellkXI2umI/XVwxUQTHSlk5nFOep5Dfzm9\n" +
 108         "pADJwuSe1qGHsHB5LpMZPVpto4GEMIGBMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgPo\n" +
 109         "MB0GA1UdDgQWBBT8nsFyccF4q1dtpWE1dkNK5UiXtTAfBgNVHSMEGDAWgBRfips/\n" +
 110         "k+AHHUnwEnyoSB+gpUtKdDAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIG\n" +
 111         "CCsGAQUFBwMDMAkGByqGSM44BAMDMAAwLQIUIcIlxpIwaZXdpMC+U076unR1Mp8C\n" +
 112         "FQCD/NE8O0xwq57nwFfp7tUvUHYMMA==\n" +
 113         "-----END CERTIFICATE-----";
 114 
 115     // Private key in the format of PKCS#8, key size is 512 bits.
 116     static String targetPrivateKey =
 117         "MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEAs6A0p3TysTtVXGSvThR/8GHpbL49KyWR\n" +
 118         "JBMIlmLc5jl/wxJgnL1t07p4YTOEa6ecyTFos04Z8n2GARmpzYlUywIVAJLDcf4J\n" +
 119         "XhZbguRFSQdWwWhZkh+LAkBLCzh3Xvpmc/5CDqU+QHqDcuSk5B8+ZHaHRi2KQ00e\n" +
 120         "jilpF2qZpW5JdHe4m3Pggh0MIuaAGX+leM4JKlnObj14BBYCFHB2Wek2g5hpNj5y\n" +
 121         "RQfCc6CFO0dv";
 122 
 123     static char passphrase[] = "passphrase".toCharArray();
 124 
 125     /*
 126      * Is the server ready to serve?
 127      */
 128     volatile static boolean serverReady = false;
 129 
 130     /*
 131      * Turn on SSL debugging?
 132      */
 133     static boolean debug = false;
 134 
 135     /*
 136      * Define the server side of the test.
 137      *
 138      * If the server prematurely exits, serverReady will be set to true
 139      * to avoid infinite hangs.
 140      */
 141     void doServerSide() throws Exception {
 142         SSLContext context = generateSSLContext(null, targetCertStr,
 143                                             targetPrivateKey);
 144         SSLServerSocketFactory sslssf = context.getServerSocketFactory();
 145         SSLServerSocket sslServerSocket =
 146             (SSLServerSocket)sslssf.createServerSocket(serverPort);
 147         serverPort = sslServerSocket.getLocalPort();
 148 
 149         /*
 150          * Signal Client, we're ready for his connect.
 151          */
 152         serverReady = true;
 153 
 154         try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
 155             try (InputStream sslIS = sslSocket.getInputStream()) {
 156                 sslIS.read();
 157             }
 158 
 159             throw new Exception(
 160                     "DSA keys shorter than 1024 bits should be disabled");
 161         } catch (SSLHandshakeException sslhe) {
 162             // the expected exception, ignore
 163         }
 164     }
 165 
 166     /*
 167      * Define the client side of the test.
 168      *
 169      * If the server prematurely exits, serverReady will be set to true
 170      * to avoid infinite hangs.
 171      */
 172     void doClientSide() throws Exception {
 173 
 174         /*
 175          * Wait for server to get started.
 176          */
 177         while (!serverReady) {
 178             Thread.sleep(50);
 179         }
 180 
 181         SSLContext context = generateSSLContext(trustedCertStr, null, null);
 182         SSLSocketFactory sslsf = context.getSocketFactory();
 183 
 184         try (SSLSocket sslSocket =
 185             (SSLSocket)sslsf.createSocket("localhost", serverPort)) {
 186 
 187             // only enable the target protocol
 188             sslSocket.setEnabledProtocols(new String[] {enabledProtocol});
 189 
 190             // enable a block cipher
 191             sslSocket.setEnabledCipherSuites(
 192                 new String[] {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"});
 193 
 194             try (OutputStream sslOS = sslSocket.getOutputStream()) {
 195                 sslOS.write('B');
 196                 sslOS.flush();
 197             }
 198 
 199             throw new Exception(
 200                     "DSA keys shorter than 1024 bits should be disabled");
 201         } catch (SSLHandshakeException sslhe) {
 202             // the expected exception, ignore
 203         }
 204     }
 205 
 206     /*
 207      * =============================================================
 208      * The remainder is just support stuff
 209      */
 210     private static String tmAlgorithm;        // trust manager
 211     private static String enabledProtocol;    // the target protocol
 212 
 213     private static void parseArguments(String[] args) {
 214         tmAlgorithm = args[0];
 215         enabledProtocol = args[1];
 216     }
 217 
 218     private static SSLContext generateSSLContext(String trustedCertStr,
 219             String keyCertStr, String keySpecStr) throws Exception {
 220 
 221         // generate certificate from cert string
 222         CertificateFactory cf = CertificateFactory.getInstance("X.509");
 223 
 224         // create a key store
 225         KeyStore ks = KeyStore.getInstance("JKS");
 226         ks.load(null, null);
 227 
 228         // import the trused cert
 229         Certificate trusedCert = null;
 230         ByteArrayInputStream is = null;
 231         if (trustedCertStr != null) {
 232             is = new ByteArrayInputStream(trustedCertStr.getBytes());
 233             trusedCert = cf.generateCertificate(is);
 234             is.close();
 235 
 236             ks.setCertificateEntry("DSA Export Signer", trusedCert);
 237         }
 238 
 239         if (keyCertStr != null) {
 240             // generate the private key.
 241             PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
 242                                 Base64.getMimeDecoder().decode(keySpecStr));
 243             KeyFactory kf = KeyFactory.getInstance("DSA");
 244             DSAPrivateKey priKey =
 245                     (DSAPrivateKey)kf.generatePrivate(priKeySpec);
 246 
 247             // generate certificate chain
 248             is = new ByteArrayInputStream(keyCertStr.getBytes());
 249             Certificate keyCert = cf.generateCertificate(is);
 250             is.close();
 251 
 252             Certificate[] chain = null;
 253             if (trusedCert != null) {
 254                 chain = new Certificate[2];
 255                 chain[0] = keyCert;
 256                 chain[1] = trusedCert;
 257             } else {
 258                 chain = new Certificate[1];
 259                 chain[0] = keyCert;
 260             }
 261 
 262             // import the key entry.
 263             ks.setKeyEntry("Whatever", priKey, passphrase, chain);
 264         }
 265 
 266         // create SSL context
 267         TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
 268         tmf.init(ks);
 269 
 270         SSLContext ctx = SSLContext.getInstance("TLS");
 271         if (keyCertStr != null && !keyCertStr.isEmpty()) {
 272             KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
 273             kmf.init(ks, passphrase);
 274 
 275             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 276             ks = null;
 277         } else {
 278             ctx.init(null, tmf.getTrustManagers(), null);
 279         }
 280 
 281         return ctx;
 282     }
 283 
 284 
 285     // use any free port by default
 286     volatile int serverPort = 0;
 287 
 288     volatile Exception serverException = null;
 289     volatile Exception clientException = null;
 290 
 291     public static void main(String[] args) throws Exception {
 292         if (debug)
 293             System.setProperty("javax.net.debug", "all");
 294 
 295         /*
 296          * Get the customized arguments.
 297          */
 298         parseArguments(args);
 299 
 300         /*
 301          * Start the tests.
 302          */
 303         new DisabledShortDSAKeys();
 304     }
 305 
 306     Thread clientThread = null;
 307     Thread serverThread = null;
 308 
 309     /*
 310      * Primary constructor, used to drive remainder of the test.
 311      *
 312      * Fork off the other side, then do your work.
 313      */
 314     DisabledShortDSAKeys() throws Exception {
 315         Exception startException = null;
 316         try {
 317             if (separateServerThread) {
 318                 startServer(true);
 319                 startClient(false);
 320             } else {
 321                 startClient(true);
 322                 startServer(false);
 323             }
 324         } catch (Exception e) {
 325             startException = e;
 326         }
 327 
 328         /*
 329          * Wait for other side to close down.
 330          */
 331         if (separateServerThread) {
 332             if (serverThread != null) {
 333                 serverThread.join();
 334             }
 335         } else {
 336             if (clientThread != null) {
 337                 clientThread.join();
 338             }
 339         }
 340 
 341         /*
 342          * When we get here, the test is pretty much over.
 343          * Which side threw the error?
 344          */
 345         Exception local;
 346         Exception remote;
 347 
 348         if (separateServerThread) {
 349             remote = serverException;
 350             local = clientException;
 351         } else {
 352             remote = clientException;
 353             local = serverException;
 354         }
 355 
 356         Exception exception = null;
 357 
 358         /*
 359          * Check various exception conditions.
 360          */
 361         if ((local != null) && (remote != null)) {
 362             // If both failed, return the curthread's exception.
 363             local.initCause(remote);
 364             exception = local;
 365         } else if (local != null) {
 366             exception = local;
 367         } else if (remote != null) {
 368             exception = remote;
 369         } else if (startException != null) {
 370             exception = startException;
 371         }
 372 
 373         /*
 374          * If there was an exception *AND* a startException,
 375          * output it.
 376          */
 377         if (exception != null) {
 378             if (exception != startException && startException != null) {
 379                 exception.addSuppressed(startException);
 380             }
 381             throw exception;
 382         }
 383 
 384         // Fall-through: no exception to throw!
 385     }
 386 
 387     void startServer(boolean newThread) throws Exception {
 388         if (newThread) {
 389             serverThread = new Thread() {
 390                 public void run() {
 391                     try {
 392                         doServerSide();
 393                     } catch (Exception e) {
 394                         /*
 395                          * Our server thread just died.
 396                          *
 397                          * Release the client, if not active already...
 398                          */
 399                         System.err.println("Server died...");
 400                         serverReady = true;
 401                         serverException = e;
 402                     }
 403                 }
 404             };
 405             serverThread.start();
 406         } else {
 407             try {
 408                 doServerSide();
 409             } catch (Exception e) {
 410                 serverException = e;
 411             } finally {
 412                 serverReady = true;
 413             }
 414         }
 415     }
 416 
 417     void startClient(boolean newThread) throws Exception {
 418         if (newThread) {
 419             clientThread = new Thread() {
 420                 public void run() {
 421                     try {
 422                         doClientSide();
 423                     } catch (Exception e) {
 424                         /*
 425                          * Our client thread just died.
 426                          */
 427                         System.err.println("Client died...");
 428                         clientException = e;
 429                     }
 430                 }
 431             };
 432             clientThread.start();
 433         } else {
 434             try {
 435                 doClientSide();
 436             } catch (Exception e) {
 437                 clientException = e;
 438             }
 439         }
 440     }
 441 }