/* * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ // SunJSSE does not support dynamic system properties, no way to re-use // system properties in samevm/agentvm mode. /* * @test * @bug 8046321 8153829 * @summary OCSP Stapling for TLS * @library ../../../../java/security/testlibrary * @build CertificateBuilder SimpleOCSPServer * @run main/othervm SSLSocketWithStapling */ import java.io.*; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import javax.net.ssl.*; import java.security.KeyStore; import java.security.PublicKey; import java.security.Security; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.Certificate; import java.security.cert.PKIXBuilderParameters; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.security.cert.PKIXRevocationChecker; import java.security.cert.PKIXRevocationChecker.Option; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.concurrent.TimeUnit; import sun.security.testlibrary.SimpleOCSPServer; import sun.security.testlibrary.CertificateBuilder; public class SSLSocketWithStapling { /* * ============================================================= * Set the various variables needed for the tests, then * specify what tests to run on each side. */ // Turn on TLS debugging static boolean debug = false; /* * Should we run the client or server in a separate thread? * Both sides can throw exceptions, but do you have a preference * as to which side should be the main thread. */ static boolean separateServerThread = true; Thread clientThread = null; Thread serverThread = null; static String passwd = "passphrase"; static String ROOT_ALIAS = "root"; static String INT_ALIAS = "intermediate"; static String SSL_ALIAS = "ssl"; /* * Is the server ready to serve? */ volatile static boolean serverReady = false; volatile int serverPort = 0; volatile Exception serverException = null; volatile Exception clientException = null; // PKI components we will need for this test static KeyStore rootKeystore; // Root CA Keystore static KeyStore intKeystore; // Intermediate CA Keystore static KeyStore serverKeystore; // SSL Server Keystore static KeyStore trustStore; // SSL Client trust store static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder static int rootOcspPort; // Port number for root OCSP static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder static int intOcspPort; // Port number for intermed. OCSP /* * If the client or server is doing some kind of object creation * that the other side depends on, and that thread prematurely * exits, you may experience a hang. The test harness will * terminate all hung threads after its timeout has expired, * currently 3 minutes by default, but you might try to be * smart about it.... */ public static void main(String[] args) throws Exception { if (debug) { System.setProperty("javax.net.debug", "ssl"); } try { // Create the PKI we will use for the test and start the OCSP servers createPKI(); testAllDefault(); testPKIXParametersRevEnabled(); testRevokedCertificate(); testHardFailFallback(); testSoftFailFallback(); testLatencyNoStaple(false); testLatencyNoStaple(true); } finally { // shut down the OCSP responders before finishing the test intOcsp.stop(); rootOcsp.stop(); } } /** * Default test using no externally-configured PKIXBuilderParameters */ static void testAllDefault() throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); serverReady = false; Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = new HashMap<>(); // We will prove revocation checking is disabled by marking the SSL // certificate as revoked. The test would only pass if revocation // checking did not happen. X509Certificate sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS); Date fiveMinsAgo = new Date(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5)); revInfo.put(sslCert.getSerialNumber(), new SimpleOCSPServer.CertStatusInfo( SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED, fiveMinsAgo)); intOcsp.updateStatusDb(revInfo); System.out.println("======================================="); System.out.println("Stapling enabled, default configuration"); System.out.println("======================================="); SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, servParams); TestResult tr = sslTest.getResult(); if (tr.clientExc != null) { throw tr.clientExc; } else if (tr.serverExc != null) { throw tr.serverExc; } // Return the ssl certificate to non-revoked status revInfo.put(sslCert.getSerialNumber(), new SimpleOCSPServer.CertStatusInfo( SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); intOcsp.updateStatusDb(revInfo); System.out.println(" PASS"); System.out.println("=======================================\n"); } /** * Do a basic connection using PKIXParameters with revocation checking * enabled and client-side OCSP disabled. It will only pass if all * stapled responses are present, valid and have a GOOD status. */ static void testPKIXParametersRevEnabled() throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); serverReady = false; System.out.println("====================================="); System.out.println("Stapling enabled, PKIXParameters with"); System.out.println("Revocation checking enabled "); System.out.println("====================================="); cliParams.pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); cliParams.pkixParams.setRevocationEnabled(true); Security.setProperty("ocsp.enable", "false"); SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, servParams); TestResult tr = sslTest.getResult(); if (tr.clientExc != null) { throw tr.clientExc; } else if (tr.serverExc != null) { throw tr.serverExc; } System.out.println(" PASS"); System.out.println("=====================================\n"); } /** * Perform a test where the certificate is revoked and placed in the * TLS handshake. Client-side OCSP is disabled, so this test will only * pass if the OCSP response is found, since we will check the * CertPathValidatorException reason for revoked status. */ static void testRevokedCertificate() throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); serverReady = false; Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = new HashMap<>(); // We will prove revocation checking is disabled by marking the SSL // certificate as revoked. The test would only pass if revocation // checking did not happen. X509Certificate sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS); Date fiveMinsAgo = new Date(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5)); revInfo.put(sslCert.getSerialNumber(), new SimpleOCSPServer.CertStatusInfo( SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED, fiveMinsAgo)); intOcsp.updateStatusDb(revInfo); System.out.println("======================================="); System.out.println("Stapling enabled, default configuration"); System.out.println("======================================="); cliParams.pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); cliParams.pkixParams.setRevocationEnabled(true); Security.setProperty("ocsp.enable", "false"); SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, servParams); TestResult tr = sslTest.getResult(); if (!checkClientValidationFailure(tr.clientExc, BasicReason.REVOKED)) { if (tr.clientExc != null) { throw tr.clientExc; } else { throw new RuntimeException( "Expected client failure, but the client succeeded"); } } // Return the ssl certificate to non-revoked status revInfo.put(sslCert.getSerialNumber(), new SimpleOCSPServer.CertStatusInfo( SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); intOcsp.updateStatusDb(revInfo); System.out.println(" PASS"); System.out.println("=======================================\n"); } /** * Test a case where client-side stapling is attempted, but does not * occur because OCSP responders are unreachable. This should use a * default hard-fail behavior. */ static void testHardFailFallback() throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); serverReady = false; // make OCSP responders reject connections intOcsp.rejectConnections(); rootOcsp.rejectConnections(); System.out.println("======================================="); System.out.println("Stapling enbled in client and server,"); System.out.println("but OCSP responders disabled."); System.out.println("PKIXParameters with Revocation checking"); System.out.println("enabled."); System.out.println("======================================="); Security.setProperty("ocsp.enable", "true"); cliParams.pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); cliParams.pkixParams.setRevocationEnabled(true); SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, servParams); TestResult tr = sslTest.getResult(); if (!checkClientValidationFailure(tr.clientExc, BasicReason.UNDETERMINED_REVOCATION_STATUS)) { if (tr.clientExc != null) { throw tr.clientExc; } else { throw new RuntimeException( "Expected client failure, but the client succeeded"); } } System.out.println(" PASS"); System.out.println("=======================================\n"); // Make OCSP responders accept connections intOcsp.acceptConnections(); rootOcsp.acceptConnections(); // Wait 5 seconds for server ready for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { throw new RuntimeException("Server not ready yet"); } } /** * Test a case where client-side stapling is attempted, but does not * occur because OCSP responders are unreachable. Client-side OCSP * checking is enabled for this, with SOFT_FAIL. */ static void testSoftFailFallback() throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); serverReady = false; // make OCSP responders reject connections intOcsp.rejectConnections(); rootOcsp.rejectConnections(); System.out.println("======================================="); System.out.println("Stapling enbled in client and server,"); System.out.println("but OCSP responders disabled."); System.out.println("PKIXParameters with Revocation checking"); System.out.println("enabled and SOFT_FAIL."); System.out.println("======================================="); Security.setProperty("ocsp.enable", "true"); cliParams.pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); cliParams.pkixParams.setRevocationEnabled(true); CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); cliParams.revChecker = (PKIXRevocationChecker)cpv.getRevocationChecker(); cliParams.revChecker.setOptions(EnumSet.of(Option.SOFT_FAIL)); SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, servParams); TestResult tr = sslTest.getResult(); if (tr.clientExc != null) { throw tr.clientExc; } else if (tr.serverExc != null) { throw tr.serverExc; } System.out.println(" PASS"); System.out.println("=======================================\n"); // Make OCSP responders accept connections intOcsp.acceptConnections(); rootOcsp.acceptConnections(); // Wait 5 seconds for server ready for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { throw new RuntimeException("Server not ready yet"); } } /** * This test initiates stapling from the client, but the server does not * support OCSP stapling for this connection. In this case it happens * because the latency of the OCSP responders is longer than the server * is willing to wait. To keep the test streamlined, we will set the server * latency to a 1 second wait, and set the responder latency to 3 seconds. * * @param fallback if we allow client-side OCSP fallback, which * will change the result from the client failing with CPVE (no fallback) * to a pass (fallback active). */ static void testLatencyNoStaple(Boolean fallback) throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); serverReady = false; // Give a 1 second delay before running the test. intOcsp.setDelay(3000); rootOcsp.setDelay(3000); Thread.sleep(1000); // Wait 5 seconds for server ready for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { throw new RuntimeException("Server not ready yet"); } System.out.println("========================================"); System.out.println("Stapling enbled in client. Server does"); System.out.println("not support stapling due to OCSP latency."); System.out.println("PKIXParameters with Revocation checking"); System.out.println("enabled, client-side OCSP checking is."); System.out.println(fallback ? "enabled" : "disabled"); System.out.println("========================================"); Security.setProperty("ocsp.enable", fallback.toString()); cliParams.pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); cliParams.pkixParams.setRevocationEnabled(true); servParams.respTimeout = 1000; SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, servParams); TestResult tr = sslTest.getResult(); if (fallback) { if (tr.clientExc != null) { throw tr.clientExc; } else if (tr.serverExc != null) { throw tr.serverExc; } } else { if (!checkClientValidationFailure(tr.clientExc, BasicReason.UNDETERMINED_REVOCATION_STATUS)) { if (tr.clientExc != null) { throw tr.clientExc; } else { throw new RuntimeException( "Expected client failure, but the client succeeded"); } } } System.out.println(" PASS"); System.out.println("========================================\n"); // Remove the OCSP responder latency intOcsp.setDelay(0); rootOcsp.setDelay(0); Thread.sleep(1000); // Wait 5 seconds for server ready for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { throw new RuntimeException("Server not ready yet"); } } /* * Define the server side of the test. * * If the server prematurely exits, serverReady will be set to true * to avoid infinite hangs. */ void doServerSide(ServerParameters servParams) throws Exception { // Selectively enable or disable the feature System.setProperty("jdk.tls.server.enableStatusRequestExtension", Boolean.toString(servParams.enabled)); // Set all the other operating parameters System.setProperty("jdk.tls.stapling.cacheSize", Integer.toString(servParams.cacheSize)); System.setProperty("jdk.tls.stapling.cacheLifetime", Integer.toString(servParams.cacheLifetime)); System.setProperty("jdk.tls.stapling.responseTimeout", Integer.toString(servParams.respTimeout)); System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri); System.setProperty("jdk.tls.stapling.responderOverride", Boolean.toString(servParams.respOverride)); System.setProperty("jdk.tls.stapling.ignoreExtensions", Boolean.toString(servParams.ignoreExts)); // Set keystores and trust stores for the server KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(serverKeystore, passwd.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(trustStore); SSLContext sslc = SSLContext.getInstance("TLS"); sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLServerSocketFactory sslssf = sslc.getServerSocketFactory(); try (SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(serverPort)) { serverPort = sslServerSocket.getLocalPort(); /* * Signal Client, we're ready for his connect. */ serverReady = true; try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream()) { int numberIn = sslIS.read(); int numberSent = 85; log("Server received number: " + numberIn); sslOS.write(numberSent); sslOS.flush(); log("Server sent number: " + numberSent); } } } /* * Define the client side of the test. * * If the server prematurely exits, serverReady will be set to true * to avoid infinite hangs. */ void doClientSide(ClientParameters cliParams) throws Exception { // Wait 5 seconds for server ready for (int i = 0; (i < 100 && !serverReady); i++) { Thread.sleep(50); } if (!serverReady) { throw new RuntimeException("Server not ready yet"); } // Selectively enable or disable the feature System.setProperty("jdk.tls.client.enableStatusRequestExtension", Boolean.toString(cliParams.enabled)); // Create the Trust Manager Factory using the PKIX variant TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); // If we have a customized pkixParameters then use it if (cliParams.pkixParams != null) { // LIf we have a customized PKIXRevocationChecker, add // it to the PKIXBuilderParameters. if (cliParams.revChecker != null) { cliParams.pkixParams.addCertPathChecker(cliParams.revChecker); } ManagerFactoryParameters trustParams = new CertPathTrustManagerParameters(cliParams.pkixParams); tmf.init(trustParams); } else { tmf.init(trustStore); } SSLContext sslc = SSLContext.getInstance("TLS"); sslc.init(null, tmf.getTrustManagers(), null); SSLSocketFactory sslsf = sslc.getSocketFactory(); try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost", serverPort); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream()) { int numberSent = 80; sslOS.write(numberSent); sslOS.flush(); log("Client sent number: " + numberSent); int numberIn = sslIS.read(); log("Client received number:" + numberIn); } } /* * Primary constructor, used to drive remainder of the test. * * Fork off the other side, then do your work. */ SSLSocketWithStapling(ClientParameters cliParams, ServerParameters servParams) throws Exception { Exception startException = null; try { if (separateServerThread) { startServer(servParams, true); startClient(cliParams, false); } else { startClient(cliParams, true); startServer(servParams, false); } } catch (Exception e) { startException = e; } /* * Wait for other side to close down. */ if (separateServerThread) { if (serverThread != null) { serverThread.join(); } } else { if (clientThread != null) { clientThread.join(); } } } /** * Checks a validation failure to see if it failed for the reason we think * it should. This comes in as an SSLException of some sort, but it * encapsulates a ValidatorException which in turn encapsulates the * CertPathValidatorException we are interested in. * * @param e the exception thrown at the top level * @param reason the underlying CertPathValidatorException BasicReason * we are expecting it to have. * * @return true if the reason matches up, false otherwise. */ static boolean checkClientValidationFailure(Exception e, BasicReason reason) { boolean result = false; if (e instanceof SSLException) { Throwable valExc = e.getCause(); if (valExc instanceof sun.security.validator.ValidatorException) { Throwable cause = valExc.getCause(); if (cause instanceof CertPathValidatorException) { CertPathValidatorException cpve = (CertPathValidatorException)cause; if (cpve.getReason() == reason) { result = true; } } } } return result; } TestResult getResult() { TestResult tr = new TestResult(); tr.clientExc = clientException; tr.serverExc = serverException; return tr; } void startServer(ServerParameters servParams, boolean newThread) throws Exception { if (newThread) { serverThread = new Thread() { public void run() { try { doServerSide(servParams); } catch (Exception e) { /* * Our server thread just died. * * Release the client, if not active already... */ System.err.println("Server died..."); e.printStackTrace(System.err); serverReady = true; serverException = e; } } }; serverThread.start(); } else { try { doServerSide(servParams); } catch (Exception e) { serverException = e; } finally { serverReady = true; } } } void startClient(ClientParameters cliParams, boolean newThread) throws Exception { if (newThread) { clientThread = new Thread() { public void run() { try { doClientSide(cliParams); } catch (Exception e) { /* * Our client thread just died. */ System.err.println("Client died..."); clientException = e; } } }; clientThread.start(); } else { try { doClientSide(cliParams); } catch (Exception e) { clientException = e; } } } /** * Creates the PKI components necessary for this test, including * Root CA, Intermediate CA and SSL server certificates, the keystores * for each entity, a client trust store, and starts the OCSP responders. */ private static void createPKI() throws Exception { CertificateBuilder cbld = new CertificateBuilder(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyStore.Builder keyStoreBuilder = KeyStore.Builder.newInstance("PKCS12", null, new KeyStore.PasswordProtection(passwd.toCharArray())); // Generate Root, IntCA, EE keys KeyPair rootCaKP = keyGen.genKeyPair(); log("Generated Root CA KeyPair"); KeyPair intCaKP = keyGen.genKeyPair(); log("Generated Intermediate CA KeyPair"); KeyPair sslKP = keyGen.genKeyPair(); log("Generated SSL Cert KeyPair"); // Set up the Root CA Cert cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany"); cbld.setPublicKey(rootCaKP.getPublic()); cbld.setSerialNumber(new BigInteger("1")); // Make a 3 year validity starting from 60 days ago long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60); long end = start + TimeUnit.DAYS.toMillis(1085); cbld.setValidity(new Date(start), new Date(end)); addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic()); addCommonCAExts(cbld); // Make our Root CA Cert! X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(), "SHA256withRSA"); log("Root CA Created:\n" + certInfo(rootCert)); // Now build a keystore and add the keys and cert rootKeystore = keyStoreBuilder.getKeyStore(); Certificate[] rootChain = {rootCert}; rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(), passwd.toCharArray(), rootChain); // Now fire up the OCSP responder rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null); rootOcsp.enableLog(debug); rootOcsp.setNextUpdateInterval(3600); rootOcsp.start(); // Wait 5 seconds for server ready for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) { Thread.sleep(50); } if (!rootOcsp.isServerReady()) { throw new RuntimeException("Server not ready yet"); } rootOcspPort = rootOcsp.getPort(); String rootRespURI = "http://localhost:" + rootOcspPort; log("Root OCSP Responder URI is " + rootRespURI); // Now that we have the root keystore and OCSP responder we can // create our intermediate CA. cbld.reset(); cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany"); cbld.setPublicKey(intCaKP.getPublic()); cbld.setSerialNumber(new BigInteger("100")); // Make a 2 year validity starting from 30 days ago start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30); end = start + TimeUnit.DAYS.toMillis(730); cbld.setValidity(new Date(start), new Date(end)); addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic()); addCommonCAExts(cbld); cbld.addAIAExt(Collections.singletonList(rootRespURI)); // Make our Intermediate CA Cert! X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(), "SHA256withRSA"); log("Intermediate CA Created:\n" + certInfo(intCaCert)); // Provide intermediate CA cert revocation info to the Root CA // OCSP responder. Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = new HashMap<>(); revInfo.put(intCaCert.getSerialNumber(), new SimpleOCSPServer.CertStatusInfo( SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); rootOcsp.updateStatusDb(revInfo); // Now build a keystore and add the keys, chain and root cert as a TA intKeystore = keyStoreBuilder.getKeyStore(); Certificate[] intChain = {intCaCert, rootCert}; intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(), passwd.toCharArray(), intChain); intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); // Now fire up the Intermediate CA OCSP responder intOcsp = new SimpleOCSPServer(intKeystore, passwd, INT_ALIAS, null); intOcsp.enableLog(debug); intOcsp.setNextUpdateInterval(3600); intOcsp.start(); // Wait 5 seconds for server ready for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady()) { throw new RuntimeException("Server not ready yet"); } intOcspPort = intOcsp.getPort(); String intCaRespURI = "http://localhost:" + intOcspPort; log("Intermediate CA OCSP Responder URI is " + intCaRespURI); // Last but not least, let's make our SSLCert and add it to its own // Keystore cbld.reset(); cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany"); cbld.setPublicKey(sslKP.getPublic()); cbld.setSerialNumber(new BigInteger("4096")); // Make a 1 year validity starting from 7 days ago start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7); end = start + TimeUnit.DAYS.toMillis(365); cbld.setValidity(new Date(start), new Date(end)); // Add extensions addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic()); boolean[] kuBits = {true, false, true, false, false, false, false, false, false}; cbld.addKeyUsageExt(kuBits); List<String> ekuOids = new ArrayList<>(); ekuOids.add("1.3.6.1.5.5.7.3.1"); ekuOids.add("1.3.6.1.5.5.7.3.2"); cbld.addExtendedKeyUsageExt(ekuOids); cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost")); cbld.addAIAExt(Collections.singletonList(intCaRespURI)); // Make our SSL Server Cert! X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(), "SHA256withRSA"); log("SSL Certificate Created:\n" + certInfo(sslCert)); // Provide SSL server cert revocation info to the Intermeidate CA // OCSP responder. revInfo = new HashMap<>(); revInfo.put(sslCert.getSerialNumber(), new SimpleOCSPServer.CertStatusInfo( SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); intOcsp.updateStatusDb(revInfo); // Now build a keystore and add the keys, chain and root cert as a TA serverKeystore = keyStoreBuilder.getKeyStore(); Certificate[] sslChain = {sslCert, intCaCert, rootCert}; serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(), passwd.toCharArray(), sslChain); serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); // And finally a Trust Store for the client trustStore = keyStoreBuilder.getKeyStore(); trustStore.setCertificateEntry(ROOT_ALIAS, rootCert); } private static void addCommonExts(CertificateBuilder cbld, PublicKey subjKey, PublicKey authKey) throws IOException { cbld.addSubjectKeyIdExt(subjKey); cbld.addAuthorityKeyIdExt(authKey); } private static void addCommonCAExts(CertificateBuilder cbld) throws IOException { cbld.addBasicConstraintsExt(true, true, -1); // Set key usage bits for digitalSignature, keyCertSign and cRLSign boolean[] kuBitSettings = {true, false, false, false, false, true, true, false, false}; cbld.addKeyUsageExt(kuBitSettings); } /** * Helper routine that dumps only a few cert fields rather than * the whole toString() output. * * @param cert an X509Certificate to be displayed * * @return the String output of the issuer, subject and * serial number */ private static String certInfo(X509Certificate cert) { StringBuilder sb = new StringBuilder(); sb.append("Issuer: ").append(cert.getIssuerX500Principal()). append("\n"); sb.append("Subject: ").append(cert.getSubjectX500Principal()). append("\n"); sb.append("Serial: ").append(cert.getSerialNumber()).append("\n"); return sb.toString(); } /** * Log a message on stdout * * @param message The message to log */ private static void log(String message) { if (debug) { System.out.println(message); } } // The following two classes are Simple nested class to group a handful // of configuration parameters used before starting a client or server. // We'll just access the data members directly for convenience. static class ClientParameters { boolean enabled = true; PKIXBuilderParameters pkixParams = null; PKIXRevocationChecker revChecker = null; ClientParameters() { } } static class ServerParameters { boolean enabled = true; int cacheSize = 256; int cacheLifetime = 3600; int respTimeout = 5000; String respUri = ""; boolean respOverride = false; boolean ignoreExts = false; ServerParameters() { } } static class TestResult { Exception serverExc = null; Exception clientExc = null; } }