--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,814 @@
+/*
+ * 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 HttpsUrlConnClient
+ */
+
+import java.io.*;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.net.Socket;
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.GeneralSecurityException;
+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.spec.PKCS8EncodedKeySpec;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+import sun.security.validator.ValidatorException;
+
+public class HttpsUrlConnClient {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ static final byte[] LINESEP = { 10 };
+ static final Base64.Encoder B64E = Base64.getMimeEncoder(64, LINESEP);
+
+ // Turn on TLS debugging
+ static boolean debug = true;
+
+ /*
+ * 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
+
+ private static final String SIMPLE_WEB_PAGE = "<HTML>\n" +
+ "<HEAD><Title>Web Page!</Title></HEAD>\n" +
+ "<BODY><H1>Web Page!</H1></BODY>\n</HTML>";
+ private static final SimpleDateFormat utcDateFmt =
+ new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
+ /*
+ * 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");
+ }
+
+ System.setProperty("javax.net.ssl.keyStore", "");
+ System.setProperty("javax.net.ssl.keyStorePassword", "");
+ System.setProperty("javax.net.ssl.trustStore", "");
+ System.setProperty("javax.net.ssl.trustStorePassword", "");
+
+ // Create the PKI we will use for the test and start the OCSP servers
+ createPKI();
+ utcDateFmt.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+ testPKIXParametersRevEnabled();
+
+ // shut down the OCSP responders before finishing the test
+ intOcsp.stop();
+ rootOcsp.stop();
+ }
+
+ /**
+ * 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("=====================================");
+
+ // Set the certificate entry in the intermediate OCSP responder
+ // with a revocation date of 8 hours ago.
+ X509Certificate sslCert =
+ (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+ Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+ new HashMap<>();
+ revInfo.put(sslCert.getSerialNumber(),
+ new SimpleOCSPServer.CertStatusInfo(
+ SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,
+ new Date(System.currentTimeMillis() -
+ TimeUnit.HOURS.toMillis(8))));
+ intOcsp.updateStatusDb(revInfo);
+
+ // Set up revocation checking on the client with no client-side
+ // OCSP fall-back
+ cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+ new X509CertSelector());
+ cliParams.pkixParams.setRevocationEnabled(true);
+ Security.setProperty("ocsp.enable", "false");
+
+ HttpsUrlConnClient sslTest = new HttpsUrlConnClient(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");
+ }
+ }
+
+ // In this case the server should also have thrown an exception
+ // because of the client alert
+ if (tr.serverExc instanceof SSLHandshakeException) {
+ if (!tr.serverExc.getMessage().contains(
+ "alert: bad_certificate_status_response")) {
+ throw tr.serverExc;
+ }
+ }
+
+ System.out.println(" PASS");
+ System.out.println("=====================================\n");
+ }
+
+ /*
+ * 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();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+ serverPort = sslServerSocket.getLocalPort();
+ log("Server Port is " + serverPort);
+
+ // Dump the private key in PKCS8 format, not encrypted. This
+ // key dump can be used if the traffic was captured using tcpdump
+ // or wireshark to look into the encrypted packets for debug purposes.
+ if (debug) {
+ byte[] keybytes = serverKeystore.getKey(SSL_ALIAS,
+ passwd.toCharArray()).getEncoded();
+ PKCS8EncodedKeySpec p8spec = new PKCS8EncodedKeySpec(keybytes);
+ StringBuilder keyPem = new StringBuilder();
+ keyPem.append("-----BEGIN PRIVATE KEY-----\n");
+ keyPem.append(B64E.encodeToString(p8spec.getEncoded())).append("\n");
+ keyPem.append("-----END PRIVATE KEY-----\n");
+ log("Private key is:\n" + keyPem.toString());
+ }
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(sslSocket.getInputStream()));
+ OutputStream out = sslSocket.getOutputStream()) {
+ StringBuilder hdrBldr = new StringBuilder();
+ String line;
+ while ((line = in.readLine()) != null && !line.isEmpty()) {
+ hdrBldr.append(line).append("\n");
+ }
+ String headerText = hdrBldr.toString();
+ log("Header Received: " + headerText.length() + " bytes\n" +
+ headerText);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("HTTP/1.0 200 OK\r\n");
+ sb.append("Date: ").append(utcDateFmt.format(new Date())).
+ append("\r\n");
+ sb.append("Content-Type: text/html\r\n");
+ sb.append("Content-Length: ").append(SIMPLE_WEB_PAGE.length());
+ sb.append("\r\n\r\n");
+ out.write(sb.toString().getBytes("UTF-8"));
+ out.write(SIMPLE_WEB_PAGE.getBytes("UTF-8"));
+ out.flush();
+ log("Server replied with:\n" + sb.toString() + SIMPLE_WEB_PAGE);
+ }
+ }
+
+ /*
+ * 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));
+
+ HtucSSLSocketFactory sockFac = new HtucSSLSocketFactory(cliParams);
+ HttpsURLConnection.setDefaultSSLSocketFactory(sockFac);
+ URL location = new URL("https://localhost:" + serverPort);
+ HttpsURLConnection tlsConn =
+ (HttpsURLConnection)location.openConnection();
+ tlsConn.setConnectTimeout(5000);
+ tlsConn.setReadTimeout(5000);
+ tlsConn.setDoInput(true);
+
+ try (InputStream in = tlsConn.getInputStream()) {
+ // Check the response
+ if (debug && tlsConn.getResponseCode() !=
+ HttpURLConnection.HTTP_OK) {
+ log("Received HTTP error: " + tlsConn.getResponseCode() +
+ " - " + tlsConn.getResponseMessage());
+ throw new IOException("HTTP error: " +
+ tlsConn.getResponseCode());
+ }
+
+ int contentLength = tlsConn.getContentLength();
+ if (contentLength == -1) {
+ contentLength = Integer.MAX_VALUE;
+ }
+ byte[] response = new byte[contentLength > 2048 ? 2048 : contentLength];
+ int total = 0;
+ while (total < contentLength) {
+ int count = in.read(response, total, response.length - total);
+ if (count < 0)
+ break;
+
+ total += count;
+ log("Read " + count + " bytes (" + total + " total)");
+ if (total >= response.length && total < contentLength) {
+ response = Arrays.copyOf(response, total * 2);
+ }
+ }
+ response = Arrays.copyOf(response, total);
+ String webPage = new String(response, 0, total);
+ if (debug) {
+ log("Web page:\n" + webPage);
+ }
+ }
+ }
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ HttpsUrlConnClient(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;
+ }
+
+ final void startServer(ServerParameters servParams, boolean newThread)
+ throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ @Override
+ 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...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide(servParams);
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ final void startClient(ClientParameters cliParams, boolean newThread)
+ throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ @Override
+ 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;
+ }
+
+ static class HtucSSLSocketFactory extends SSLSocketFactory {
+ SSLContext sslc = SSLContext.getInstance("TLS");
+
+ HtucSSLSocketFactory(ClientParameters cliParams)
+ throws GeneralSecurityException {
+ super();
+
+ // 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);
+ }
+
+ sslc.init(null, tmf.getTrustManagers(), null);
+ }
+
+ @Override
+ public Socket createSocket(Socket s, String host, int port,
+ boolean autoClose) throws IOException {
+ Socket sock = sslc.getSocketFactory().createSocket(s, host, port,
+ autoClose);
+ setCiphers(sock);
+ return sock;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port)
+ throws IOException {
+ Socket sock = sslc.getSocketFactory().createSocket(host, port);
+ setCiphers(sock);
+ return sock;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port,
+ InetAddress localAddress, int localPort) throws IOException {
+ Socket sock = sslc.getSocketFactory().createSocket(host, port,
+ localAddress, localPort);
+ setCiphers(sock);
+ return sock;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port)
+ throws IOException {
+ Socket sock = sslc.getSocketFactory().createSocket(host, port);
+ setCiphers(sock);
+ return sock;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port,
+ InetAddress localAddress, int localPort)
+ throws IOException {
+ Socket sock = sslc.getSocketFactory().createSocket(host, port,
+ localAddress, localPort);
+ setCiphers(sock);
+ return sock;
+ }
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+ return sslc.getDefaultSSLParameters().getCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return sslc.getSupportedSSLParameters().getCipherSuites();
+ }
+
+ private static void setCiphers(Socket sock) {
+ if (sock instanceof SSLSocket) {
+ String[] ciphers = { "TLS_RSA_WITH_AES_128_CBC_SHA" };
+ ((SSLSocket)sock).setEnabledCipherSuites(ciphers);
+ }
+ }
+ }
+
+}