jdk/test/sun/security/ssl/StatusStapling/StatusResponseManagerTests.java
changeset 36511 9d0388c6b336
parent 36510 043f1af70518
child 36512 1b1dea65af3e
equal deleted inserted replaced
36510:043f1af70518 36511:9d0388c6b336
     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 package sun.security.ssl;
       
    25 
       
    26 /*
       
    27  * @test
       
    28  * @bug 8046321
       
    29  * @summary OCSP Stapling for TLS (StatusResponseManager tests)
       
    30  * @library ../../../../java/security/testlibrary
       
    31  * @build CertificateBuilder SimpleOCSPServer
       
    32  * @build StatusResponseManagerTests TestCase TestUtils
       
    33  * @run main/othervm -Djavax.net.debug=ssl:respmgr
       
    34  *      sun.security.ssl.StatusResponseManagerTests
       
    35  */
       
    36 
       
    37 import java.io.IOException;
       
    38 import java.math.BigInteger;
       
    39 import java.security.cert.*;
       
    40 import java.util.*;
       
    41 import java.security.KeyPair;
       
    42 import java.security.KeyPairGenerator;
       
    43 import java.security.KeyStore;
       
    44 import java.security.PublicKey;
       
    45 import java.util.concurrent.TimeUnit;
       
    46 
       
    47 import sun.security.testlibrary.SimpleOCSPServer;
       
    48 import sun.security.testlibrary.CertificateBuilder;
       
    49 
       
    50 /*
       
    51  * Checks that the hash value for a certificate's issuer name is generated
       
    52  * correctly. Requires any certificate that is not self-signed.
       
    53  *
       
    54  * NOTE: this test uses Sun private classes which are subject to change.
       
    55  */
       
    56 public class StatusResponseManagerTests {
       
    57 
       
    58     private static final boolean debug = true;
       
    59     private static final boolean ocspDebug = false;
       
    60 
       
    61     // PKI components we will need for this test
       
    62     static String passwd = "passphrase";
       
    63     static String ROOT_ALIAS = "root";
       
    64     static String INT_ALIAS = "intermediate";
       
    65     static String SSL_ALIAS = "ssl";
       
    66     static KeyStore rootKeystore;           // Root CA Keystore
       
    67     static KeyStore intKeystore;            // Intermediate CA Keystore
       
    68     static KeyStore serverKeystore;         // SSL Server Keystore
       
    69     static KeyStore trustStore;             // SSL Client trust store
       
    70     static X509Certificate rootCert;
       
    71     static X509Certificate intCert;
       
    72     static X509Certificate sslCert;
       
    73     static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
       
    74     static int rootOcspPort;                // Port number for root OCSP
       
    75     static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
       
    76     static int intOcspPort;                 // Port number for intermed. OCSP
       
    77 
       
    78     static X509Certificate[] chain;
       
    79 
       
    80     public static void main(String[] args) throws Exception {
       
    81         Map<String, TestCase> testList =
       
    82                 new LinkedHashMap<String, TestCase>() {{
       
    83             put("Basic OCSP fetch test", testOcspFetch);
       
    84             put("Clear StatusResponseManager cache", testClearSRM);
       
    85             put("Basic OCSP_MULTI fetch test", testOcspMultiFetch);
       
    86             put("Test Cache Expiration", testCacheExpiry);
       
    87         }};
       
    88 
       
    89         // Create the CAs and OCSP responders
       
    90         createPKI();
       
    91 
       
    92         // Grab the certificates and make a chain we can reuse for tests
       
    93         sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
       
    94         intCert = (X509Certificate)intKeystore.getCertificate(INT_ALIAS);
       
    95         rootCert = (X509Certificate)rootKeystore.getCertificate(ROOT_ALIAS);
       
    96         chain = new X509Certificate[3];
       
    97         chain[0] = sslCert;
       
    98         chain[1] = intCert;
       
    99         chain[2] = rootCert;
       
   100 
       
   101         TestUtils.runTests(testList);
       
   102 
       
   103         intOcsp.stop();
       
   104         rootOcsp.stop();
       
   105     }
       
   106 
       
   107     // Test a simple RFC 6066 server-side fetch
       
   108     public static final TestCase testOcspFetch = new TestCase() {
       
   109         @Override
       
   110         public Map.Entry<Boolean, String> runTest() {
       
   111             StatusResponseManager srm = new StatusResponseManager();
       
   112             Boolean pass = Boolean.FALSE;
       
   113             String message = null;
       
   114             StatusRequest oReq = new OCSPStatusRequest();
       
   115 
       
   116             try {
       
   117                 // Get OCSP responses for non-root certs in the chain
       
   118                 Map<X509Certificate, byte[]> responseMap = srm.get(
       
   119                         StatusRequestType.OCSP, oReq, chain, 5000,
       
   120                         TimeUnit.MILLISECONDS);
       
   121 
       
   122                 // There should be one entry in the returned map and
       
   123                 // one entry in the cache when the operation is complete.
       
   124                 if (responseMap.size() != 1) {
       
   125                     message = "Incorrect number of responses: expected 1, got "
       
   126                             + responseMap.size();
       
   127                 } else if (!responseMap.containsKey(sslCert)) {
       
   128                     message = "Response map key is incorrect, expected " +
       
   129                             sslCert.getSubjectX500Principal().toString();
       
   130                 } else if (srm.size() != 1) {
       
   131                     message = "Incorrect number of cache entries: " +
       
   132                             "expected 1, got " + srm.size();
       
   133                 } else {
       
   134                     pass = Boolean.TRUE;
       
   135                 }
       
   136             } catch (Exception e) {
       
   137                 e.printStackTrace(System.out);
       
   138                 message = e.getClass().getName();
       
   139             }
       
   140 
       
   141             return new AbstractMap.SimpleEntry<>(pass, message);
       
   142         }
       
   143     };
       
   144 
       
   145     // Test clearing the StatusResponseManager cache.
       
   146     public static final TestCase testClearSRM = new TestCase() {
       
   147         @Override
       
   148         public Map.Entry<Boolean, String> runTest() {
       
   149             StatusResponseManager srm = new StatusResponseManager();
       
   150             Boolean pass = Boolean.FALSE;
       
   151             String message = null;
       
   152             StatusRequest oReq = new OCSPStatusRequest();
       
   153 
       
   154             try {
       
   155                 // Get OCSP responses for non-root certs in the chain
       
   156                 srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
       
   157                         TimeUnit.MILLISECONDS);
       
   158 
       
   159                 // There should be two entries in the returned map and
       
   160                 // two entries in the cache when the operation is complete.
       
   161                 if (srm.size() != 2) {
       
   162                     message = "Incorrect number of responses: expected 2, got "
       
   163                             + srm.size();
       
   164                 } else {
       
   165                     // Next, clear the SRM, then check the size again
       
   166                     srm.clear();
       
   167                     if (srm.size() != 0) {
       
   168                         message = "Incorrect number of responses: expected 0," +
       
   169                                 " got " + srm.size();
       
   170                     } else {
       
   171                         pass = Boolean.TRUE;
       
   172                     }
       
   173                 }
       
   174             } catch (Exception e) {
       
   175                 e.printStackTrace(System.out);
       
   176                 message = e.getClass().getName();
       
   177             }
       
   178 
       
   179             return new AbstractMap.SimpleEntry<>(pass, message);
       
   180         }
       
   181     };
       
   182 
       
   183     // Test a simple RFC 6961 server-side fetch
       
   184     public static final TestCase testOcspMultiFetch = new TestCase() {
       
   185         @Override
       
   186         public Map.Entry<Boolean, String> runTest() {
       
   187             StatusResponseManager srm = new StatusResponseManager();
       
   188             Boolean pass = Boolean.FALSE;
       
   189             String message = null;
       
   190             StatusRequest oReq = new OCSPStatusRequest();
       
   191 
       
   192             try {
       
   193                 // Get OCSP responses for non-root certs in the chain
       
   194                 Map<X509Certificate, byte[]> responseMap = srm.get(
       
   195                         StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
       
   196                         TimeUnit.MILLISECONDS);
       
   197 
       
   198                 // There should be two entries in the returned map and
       
   199                 // two entries in the cache when the operation is complete.
       
   200                 if (responseMap.size() != 2) {
       
   201                     message = "Incorrect number of responses: expected 2, got "
       
   202                             + responseMap.size();
       
   203                 } else if (!responseMap.containsKey(sslCert) ||
       
   204                         !responseMap.containsKey(intCert)) {
       
   205                     message = "Response map keys are incorrect, expected " +
       
   206                             sslCert.getSubjectX500Principal().toString() +
       
   207                             " and " +
       
   208                             intCert.getSubjectX500Principal().toString();
       
   209                 } else if (srm.size() != 2) {
       
   210                     message = "Incorrect number of cache entries: " +
       
   211                             "expected 2, got " + srm.size();
       
   212                 } else {
       
   213                     pass = Boolean.TRUE;
       
   214                 }
       
   215             } catch (Exception e) {
       
   216                 e.printStackTrace(System.out);
       
   217                 message = e.getClass().getName();
       
   218             }
       
   219 
       
   220             return new AbstractMap.SimpleEntry<>(pass, message);
       
   221         }
       
   222     };
       
   223 
       
   224     // Test cache expiration
       
   225     public static final TestCase testCacheExpiry = new TestCase() {
       
   226         @Override
       
   227         public Map.Entry<Boolean, String> runTest() {
       
   228             // For this test, we will set the cache expiry to 5 seconds
       
   229             System.setProperty("jdk.tls.stapling.cacheLifetime", "5");
       
   230             StatusResponseManager srm = new StatusResponseManager();
       
   231             Boolean pass = Boolean.FALSE;
       
   232             String message = null;
       
   233             StatusRequest oReq = new OCSPStatusRequest();
       
   234 
       
   235             try {
       
   236                 // Get OCSP responses for non-root certs in the chain
       
   237                 srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
       
   238                         TimeUnit.MILLISECONDS);
       
   239 
       
   240                 // There should be two entries in the returned map and
       
   241                 // two entries in the cache when the operation is complete.
       
   242                 if (srm.size() != 2) {
       
   243                     message = "Incorrect number of responses: expected 2, got "
       
   244                             + srm.size();
       
   245                 } else {
       
   246                     // Next, wait for more than 5 seconds so the responses
       
   247                     // in the SRM will expire.
       
   248                     Thread.sleep(7000);
       
   249                     if (srm.size() != 0) {
       
   250                         message = "Incorrect number of responses: expected 0," +
       
   251                                 " got " + srm.size();
       
   252                     } else {
       
   253                         pass = Boolean.TRUE;
       
   254                     }
       
   255                 }
       
   256             } catch (Exception e) {
       
   257                 e.printStackTrace(System.out);
       
   258                 message = e.getClass().getName();
       
   259             }
       
   260 
       
   261             // Set the cache lifetime back to the default
       
   262             System.setProperty("jdk.tls.stapling.cacheLifetime", "");
       
   263             return new AbstractMap.SimpleEntry<>(pass, message);
       
   264         }
       
   265     };
       
   266 
       
   267     /**
       
   268      * Creates the PKI components necessary for this test, including
       
   269      * Root CA, Intermediate CA and SSL server certificates, the keystores
       
   270      * for each entity, a client trust store, and starts the OCSP responders.
       
   271      */
       
   272     private static void createPKI() throws Exception {
       
   273         CertificateBuilder cbld = new CertificateBuilder();
       
   274         KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
       
   275         keyGen.initialize(2048);
       
   276         KeyStore.Builder keyStoreBuilder =
       
   277                 KeyStore.Builder.newInstance("PKCS12", null,
       
   278                         new KeyStore.PasswordProtection(passwd.toCharArray()));
       
   279 
       
   280         // Generate Root, IntCA, EE keys
       
   281         KeyPair rootCaKP = keyGen.genKeyPair();
       
   282         log("Generated Root CA KeyPair");
       
   283         KeyPair intCaKP = keyGen.genKeyPair();
       
   284         log("Generated Intermediate CA KeyPair");
       
   285         KeyPair sslKP = keyGen.genKeyPair();
       
   286         log("Generated SSL Cert KeyPair");
       
   287 
       
   288         // Set up the Root CA Cert
       
   289         cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
       
   290         cbld.setPublicKey(rootCaKP.getPublic());
       
   291         cbld.setSerialNumber(new BigInteger("1"));
       
   292         // Make a 3 year validity starting from 60 days ago
       
   293         long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
       
   294         long end = start + TimeUnit.DAYS.toMillis(1085);
       
   295         cbld.setValidity(new Date(start), new Date(end));
       
   296         addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
       
   297         addCommonCAExts(cbld);
       
   298         // Make our Root CA Cert!
       
   299         X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
       
   300                 "SHA256withRSA");
       
   301         log("Root CA Created:\n" + certInfo(rootCert));
       
   302 
       
   303         // Now build a keystore and add the keys and cert
       
   304         rootKeystore = keyStoreBuilder.getKeyStore();
       
   305         Certificate[] rootChain = {rootCert};
       
   306         rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
       
   307                 passwd.toCharArray(), rootChain);
       
   308 
       
   309         // Now fire up the OCSP responder
       
   310         rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
       
   311         rootOcsp.enableLog(ocspDebug);
       
   312         rootOcsp.setNextUpdateInterval(3600);
       
   313         rootOcsp.start();
       
   314         Thread.sleep(1000);         // Give the server a second to start up
       
   315         rootOcspPort = rootOcsp.getPort();
       
   316         String rootRespURI = "http://localhost:" + rootOcspPort;
       
   317         log("Root OCSP Responder URI is " + rootRespURI);
       
   318 
       
   319         // Now that we have the root keystore and OCSP responder we can
       
   320         // create our intermediate CA.
       
   321         cbld.reset();
       
   322         cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
       
   323         cbld.setPublicKey(intCaKP.getPublic());
       
   324         cbld.setSerialNumber(new BigInteger("100"));
       
   325         // Make a 2 year validity starting from 30 days ago
       
   326         start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
       
   327         end = start + TimeUnit.DAYS.toMillis(730);
       
   328         cbld.setValidity(new Date(start), new Date(end));
       
   329         addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
       
   330         addCommonCAExts(cbld);
       
   331         cbld.addAIAExt(Collections.singletonList(rootRespURI));
       
   332         // Make our Intermediate CA Cert!
       
   333         X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
       
   334                 "SHA256withRSA");
       
   335         log("Intermediate CA Created:\n" + certInfo(intCaCert));
       
   336 
       
   337         // Provide intermediate CA cert revocation info to the Root CA
       
   338         // OCSP responder.
       
   339         Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
       
   340             new HashMap<>();
       
   341         revInfo.put(intCaCert.getSerialNumber(),
       
   342                 new SimpleOCSPServer.CertStatusInfo(
       
   343                         SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
       
   344         rootOcsp.updateStatusDb(revInfo);
       
   345 
       
   346         // Now build a keystore and add the keys, chain and root cert as a TA
       
   347         intKeystore = keyStoreBuilder.getKeyStore();
       
   348         Certificate[] intChain = {intCaCert, rootCert};
       
   349         intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
       
   350                 passwd.toCharArray(), intChain);
       
   351         intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
       
   352 
       
   353         // Now fire up the Intermediate CA OCSP responder
       
   354         intOcsp = new SimpleOCSPServer(intKeystore, passwd,
       
   355                 INT_ALIAS, null);
       
   356         intOcsp.enableLog(ocspDebug);
       
   357         intOcsp.setNextUpdateInterval(3600);
       
   358         intOcsp.start();
       
   359         Thread.sleep(1000);
       
   360         intOcspPort = intOcsp.getPort();
       
   361         String intCaRespURI = "http://localhost:" + intOcspPort;
       
   362         log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
       
   363 
       
   364         // Last but not least, let's make our SSLCert and add it to its own
       
   365         // Keystore
       
   366         cbld.reset();
       
   367         cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
       
   368         cbld.setPublicKey(sslKP.getPublic());
       
   369         cbld.setSerialNumber(new BigInteger("4096"));
       
   370         // Make a 1 year validity starting from 7 days ago
       
   371         start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
       
   372         end = start + TimeUnit.DAYS.toMillis(365);
       
   373         cbld.setValidity(new Date(start), new Date(end));
       
   374 
       
   375         // Add extensions
       
   376         addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
       
   377         boolean[] kuBits = {true, false, true, false, false, false,
       
   378             false, false, false};
       
   379         cbld.addKeyUsageExt(kuBits);
       
   380         List<String> ekuOids = new ArrayList<>();
       
   381         ekuOids.add("1.3.6.1.5.5.7.3.1");
       
   382         ekuOids.add("1.3.6.1.5.5.7.3.2");
       
   383         cbld.addExtendedKeyUsageExt(ekuOids);
       
   384         cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
       
   385         cbld.addAIAExt(Collections.singletonList(intCaRespURI));
       
   386         // Make our SSL Server Cert!
       
   387         X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
       
   388                 "SHA256withRSA");
       
   389         log("SSL Certificate Created:\n" + certInfo(sslCert));
       
   390 
       
   391         // Provide SSL server cert revocation info to the Intermeidate CA
       
   392         // OCSP responder.
       
   393         revInfo = new HashMap<>();
       
   394         revInfo.put(sslCert.getSerialNumber(),
       
   395                 new SimpleOCSPServer.CertStatusInfo(
       
   396                         SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
       
   397         intOcsp.updateStatusDb(revInfo);
       
   398 
       
   399         // Now build a keystore and add the keys, chain and root cert as a TA
       
   400         serverKeystore = keyStoreBuilder.getKeyStore();
       
   401         Certificate[] sslChain = {sslCert, intCaCert, rootCert};
       
   402         serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
       
   403                 passwd.toCharArray(), sslChain);
       
   404         serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
       
   405 
       
   406         // And finally a Trust Store for the client
       
   407         trustStore = keyStoreBuilder.getKeyStore();
       
   408         trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
       
   409     }
       
   410 
       
   411     private static void addCommonExts(CertificateBuilder cbld,
       
   412             PublicKey subjKey, PublicKey authKey) throws IOException {
       
   413         cbld.addSubjectKeyIdExt(subjKey);
       
   414         cbld.addAuthorityKeyIdExt(authKey);
       
   415     }
       
   416 
       
   417     private static void addCommonCAExts(CertificateBuilder cbld)
       
   418             throws IOException {
       
   419         cbld.addBasicConstraintsExt(true, true, -1);
       
   420         // Set key usage bits for digitalSignature, keyCertSign and cRLSign
       
   421         boolean[] kuBitSettings = {true, false, false, false, false, true,
       
   422             true, false, false};
       
   423         cbld.addKeyUsageExt(kuBitSettings);
       
   424     }
       
   425 
       
   426     /**
       
   427      * Helper routine that dumps only a few cert fields rather than
       
   428      * the whole toString() output.
       
   429      *
       
   430      * @param cert An X509Certificate to be displayed
       
   431      *
       
   432      * @return The {@link String} output of the issuer, subject and
       
   433      * serial number
       
   434      */
       
   435     private static String certInfo(X509Certificate cert) {
       
   436         StringBuilder sb = new StringBuilder();
       
   437         sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
       
   438                 append("\n");
       
   439         sb.append("Subject: ").append(cert.getSubjectX500Principal()).
       
   440                 append("\n");
       
   441         sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
       
   442         return sb.toString();
       
   443     }
       
   444 
       
   445     /**
       
   446      * Log a message on stdout
       
   447      *
       
   448      * @param message The message to log
       
   449      */
       
   450     private static void log(String message) {
       
   451         if (debug) {
       
   452             System.out.println(message);
       
   453         }
       
   454     }
       
   455 }