Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/javax/net/ssl/Stapling/SSLEngineWithStapling.java
38853 views
/*1* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223// SunJSSE does not support dynamic system properties, no way to re-use24// system properties in samevm/agentvm mode.2526/*27* @test28* @bug 8046321 815382929* @summary OCSP Stapling for TLS30* @library ../../../../java/security/testlibrary31* @build CertificateBuilder SimpleOCSPServer32* @run main/othervm -Djdk.tls.client.protocols="TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv3" SSLEngineWithStapling33*/3435/**36* A SSLEngine usage example which simplifies the presentation37* by removing the I/O and multi-threading concerns.38*39* The test creates two SSLEngines, simulating a client and server.40* The "transport" layer consists two byte buffers: think of them41* as directly connected pipes.42*43* Note, this is a *very* simple example: real code will be much more44* involved. For example, different threading and I/O models could be45* used, transport mechanisms could close unexpectedly, and so on.46*47* When this application runs, notice that several messages48* (wrap/unwrap) pass before any application data is consumed or49* produced. (For more information, please see the SSL/TLS50* specifications.) There may several steps for a successful handshake,51* so it's typical to see the following series of operations:52*53* client server message54* ====== ====== =======55* wrap() ... ClientHello56* ... unwrap() ClientHello57* ... wrap() ServerHello/Certificate58* unwrap() ... ServerHello/Certificate59* wrap() ... ClientKeyExchange60* wrap() ... ChangeCipherSpec61* wrap() ... Finished62* ... unwrap() ClientKeyExchange63* ... unwrap() ChangeCipherSpec64* ... unwrap() Finished65* ... wrap() ChangeCipherSpec66* ... wrap() Finished67* unwrap() ... ChangeCipherSpec68* unwrap() ... Finished69*/7071import javax.net.ssl.*;72import javax.net.ssl.SSLEngineResult.*;73import java.io.*;74import java.math.BigInteger;75import java.security.*;76import java.nio.*;77import java.security.cert.CertPathValidatorException;78import java.security.cert.PKIXBuilderParameters;79import java.security.cert.X509Certificate;80import java.security.cert.X509CertSelector;81import java.util.ArrayList;82import java.util.Collections;83import java.util.Date;84import java.util.HashMap;85import java.util.List;86import java.util.Map;87import java.util.concurrent.TimeUnit;8889import sun.security.testlibrary.SimpleOCSPServer;90import sun.security.testlibrary.CertificateBuilder;9192public class SSLEngineWithStapling {9394/*95* Enables logging of the SSLEngine operations.96*/97private static final boolean logging = true;9899/*100* Enables the JSSE system debugging system property:101*102* -Djavax.net.debug=all103*104* This gives a lot of low-level information about operations underway,105* including specific handshake messages, and might be best examined106* after gaining some familiarity with this application.107*/108private static final boolean debug = true;109110private SSLEngine clientEngine; // client Engine111private ByteBuffer clientOut; // write side of clientEngine112private ByteBuffer clientIn; // read side of clientEngine113114private SSLEngine serverEngine; // server Engine115private ByteBuffer serverOut; // write side of serverEngine116private ByteBuffer serverIn; // read side of serverEngine117118/*119* For data transport, this example uses local ByteBuffers. This120* isn't really useful, but the purpose of this example is to show121* SSLEngine concepts, not how to do network transport.122*/123private ByteBuffer cTOs; // "reliable" transport client->server124private ByteBuffer sTOc; // "reliable" transport server->client125126/*127* The following is to set up the keystores.128*/129static final String passwd = "passphrase";130static final String ROOT_ALIAS = "root";131static final String INT_ALIAS = "intermediate";132static final String SSL_ALIAS = "ssl";133134// PKI components we will need for this test135static KeyStore rootKeystore; // Root CA Keystore136static KeyStore intKeystore; // Intermediate CA Keystore137static KeyStore serverKeystore; // SSL Server Keystore138static KeyStore trustStore; // SSL Client trust store139static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder140static int rootOcspPort; // Port number for root OCSP141static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder142static int intOcspPort; // Port number for intermed. OCSP143144// Extra configuration parameters and constants145static final String[] TLS13ONLY = new String[] { "TLSv1.3" };146static final String[] TLS12MAX =147new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };148149/*150* Main entry point for this test.151*/152public static void main(String args[]) throws Exception {153if (debug) {154System.setProperty("javax.net.debug", "ssl:handshake");155}156157// Create the PKI we will use for the test and start the OCSP servers158createPKI();159160// Set the certificate entry in the intermediate OCSP responder161// with a revocation date of 8 hours ago.162X509Certificate sslCert =163(X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);164Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =165new HashMap<>();166revInfo.put(sslCert.getSerialNumber(),167new SimpleOCSPServer.CertStatusInfo(168SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,169new Date(System.currentTimeMillis() -170TimeUnit.HOURS.toMillis(8))));171intOcsp.updateStatusDb(revInfo);172173// Create a list of TLS protocol configurations we can use to174// drive tests with different handshaking models.175List<String[]> allowedProtList = new ArrayList();176allowedProtList.add(TLS12MAX);177allowedProtList.add(TLS13ONLY);178179for (String[] protocols : allowedProtList) {180SSLEngineWithStapling test = new SSLEngineWithStapling();181try {182test.runTest(protocols);183throw new RuntimeException("Expected failure due to " +184"revocation did not occur");185} catch (Exception e) {186if (!checkClientValidationFailure(e,187CertPathValidatorException.BasicReason.REVOKED)) {188System.out.println(189"*** Didn't find the exception we wanted");190throw e;191}192}193}194195System.out.println("Test Passed.");196}197198/*199* Create an initialized SSLContext to use for these tests.200*/201public SSLEngineWithStapling() throws Exception {202System.setProperty("javax.net.ssl.keyStore", "");203System.setProperty("javax.net.ssl.keyStorePassword", "");204System.setProperty("javax.net.ssl.trustStore", "");205System.setProperty("javax.net.ssl.trustStorePassword", "");206207// Enable OCSP Stapling on both client and server sides, but turn off208// client-side OCSP for revocation checking. This ensures that the209// revocation information from the test has to come via stapling.210System.setProperty("jdk.tls.client.enableStatusRequestExtension",211Boolean.toString(true));212System.setProperty("jdk.tls.server.enableStatusRequestExtension",213Boolean.toString(true));214Security.setProperty("ocsp.enable", "false");215}216217/*218* Run the test.219*220* Sit in a tight loop, both engines calling wrap/unwrap regardless221* of whether data is available or not. We do this until both engines222* report back they are closed.223*224* The main loop handles all of the I/O phases of the SSLEngine's225* lifetime:226*227* initial handshaking228* application data transfer229* engine closing230*231* One could easily separate these phases into separate232* sections of code.233*/234private void runTest(String[] protocols) throws Exception {235boolean dataDone = false;236237createSSLEngines(protocols);238createBuffers();239240SSLEngineResult clientResult; // results from client's last operation241SSLEngineResult serverResult; // results from server's last operation242243/*244* Examining the SSLEngineResults could be much more involved,245* and may alter the overall flow of the application.246*247* For example, if we received a BUFFER_OVERFLOW when trying248* to write to the output pipe, we could reallocate a larger249* pipe, but instead we wait for the peer to drain it.250*/251while (!isEngineClosed(clientEngine) ||252!isEngineClosed(serverEngine)) {253254log("================");255256clientResult = clientEngine.wrap(clientOut, cTOs);257log("client wrap: ", clientResult);258runDelegatedTasks(clientResult, clientEngine);259260serverResult = serverEngine.wrap(serverOut, sTOc);261log("server wrap: ", serverResult);262runDelegatedTasks(serverResult, serverEngine);263264cTOs.flip();265sTOc.flip();266267log("----");268269clientResult = clientEngine.unwrap(sTOc, clientIn);270log("client unwrap: ", clientResult);271runDelegatedTasks(clientResult, clientEngine);272273serverResult = serverEngine.unwrap(cTOs, serverIn);274log("server unwrap: ", serverResult);275runDelegatedTasks(serverResult, serverEngine);276277cTOs.compact();278sTOc.compact();279280/*281* After we've transfered all application data between the client282* and server, we close the clientEngine's outbound stream.283* This generates a close_notify handshake message, which the284* server engine receives and responds by closing itself.285*/286if (!dataDone && (clientOut.limit() == serverIn.position()) &&287(serverOut.limit() == clientIn.position())) {288289/*290* A sanity check to ensure we got what was sent.291*/292checkTransfer(serverOut, clientIn);293checkTransfer(clientOut, serverIn);294295log("\tClosing clientEngine's *OUTBOUND*...");296clientEngine.closeOutbound();297dataDone = true;298}299}300}301302/*303* Using the SSLContext created during object creation,304* create/configure the SSLEngines we'll use for this test.305*/306private void createSSLEngines(String[] protocols) throws Exception {307// Initialize the KeyManager and TrustManager for the server308KeyManagerFactory servKmf = KeyManagerFactory.getInstance("PKIX");309servKmf.init(serverKeystore, passwd.toCharArray());310TrustManagerFactory servTmf =311TrustManagerFactory.getInstance("PKIX");312servTmf.init(trustStore);313314// Initialize the TrustManager for the client with revocation checking315PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustStore,316new X509CertSelector());317pkixParams.setRevocationEnabled(true);318ManagerFactoryParameters mfp =319new CertPathTrustManagerParameters(pkixParams);320TrustManagerFactory cliTmf =321TrustManagerFactory.getInstance("PKIX");322cliTmf.init(mfp);323324// Create the SSLContexts from the factories325SSLContext servCtx = SSLContext.getInstance("TLS");326servCtx.init(servKmf.getKeyManagers(), servTmf.getTrustManagers(),327null);328SSLContext cliCtx = SSLContext.getInstance("TLS");329cliCtx.init(null, cliTmf.getTrustManagers(), null);330331332/*333* Configure the serverEngine to act as a server in the SSL/TLS334* handshake.335*/336serverEngine = servCtx.createSSLEngine();337serverEngine.setEnabledProtocols(protocols);338serverEngine.setUseClientMode(false);339serverEngine.setNeedClientAuth(false);340341/*342* Similar to above, but using client mode instead.343*/344clientEngine = cliCtx.createSSLEngine("client", 80);345clientEngine.setEnabledProtocols(protocols);346clientEngine.setUseClientMode(true);347}348349/*350* Create and size the buffers appropriately.351*/352private void createBuffers() {353354/*355* We'll assume the buffer sizes are the same356* between client and server.357*/358SSLSession session = clientEngine.getSession();359int appBufferMax = session.getApplicationBufferSize();360int netBufferMax = session.getPacketBufferSize();361362/*363* We'll make the input buffers a bit bigger than the max needed364* size, so that unwrap()s following a successful data transfer365* won't generate BUFFER_OVERFLOWS.366*367* We'll use a mix of direct and indirect ByteBuffers for368* tutorial purposes only. In reality, only use direct369* ByteBuffers when they give a clear performance enhancement.370*/371clientIn = ByteBuffer.allocate(appBufferMax + 50);372serverIn = ByteBuffer.allocate(appBufferMax + 50);373374cTOs = ByteBuffer.allocateDirect(netBufferMax);375sTOc = ByteBuffer.allocateDirect(netBufferMax);376377clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());378serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());379}380381/*382* If the result indicates that we have outstanding tasks to do,383* go ahead and run them in this thread.384*/385private static void runDelegatedTasks(SSLEngineResult result,386SSLEngine engine) throws Exception {387388if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {389Runnable runnable;390while ((runnable = engine.getDelegatedTask()) != null) {391log("\trunning delegated task...");392runnable.run();393}394HandshakeStatus hsStatus = engine.getHandshakeStatus();395if (hsStatus == HandshakeStatus.NEED_TASK) {396throw new Exception(397"handshake shouldn't need additional tasks");398}399log("\tnew HandshakeStatus: " + hsStatus);400}401}402403private static boolean isEngineClosed(SSLEngine engine) {404return (engine.isOutboundDone() && engine.isInboundDone());405}406407/*408* Simple check to make sure everything came across as expected.409*/410private static void checkTransfer(ByteBuffer a, ByteBuffer b)411throws Exception {412a.flip();413b.flip();414415if (!a.equals(b)) {416throw new Exception("Data didn't transfer cleanly");417} else {418log("\tData transferred cleanly");419}420421a.position(a.limit());422b.position(b.limit());423a.limit(a.capacity());424b.limit(b.capacity());425}426427/*428* Logging code429*/430private static boolean resultOnce = true;431432private static void log(String str, SSLEngineResult result) {433if (!logging) {434return;435}436if (resultOnce) {437resultOnce = false;438System.out.println("The format of the SSLEngineResult is: \n" +439"\t\"getStatus() / getHandshakeStatus()\" +\n" +440"\t\"bytesConsumed() / bytesProduced()\"\n");441}442HandshakeStatus hsStatus = result.getHandshakeStatus();443log(str +444result.getStatus() + "/" + hsStatus + ", " +445result.bytesConsumed() + "/" + result.bytesProduced() +446" bytes");447if (hsStatus == HandshakeStatus.FINISHED) {448log("\t...ready for application data");449}450}451452private static void log(String str) {453if (logging) {454System.out.println(str);455}456}457458/**459* Creates the PKI components necessary for this test, including460* Root CA, Intermediate CA and SSL server certificates, the keystores461* for each entity, a client trust store, and starts the OCSP responders.462*/463private static void createPKI() throws Exception {464CertificateBuilder cbld = new CertificateBuilder();465KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");466keyGen.initialize(2048);467KeyStore.Builder keyStoreBuilder =468KeyStore.Builder.newInstance("PKCS12", null,469new KeyStore.PasswordProtection(passwd.toCharArray()));470471// Generate Root, IntCA, EE keys472KeyPair rootCaKP = keyGen.genKeyPair();473log("Generated Root CA KeyPair");474KeyPair intCaKP = keyGen.genKeyPair();475log("Generated Intermediate CA KeyPair");476KeyPair sslKP = keyGen.genKeyPair();477log("Generated SSL Cert KeyPair");478479// Set up the Root CA Cert480cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");481cbld.setPublicKey(rootCaKP.getPublic());482cbld.setSerialNumber(new BigInteger("1"));483// Make a 3 year validity starting from 60 days ago484long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);485long end = start + TimeUnit.DAYS.toMillis(1085);486cbld.setValidity(new Date(start), new Date(end));487addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());488addCommonCAExts(cbld);489// Make our Root CA Cert!490X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),491"SHA256withRSA");492log("Root CA Created:\n" + certInfo(rootCert));493494// Now build a keystore and add the keys and cert495rootKeystore = keyStoreBuilder.getKeyStore();496java.security.cert.Certificate[] rootChain = {rootCert};497rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),498passwd.toCharArray(), rootChain);499500// Now fire up the OCSP responder501rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);502rootOcsp.enableLog(logging);503rootOcsp.setNextUpdateInterval(3600);504rootOcsp.start();505506// Wait 5 seconds for server ready507for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) {508Thread.sleep(50);509}510if (!rootOcsp.isServerReady()) {511throw new RuntimeException("Server not ready yet");512}513514rootOcspPort = rootOcsp.getPort();515String rootRespURI = "http://localhost:" + rootOcspPort;516log("Root OCSP Responder URI is " + rootRespURI);517518// Now that we have the root keystore and OCSP responder we can519// create our intermediate CA.520cbld.reset();521cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");522cbld.setPublicKey(intCaKP.getPublic());523cbld.setSerialNumber(new BigInteger("100"));524// Make a 2 year validity starting from 30 days ago525start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);526end = start + TimeUnit.DAYS.toMillis(730);527cbld.setValidity(new Date(start), new Date(end));528addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());529addCommonCAExts(cbld);530cbld.addAIAExt(Collections.singletonList(rootRespURI));531// Make our Intermediate CA Cert!532X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),533"SHA256withRSA");534log("Intermediate CA Created:\n" + certInfo(intCaCert));535536// Provide intermediate CA cert revocation info to the Root CA537// OCSP responder.538Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =539new HashMap<>();540revInfo.put(intCaCert.getSerialNumber(),541new SimpleOCSPServer.CertStatusInfo(542SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));543rootOcsp.updateStatusDb(revInfo);544545// Now build a keystore and add the keys, chain and root cert as a TA546intKeystore = keyStoreBuilder.getKeyStore();547java.security.cert.Certificate[] intChain = {intCaCert, rootCert};548intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),549passwd.toCharArray(), intChain);550intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);551552// Now fire up the Intermediate CA OCSP responder553intOcsp = new SimpleOCSPServer(intKeystore, passwd,554INT_ALIAS, null);555intOcsp.enableLog(logging);556intOcsp.setNextUpdateInterval(3600);557intOcsp.start();558559// Wait 5 seconds for server ready560for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) {561Thread.sleep(50);562}563if (!intOcsp.isServerReady()) {564throw new RuntimeException("Server not ready yet");565}566567intOcspPort = intOcsp.getPort();568String intCaRespURI = "http://localhost:" + intOcspPort;569log("Intermediate CA OCSP Responder URI is " + intCaRespURI);570571// Last but not least, let's make our SSLCert and add it to its own572// Keystore573cbld.reset();574cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");575cbld.setPublicKey(sslKP.getPublic());576cbld.setSerialNumber(new BigInteger("4096"));577// Make a 1 year validity starting from 7 days ago578start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);579end = start + TimeUnit.DAYS.toMillis(365);580cbld.setValidity(new Date(start), new Date(end));581582// Add extensions583addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());584boolean[] kuBits = {true, false, true, false, false, false,585false, false, false};586cbld.addKeyUsageExt(kuBits);587List<String> ekuOids = new ArrayList<>();588ekuOids.add("1.3.6.1.5.5.7.3.1");589ekuOids.add("1.3.6.1.5.5.7.3.2");590cbld.addExtendedKeyUsageExt(ekuOids);591cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));592cbld.addAIAExt(Collections.singletonList(intCaRespURI));593// Make our SSL Server Cert!594X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),595"SHA256withRSA");596log("SSL Certificate Created:\n" + certInfo(sslCert));597598// Provide SSL server cert revocation info to the Intermeidate CA599// OCSP responder.600revInfo = new HashMap<>();601revInfo.put(sslCert.getSerialNumber(),602new SimpleOCSPServer.CertStatusInfo(603SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));604intOcsp.updateStatusDb(revInfo);605606// Now build a keystore and add the keys, chain and root cert as a TA607serverKeystore = keyStoreBuilder.getKeyStore();608java.security.cert.Certificate[] sslChain = {sslCert, intCaCert, rootCert};609serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),610passwd.toCharArray(), sslChain);611serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);612613// And finally a Trust Store for the client614trustStore = keyStoreBuilder.getKeyStore();615trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);616}617618private static void addCommonExts(CertificateBuilder cbld,619PublicKey subjKey, PublicKey authKey) throws IOException {620cbld.addSubjectKeyIdExt(subjKey);621cbld.addAuthorityKeyIdExt(authKey);622}623624private static void addCommonCAExts(CertificateBuilder cbld)625throws IOException {626cbld.addBasicConstraintsExt(true, true, -1);627// Set key usage bits for digitalSignature, keyCertSign and cRLSign628boolean[] kuBitSettings = {true, false, false, false, false, true,629true, false, false};630cbld.addKeyUsageExt(kuBitSettings);631}632633/**634* Helper routine that dumps only a few cert fields rather than635* the whole toString() output.636*637* @param cert an X509Certificate to be displayed638*639* @return the String output of the issuer, subject and640* serial number641*/642private static String certInfo(X509Certificate cert) {643StringBuilder sb = new StringBuilder();644sb.append("Issuer: ").append(cert.getIssuerX500Principal()).645append("\n");646sb.append("Subject: ").append(cert.getSubjectX500Principal()).647append("\n");648sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");649return sb.toString();650}651652/**653* Checks a validation failure to see if it failed for the reason we think654* it should. This comes in as an SSLException of some sort, but it655* encapsulates a CertPathValidatorException at some point in the656* exception stack.657*658* @param e the exception thrown at the top level659* @param reason the underlying CertPathValidatorException BasicReason660* we are expecting it to have.661*662* @return true if the reason matches up, false otherwise.663*/664static boolean checkClientValidationFailure(Exception e,665CertPathValidatorException.BasicReason reason) {666boolean result = false;667668// Locate the CertPathValidatorException. If one669// Does not exist, then it's an automatic failure of670// the test.671Throwable curExc = e;672CertPathValidatorException cpve = null;673while (curExc != null) {674if (curExc instanceof CertPathValidatorException) {675cpve = (CertPathValidatorException)curExc;676}677curExc = curExc.getCause();678}679680// If we get through the loop and cpve is null then we681// we didn't find CPVE and this is a failure682if (cpve != null) {683if (cpve.getReason() == reason) {684result = true;685} else {686System.out.println("CPVE Reason Mismatch: Expected = " +687reason + ", Actual = " + cpve.getReason());688}689} else {690System.out.println("Failed to find an expected CPVE");691}692693return result;694}695}696697698