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