1 /*
   2  * Copyright (c) 2011, 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 /*
  27  * @test
  28  * @bug 7113275
  29  * @summary compatibility issue with MD2 trust anchor and old X509TrustManager
  30  *
  31  *     SunJSSE does not support dynamic system properties, no way to re-use
  32  *     system properties in samevm/agentvm mode.
  33  * @run main/othervm MD2InTrustAnchor PKIX TLSv1.1
  34  * @run main/othervm MD2InTrustAnchor SunX509 TLSv1.1
  35  * @run main/othervm MD2InTrustAnchor PKIX TLSv1.2
  36  * @run main/othervm MD2InTrustAnchor SunX509 TLSv1.2
  37  */
  38 
  39 import java.net.*;
  40 import java.util.*;
  41 import java.io.*;
  42 import javax.net.ssl.*;
  43 import java.security.KeyStore;
  44 import java.security.KeyFactory;
  45 import java.security.cert.Certificate;
  46 import java.security.cert.CertificateFactory;
  47 import java.security.spec.*;
  48 import java.security.interfaces.*;
  49 import sun.misc.BASE64Decoder;
  50 
  51 
  52 public class MD2InTrustAnchor {
  53 
  54     /*
  55      * =============================================================
  56      * Set the various variables needed for the tests, then
  57      * specify what tests to run on each side.
  58      */
  59 
  60     /*
  61      * Should we run the client or server in a separate thread?
  62      * Both sides can throw exceptions, but do you have a preference
  63      * as to which side should be the main thread.
  64      */
  65     static boolean separateServerThread = false;
  66 
  67     /*
  68      * Certificates and key used in the test.
  69      */
  70 
  71     // It's a trust anchor signed with MD2 hash function.
  72     static String trustedCertStr =
  73         "-----BEGIN CERTIFICATE-----\n" +
  74         "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQIFADA7MQswCQYDVQQGEwJVUzEN\n" +
  75         "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
  76         "MTExMTE4MTExNDA0WhcNMzIxMDI4MTExNDA0WjA7MQswCQYDVQQGEwJVUzENMAsG\n" +
  77         "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" +
  78         "KoZIhvcNAQEBBQADgY0AMIGJAoGBAPGyB9tugUGgxtdeqe0qJEwf9x1Gy4BOi1yR\n" +
  79         "wzDZY4H5LquvIfQ2V3J9X1MQENVsFvkvp65ZcFcy+ObOucXUUPFcd/iw2DVb5QXA\n" +
  80         "ffyeVqWD56GPi8Qe37wrJO3L6fBhN9oxp/BbdRLgjU81zx8qLEyPODhPMxV4OkcA\n" +
  81         "SDwZTSxxAgMBAAGjgaUwgaIwHQYDVR0OBBYEFLOAtr/YrYj9H04EDLA0fd14jisF\n" +
  82         "MGMGA1UdIwRcMFqAFLOAtr/YrYj9H04EDLA0fd14jisFoT+kPTA7MQswCQYDVQQG\n" +
  83         "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
  84         "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEC\n" +
  85         "BQADgYEAr8ExpXu/FTIRiMzPm0ubqwME4lniilwQUiEOD/4DbksNjEIcUyS2hIk1\n" +
  86         "qsmjJz3SHBnwhxl9dhJVwk2tZLkPGW86Zn0TPVRsttK4inTgCC9GFGeqQBdrU/uf\n" +
  87         "lipBzXWljrfbg4N/kK8m2LabtKUMMnGysM8rN0Fx2PYm5xxGvtM=\n" +
  88         "-----END CERTIFICATE-----";
  89 
  90     // The certificate issued by above trust anchor, signed with MD5
  91     static String targetCertStr =
  92         "-----BEGIN CERTIFICATE-----\n" +
  93         "MIICeDCCAeGgAwIBAgIBAjANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
  94         "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
  95         "MTExMTE4MTExNDA2WhcNMzEwODA1MTExNDA2WjBPMQswCQYDVQQGEwJVUzENMAsG\n" +
  96         "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxEjAQBgNV\n" +
  97         "BAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwDnm96mw\n" +
  98         "fXCH4bgXk1US0VcJsQVxUtGMyncAveMuzBzNzOmKZPeqyYX1Fuh4q+cuza03WTJd\n" +
  99         "G9nOkNr364e3Rn1aaHjCMcBmFflObnGnhhufNmIGYogJ9dJPmhUVPEVAXrMG+Ces\n" +
 100         "NKy2E8woGnLMrqu6yiuTClbLBPK8fWzTXrECAwEAAaN4MHYwCwYDVR0PBAQDAgPo\n" +
 101         "MB0GA1UdDgQWBBSdRrpocLPJXyGfDmMWJrcEf29WGDAfBgNVHSMEGDAWgBSzgLa/\n" +
 102         "2K2I/R9OBAywNH3deI4rBTAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIG\n" +
 103         "CCsGAQUFBwMDMA0GCSqGSIb3DQEBBAUAA4GBAKJ71ZiCUykkJrCLYUxlFlhvUcr9\n" +
 104         "sTcOc67QdroW5f412NI15SXWDiley/JOasIiuIFPjaJBjOKoHOvTjG/snVu9wEgq\n" +
 105         "YNR8dPsO+NM8r79C6jO+Jx5fYAC7os2XxS75h3NX0ElJcbwIXGBJ6xRrsFh/BGYH\n" +
 106         "yvudOlX4BkVR0l1K\n" +
 107         "-----END CERTIFICATE-----";
 108 
 109     // Private key in the format of PKCS#8.
 110     static String targetPrivateKey =
 111         "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMA55vepsH1wh+G4\n" +
 112         "F5NVEtFXCbEFcVLRjMp3AL3jLswczczpimT3qsmF9RboeKvnLs2tN1kyXRvZzpDa\n" +
 113         "9+uHt0Z9Wmh4wjHAZhX5Tm5xp4YbnzZiBmKICfXST5oVFTxFQF6zBvgnrDSsthPM\n" +
 114         "KBpyzK6rusorkwpWywTyvH1s016xAgMBAAECgYEAn9bF3oRkdDoBU0i/mcww5I+K\n" +
 115         "SH9tFt+WQbiojjz9ac49trkvUfu7MO1Jui2+QbrvaSkyj+HYGFOJd1wMsPXeB7ck\n" +
 116         "5mOIYV4uZK8jfNMSQ8v0tFEeIPp5lKdw1XnrQfSe+abo2eL5Lwso437Y4s3w37+H\n" +
 117         "aY3d76hR5qly+Ys+Ww0CQQDjeOoX89d/xhRqGXKjCx8ImE/dPmsI8O27cwtKrDYJ\n" +
 118         "6t0v/xryVIdvOYcRBvKnqEogOH7T1kI+LnWKUTJ2ehJ7AkEA2FVloPVqCehXcc7e\n" +
 119         "z3TDpU9w1B0JXklcV5HddYsRqp9RukN/VK4szKE7F1yoarIUtfE9Lr9082Jwyp3M\n" +
 120         "L11xwwJBAKsZ+Hur3x0tUY29No2Nf/pnFyvEF57SGwA0uPmiL8Ol9lpz+UDudDEl\n" +
 121         "hIM6Rqv12kwCMuQE9i7vo1o3WU3k5KECQEqhg1L49yD935TqiiFFpe0Ur9btQXse\n" +
 122         "kdXAA4d2d5zGI7q/aGD9SYU6phkUJSHR16VA2RuUfzMrpb+wmm1IrmMCQFtLoKRT\n" +
 123         "A5kokFb+E3Gplu29tJvCUpfwgBFRS+wmkvtiaU/tiyDcVgDO+An5DwedxxdVzqiE\n" +
 124         "njWHoKY3axDQ8OU=\n";
 125 
 126 
 127     static char passphrase[] = "passphrase".toCharArray();
 128 
 129     /*
 130      * Is the server ready to serve?
 131      */
 132     volatile static boolean serverReady = false;
 133 
 134     /*
 135      * Turn on SSL debugging?
 136      */
 137     static boolean debug = false;
 138 
 139     /*
 140      * Define the server side of the test.
 141      *
 142      * If the server prematurely exits, serverReady will be set to true
 143      * to avoid infinite hangs.
 144      */
 145     void doServerSide() throws Exception {
 146         SSLContext context = generateSSLContext(trustedCertStr, targetCertStr,
 147                                             targetPrivateKey);
 148         SSLServerSocketFactory sslssf = context.getServerSocketFactory();
 149         SSLServerSocket sslServerSocket =
 150             (SSLServerSocket)sslssf.createServerSocket(serverPort);
 151         sslServerSocket.setNeedClientAuth(true);
 152         serverPort = sslServerSocket.getLocalPort();
 153 
 154         /*
 155          * Signal Client, we're ready for his connect.
 156          */
 157         serverReady = true;
 158 
 159         SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept();
 160         InputStream sslIS = sslSocket.getInputStream();
 161         OutputStream sslOS = sslSocket.getOutputStream();
 162 
 163         sslIS.read();
 164         sslOS.write('A');
 165         sslOS.flush();
 166 
 167         sslSocket.close();
 168     }
 169 
 170     /*
 171      * Define the client side of the test.
 172      *
 173      * If the server prematurely exits, serverReady will be set to true
 174      * to avoid infinite hangs.
 175      */
 176     void doClientSide() throws Exception {
 177 
 178         /*
 179          * Wait for server to get started.
 180          */
 181         while (!serverReady) {
 182             Thread.sleep(50);
 183         }
 184 
 185         SSLContext context = generateSSLContext(trustedCertStr, targetCertStr,
 186                                             targetPrivateKey);
 187         SSLSocketFactory sslsf = context.getSocketFactory();
 188 
 189         SSLSocket sslSocket =
 190             (SSLSocket)sslsf.createSocket("localhost", serverPort);
 191 
 192         // enable the specified TLS protocol
 193         sslSocket.setEnabledProtocols(new String[] {tlsProtocol});
 194 
 195         InputStream sslIS = sslSocket.getInputStream();
 196         OutputStream sslOS = sslSocket.getOutputStream();
 197 
 198         sslOS.write('B');
 199         sslOS.flush();
 200         sslIS.read();
 201 
 202         sslSocket.close();
 203     }
 204 
 205     /*
 206      * =============================================================
 207      * The remainder is just support stuff
 208      */
 209     private static String tmAlgorithm;        // trust manager
 210     private static String tlsProtocol;        // trust manager
 211 
 212     private static void parseArguments(String[] args) {
 213         tmAlgorithm = args[0];
 214         tlsProtocol = args[1];
 215     }
 216 
 217     private static SSLContext generateSSLContext(String trustedCertStr,
 218             String keyCertStr, String keySpecStr) throws Exception {
 219 
 220         // generate certificate from cert string
 221         CertificateFactory cf = CertificateFactory.getInstance("X.509");
 222 
 223         // create a key store
 224         KeyStore ks = KeyStore.getInstance("JKS");
 225         ks.load(null, null);
 226 
 227         // import the trused cert
 228         Certificate trusedCert = null;
 229         ByteArrayInputStream is = null;
 230         if (trustedCertStr != null) {
 231             is = new ByteArrayInputStream(trustedCertStr.getBytes());
 232             trusedCert = cf.generateCertificate(is);
 233             is.close();
 234 
 235             ks.setCertificateEntry("RSA Export Signer", trusedCert);
 236         }
 237 
 238         if (keyCertStr != null) {
 239             // generate the private key.
 240             PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
 241                                 new BASE64Decoder().decodeBuffer(keySpecStr));
 242             KeyFactory kf = KeyFactory.getInstance("RSA");
 243             RSAPrivateKey priKey =
 244                     (RSAPrivateKey)kf.generatePrivate(priKeySpec);
 245 
 246             // generate certificate chain
 247             is = new ByteArrayInputStream(keyCertStr.getBytes());
 248             Certificate keyCert = cf.generateCertificate(is);
 249             is.close();
 250 
 251             // It's not allowed to send MD2 signed certificate to peer,
 252             // even it may be a trusted certificate. Then we will not
 253             // place the trusted certficate in the chain.
 254             Certificate[] chain = new Certificate[1];
 255             chain[0] = keyCert;
 256 
 257             // import the key entry.
 258             ks.setKeyEntry("Whatever", priKey, passphrase, chain);
 259         }
 260 
 261         // create SSL context
 262         TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
 263         tmf.init(ks);
 264 
 265         SSLContext ctx = SSLContext.getInstance(tlsProtocol);
 266         if (keyCertStr != null && !keyCertStr.isEmpty()) {
 267             KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
 268             kmf.init(ks, passphrase);
 269 
 270             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 271             ks = null;
 272         } else {
 273             ctx.init(null, tmf.getTrustManagers(), null);
 274         }
 275 
 276         return ctx;
 277     }
 278 
 279 
 280     // use any free port by default
 281     volatile int serverPort = 0;
 282 
 283     volatile Exception serverException = null;
 284     volatile Exception clientException = null;
 285 
 286     public static void main(String[] args) throws Exception {
 287         if (debug)
 288             System.setProperty("javax.net.debug", "all");
 289 
 290         /*
 291          * Get the customized arguments.
 292          */
 293         parseArguments(args);
 294 
 295         /*
 296          * Start the tests.
 297          */
 298         new MD2InTrustAnchor();
 299     }
 300 
 301     Thread clientThread = null;
 302     Thread serverThread = null;
 303 
 304     /*
 305      * Primary constructor, used to drive remainder of the test.
 306      *
 307      * Fork off the other side, then do your work.
 308      */
 309     MD2InTrustAnchor() throws Exception {
 310         try {
 311             if (separateServerThread) {
 312                 startServer(true);
 313                 startClient(false);
 314             } else {
 315                 startClient(true);
 316                 startServer(false);
 317             }
 318         } catch (Exception e) {
 319             // swallow for now.  Show later
 320         }
 321 
 322         /*
 323          * Wait for other side to close down.
 324          */
 325         if (separateServerThread) {
 326             serverThread.join();
 327         } else {
 328             clientThread.join();
 329         }
 330 
 331         /*
 332          * When we get here, the test is pretty much over.
 333          * Which side threw the error?
 334          */
 335         Exception local;
 336         Exception remote;
 337         String whichRemote;
 338 
 339         if (separateServerThread) {
 340             remote = serverException;
 341             local = clientException;
 342             whichRemote = "server";
 343         } else {
 344             remote = clientException;
 345             local = serverException;
 346             whichRemote = "client";
 347         }
 348 
 349         /*
 350          * If both failed, return the curthread's exception, but also
 351          * print the remote side Exception
 352          */
 353         if ((local != null) && (remote != null)) {
 354             System.out.println(whichRemote + " also threw:");
 355             remote.printStackTrace();
 356             System.out.println();
 357             throw local;
 358         }
 359 
 360         if (remote != null) {
 361             throw remote;
 362         }
 363 
 364         if (local != null) {
 365             throw local;
 366         }
 367     }
 368 
 369     void startServer(boolean newThread) throws Exception {
 370         if (newThread) {
 371             serverThread = new Thread() {
 372                 public void run() {
 373                     try {
 374                         doServerSide();
 375                     } catch (Exception e) {
 376                         /*
 377                          * Our server thread just died.
 378                          *
 379                          * Release the client, if not active already...
 380                          */
 381                         System.err.println("Server died...");
 382                         serverReady = true;
 383                         serverException = e;
 384                     }
 385                 }
 386             };
 387             serverThread.start();
 388         } else {
 389             try {
 390                 doServerSide();
 391             } catch (Exception e) {
 392                 serverException = e;
 393             } finally {
 394                 serverReady = true;
 395             }
 396         }
 397     }
 398 
 399     void startClient(boolean newThread) throws Exception {
 400         if (newThread) {
 401             clientThread = new Thread() {
 402                 public void run() {
 403                     try {
 404                         doClientSide();
 405                     } catch (Exception e) {
 406                         /*
 407                          * Our client thread just died.
 408                          */
 409                         System.err.println("Client died...");
 410                         clientException = e;
 411                     }
 412                 }
 413             };
 414             clientThread.start();
 415         } else {
 416             try {
 417                 doClientSide();
 418             } catch (Exception e) {
 419                 clientException = e;
 420             }
 421         }
 422     }
 423 }