/* * Copyright (c) 2023, 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. */ /* * @test * @bug 8179502 * @summary Enhance OCSP, CRL and Certificate Fetch Timeouts * @modules java.base/sun.security.x509 * java.base/sun.security.util * @library /test/lib ../../../../java/security/testlibrary * @build CertificateBuilder * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true * -Dcom.sun.security.cert.readtimeout=1 AIACertTimeout 2000 false * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true * -Dcom.sun.security.cert.readtimeout=1s AIACertTimeout 2000 false * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true * -Dcom.sun.security.cert.readtimeout=3 AIACertTimeout 2000 true * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true * -Dcom.sun.security.cert.readtimeout=1500ms AIACertTimeout 2000 false * @run main/othervm -Dcom.sun.security.enableAIAcaIssuers=true * -Dcom.sun.security.cert.readtimeout=2750ms AIACertTimeout 2000 true * @run main/othervm -Djava.security.debug=certpath * -Dcom.sun.security.enableAIAcaIssuers=false * -Dcom.sun.security.cert.readtimeout=20000ms AIACertTimeout 10000 false */ import com.sun.net.httpserver.*; import java.io.*; import java.math.BigInteger; import java.net.InetSocketAddress; import java.security.cert.*; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.spec.*; import java.util.*; import java.util.concurrent.TimeUnit; import sun.security.testlibrary.CertificateBuilder; import sun.security.x509.*; import sun.security.util.*; public class AIACertTimeout { private static final boolean logging = true; // PKI and server components we will need for this test private static KeyPair rootKp; // Root CA keys private static X509Certificate rootCert; private static KeyPair intKp; // Intermediate CA keys private static X509Certificate intCert; private static KeyPair eeKp; // End-entity keys private static X509Certificate eeCert; public static void main(String[] args) throws Exception { int servTimeoutMsec = (args != null && args.length >= 1) ? Integer.parseInt(args[0]) : -1; boolean expectedPass = args != null && args.length >= 2 && Boolean.parseBoolean(args[1]); createAuthorities(); CaCertHttpServer aiaServer = new CaCertHttpServer(intCert, servTimeoutMsec); try { aiaServer.start(); createEE(aiaServer.getAddress()); X509CertSelector target = new X509CertSelector(); target.setCertificate(eeCert); PKIXParameters params = new PKIXBuilderParameters(Set.of( new TrustAnchor(rootCert, null)), target); params.setRevocationEnabled(false); try { CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX"); CertPathBuilderResult result = cpb.build(params); if (expectedPass) { int pathLen = result.getCertPath().getCertificates().size(); if (pathLen != 2) { throw new RuntimeException("Expected 2 certificates " + "in certpath, got " + pathLen); } } else { throw new RuntimeException("Missing expected CertPathBuilderException"); } } catch (CertPathBuilderException cpve) { if (!expectedPass) { log("Cert path building failed as expected: " + cpve); } else { throw cpve; } } } finally { aiaServer.stop(); } } private static class CaCertHttpServer { private final X509Certificate caCert; private final HttpServer server; private final int timeout; public CaCertHttpServer(X509Certificate cert, int timeout) throws IOException { caCert = Objects.requireNonNull(cert, "Null CA cert disallowed"); server = HttpServer.create(); this.timeout = timeout; if (timeout > 0) { log("Created HttpServer with timeout of " + timeout + " msec."); } else { log("Created HttpServer with no timeout"); } } public void start() throws IOException { server.bind(new InetSocketAddress("localhost", 0), 0); server.createContext("/cacert", t -> { try (InputStream is = t.getRequestBody()) { is.readAllBytes(); } try { if (timeout > 0) { // Sleep in order to simulate network latency log("Server sleeping for " + timeout + " msec."); Thread.sleep(timeout); } byte[] derCert = caCert.getEncoded(); t.getResponseHeaders().add("Content-Type", "application/pkix-cert"); t.sendResponseHeaders(200, derCert.length); try (OutputStream os = t.getResponseBody()) { os.write(derCert); } } catch (InterruptedException | CertificateEncodingException exc) { throw new IOException(exc); } }); server.setExecutor(null); server.start(); log("Started HttpServer: Listening on " + server.getAddress()); } public void stop() { server.stop(0); } public InetSocketAddress getAddress() { return server.getAddress(); } } /** * Creates the CA PKI components necessary for this test. */ private static void createAuthorities() throws Exception { CertificateBuilder cbld = new CertificateBuilder(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); keyGen.initialize(new ECGenParameterSpec("secp256r1")); // Generate Root, IntCA, EE keys rootKp = keyGen.genKeyPair(); log("Generated Root CA KeyPair"); intKp = keyGen.genKeyPair(); log("Generated Intermediate CA KeyPair"); eeKp = keyGen.genKeyPair(); log("Generated End Entity Cert KeyPair"); // 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); boolean[] kuBitSettings = {true, false, false, false, false, true, true, false, false}; // Set up the Root CA Cert cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany"). setPublicKey(rootKp.getPublic()). setSerialNumber(new BigInteger("1")). setValidity(new Date(start), new Date(end)). addSubjectKeyIdExt(rootKp.getPublic()). addAuthorityKeyIdExt(rootKp.getPublic()). addBasicConstraintsExt(true, true, -1). addKeyUsageExt(kuBitSettings); // Make our Root CA Cert! rootCert = cbld.build(null, rootKp.getPrivate(), "SHA256withECDSA"); log("Root CA Created:\n" + certInfo(rootCert)); // Make a 2 year validity starting from 30 days ago start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30); end = start + TimeUnit.DAYS.toMillis(730); // Now that we have the root keystore we can create our // intermediate CA. cbld.reset(); cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany"). setPublicKey(intKp.getPublic()). setSerialNumber(new BigInteger("100")). setValidity(new Date(start), new Date(end)). addSubjectKeyIdExt(intKp.getPublic()). addAuthorityKeyIdExt(rootKp.getPublic()). addBasicConstraintsExt(true, true, -1). addKeyUsageExt(kuBitSettings); // Make our Intermediate CA Cert! intCert = cbld.build(rootCert, rootKp.getPrivate(), "SHA256withECDSA"); log("Intermediate CA Created:\n" + certInfo(intCert)); } /** * Creates the end entity certificate from the previously generated * intermediate CA cert. * * @param aiaAddr the address/port of the server that will hold the issuer * certificate. This will be used to create an AIA URI. */ private static void createEE(InetSocketAddress aiaAddr) throws Exception { // Make a 1 year validity starting from 7 days ago long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7); long end = start + TimeUnit.DAYS.toMillis(365); boolean[] kuBits = {true, false, false, false, false, false, false, false, false}; List ekuOids = List.of("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.4"); String aiaUri = String.format("http://%s:%d/cacert", aiaAddr.getHostName(), aiaAddr.getPort()); CertificateBuilder cbld = new CertificateBuilder(); cbld.setSubjectName("CN=Oscar T. Grouch, O=SomeCompany"). setPublicKey(eeKp.getPublic()). setSerialNumber(new BigInteger("4096")). setValidity(new Date(start), new Date(end)). addSubjectKeyIdExt(eeKp.getPublic()). addAuthorityKeyIdExt(intKp.getPublic()). addKeyUsageExt(kuBits).addExtendedKeyUsageExt(ekuOids). addAIAExt(List.of("CAISSUER|" + aiaUri)); // Build the cert eeCert = cbld.build(intCert, intKp.getPrivate(), "SHA256withECDSA"); log("EE Certificate Created:\n" + certInfo(eeCert)); } /** * 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(); } private static void log(String str) { if (logging) { System.out.println("[" + Thread.currentThread().getName() + "] " + str); } } }