This is an automated email from the ASF dual-hosted git repository. dsoumis pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 61510fbf97208b54132b0c756b8ee1e7f8acd0e8 Author: Dimitris Soumis <[email protected]> AuthorDate: Thu Oct 29 21:03:25 2026 +0200 Replace runtime certificate generation with pre-generated fixtures --- .../SSLUtilBaseCertificateSelectionTestBase.java | 164 +++++++++++++++++---- ...stSSLUtilBaseAutomaticCertificateSelection.java | 36 +++-- .../TestSSLUtilBaseConfiguredAliasSelection.java | 82 +++++------ .../tomcat/util/net/TesterKeystoreGenerator.java | 133 ++--------------- .../tomcat/util/net/localhost-rsa-anyauth.jks | Bin 0 -> 2639 bytes .../tomcat/util/net/localhost-rsa-clientauth.jks | Bin 0 -> 2643 bytes .../apache/tomcat/util/net/localhost-rsa-noeku.jks | Bin 0 -> 2757 bytes .../tomcat/util/net/localhost-rsa-serverauth.jks | Bin 0 -> 2644 bytes .../tomcat/util/net/localhost-rsa-serverauth2.jks | Bin 0 -> 2641 bytes 9 files changed, 207 insertions(+), 208 deletions(-) diff --git a/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java b/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java index d2efc36526..3c3522e889 100644 --- a/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java +++ b/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java @@ -18,6 +18,8 @@ package org.apache.tomcat.util.net; import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreSpi; @@ -28,7 +30,9 @@ import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.EnumMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,11 +46,6 @@ import org.junit.runners.Parameterized.Parameter; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; -import org.apache.tomcat.util.net.TesterKeystoreGenerator.CertificateExtensionsCustomizer; -import org.apache.tomcat.util.net.TesterKeystoreGenerator.KeystoreEntry; -import org.bouncycastle.asn1.x509.ExtendedKeyUsage; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.KeyPurposeId; import org.easymock.EasyMock; /** @@ -91,19 +90,75 @@ public abstract class SSLUtilBaseCertificateSelectionTestBase extends TomcatBase protected static final String CLIENT_AUTH_EXTENDED_KEY_USAGE_OID = "1.3.6.1.5.5.7.3.2"; - @SuppressWarnings("unused") - protected static final CertificateExtensionsCustomizer SERVER_AUTH = (keyPair, builder) -> builder - .addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth)); - @SuppressWarnings("unused") - protected static final CertificateExtensionsCustomizer CLIENT_AUTH = (keyPair, builder) -> builder - .addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth)); - @SuppressWarnings("unused") - protected static final CertificateExtensionsCustomizer ANY_AUTH = (keyPair, builder) -> builder - .addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.anyExtendedKeyUsage)); + private static final String SERVER_AUTH_KEYSTORE = TesterSupport.SSL_DIR + "localhost-rsa-serverauth.jks"; + private static final String SERVER_AUTH_KEYSTORE_2 = TesterSupport.SSL_DIR + "localhost-rsa-serverauth2.jks"; + private static final String CLIENT_AUTH_KEYSTORE = TesterSupport.SSL_DIR + "localhost-rsa-clientauth.jks"; + private static final String ANY_AUTH_KEYSTORE = TesterSupport.SSL_DIR + "localhost-rsa-anyauth.jks"; + private static final String NO_EKU_KEYSTORE = TesterSupport.SSL_DIR + "localhost-rsa-noeku.jks"; + + protected enum CertType { + SERVER_AUTH, + CLIENT_AUTH, + ANY_AUTH, + NO_EKU + } + + private static Map<CertType,List<CertAndKey>> certificatePool; + + private static final class CertAndKey { + + private final PrivateKey key; + private final X509Certificate certificate; + + private CertAndKey(PrivateKey key, X509Certificate certificate) { + this.key = key; + this.certificate = certificate; + } + + public PrivateKey key() { + return key; + } + + public X509Certificate certificate() { + return certificate; + } + } + /** + * An entry to place into a test key store: the alias, which pre-generated certificate to use, and whether it is a + * private key entry or a trusted (certificate-only) entry. + */ + protected static final class KeystoreEntry { + + private final String alias; + private final CertType certType; + private final boolean keyEntry; + + protected KeystoreEntry(String alias, CertType certType, boolean keyEntry) { + this.alias = alias; + this.certType = certType; + this.keyEntry = keyEntry; + } + + protected static KeystoreEntry trusted(String alias) { + return new KeystoreEntry(alias, CertType.SERVER_AUTH, false); + } + + public String alias() { + return alias; + } + + public CertType certType() { + return certType; + } + + public boolean keyEntry() { + return keyEntry; + } + } - protected static KeystoreEntry key(String alias, CertificateExtensionsCustomizer eku) { - return KeystoreEntry.key(alias, HOST, eku); + protected static KeystoreEntry key(String alias, CertType certType) { + return new KeystoreEntry(alias, certType, true); } @@ -150,8 +205,8 @@ public abstract class SSLUtilBaseCertificateSelectionTestBase extends TomcatBase * The cut-off date is far enough in the future that every certificate qualifies, so the result is exactly the set * of certificates the listener monitors for reload. */ - protected Set<X509Certificate> certificatesMonitoredForReload(String alias, KeyStore keystore) throws Exception { - SSLHostConfigCertificate certificate = registeredCertificate(alias, keystore); + protected Set<X509Certificate> certificatesMonitoredForReload(KeyStore keystore) throws Exception { + SSLHostConfigCertificate certificate = registeredCertificate(keystore); SSLContext context = sslUtil(certificate).createSSLContext(List.of()); try { certificate.setSslContext(context); @@ -167,8 +222,8 @@ public abstract class SSLUtilBaseCertificateSelectionTestBase extends TomcatBase * Builds and registers an SSLContext for the given configured alias and key store, then returns the alias the * context reports as actually served - which is what the certificate logging and reload paths resolve. */ - protected String servedAlias(String alias, KeyStore keystore) throws Exception { - SSLHostConfigCertificate certificate = registeredCertificate(alias, keystore); + protected String servedAlias(KeyStore keystore) throws Exception { + SSLHostConfigCertificate certificate = registeredCertificate(keystore); SSLContext context = sslUtil(certificate).createSSLContext(List.of()); try { certificate.setSslContext(context); @@ -179,20 +234,76 @@ public abstract class SSLUtilBaseCertificateSelectionTestBase extends TomcatBase } - private static SSLHostConfigCertificate registeredCertificate(String alias, KeyStore keystore) { + private static SSLHostConfigCertificate registeredCertificate(KeyStore keystore) { SSLHostConfig hostConfig = new SSLHostConfig(); SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(hostConfig, Type.UNDEFINED); certificate.setCertificateKeystore(keystore); certificate.setCertificateKeystoreType("JKS"); certificate.setCertificateKeystorePassword(TesterSupport.JKS_PASS); - if (alias != null) { - certificate.setCertificateKeyAlias(alias); - } + certificate.setCertificateKeyAlias(SSLUtilBaseCertificateSelectionTestBase.HOST); hostConfig.addCertificate(certificate); return certificate; } + private static synchronized Map<CertType,List<CertAndKey>> certificatePool() throws Exception { + if (certificatePool == null) { + Map<CertType,List<CertAndKey>> pool = new EnumMap<>(CertType.class); + pool.put(CertType.SERVER_AUTH, List.of(load(SERVER_AUTH_KEYSTORE), load(SERVER_AUTH_KEYSTORE_2))); + pool.put(CertType.CLIENT_AUTH, List.of(load(CLIENT_AUTH_KEYSTORE))); + pool.put(CertType.ANY_AUTH, List.of(load(ANY_AUTH_KEYSTORE))); + pool.put(CertType.NO_EKU, List.of(load(NO_EKU_KEYSTORE))); + certificatePool = pool; + } + return certificatePool; + } + + + private static CertAndKey load(String keystoreFile) throws Exception { + KeyStore keystore = KeyStore.getInstance(new File(keystoreFile), TesterSupport.JKS_PASS.toCharArray()); + String alias = keystore.aliases().nextElement(); + return new CertAndKey((PrivateKey) keystore.getKey(alias, TesterSupport.JKS_PASS.toCharArray()), + (X509Certificate) keystore.getCertificate(alias)); + } + + + /* + * Populates the key store from the pre-generated pool, one certificate per entry. Repeated entries of the same + * category are given distinct certificates where the pool holds more than one (so two server-auth entries in one + * store differ, as the duplicate-selection test requires). + */ + private static void populate(KeyStore keystore, KeystoreEntry... entries) throws Exception { + Map<CertType,Integer> used = new EnumMap<>(CertType.class); + for (KeystoreEntry entry : entries) { + List<CertAndKey> options = certificatePool().get(entry.certType()); + CertAndKey chosen = options.get((used.merge(entry.certType(), 1, Integer::sum) - 1) % options.size()); + if (entry.keyEntry()) { + keystore.setKeyEntry(entry.alias(), chosen.key(), TesterSupport.JKS_PASS.toCharArray(), + new X509Certificate[] { chosen.certificate() }); + } else { + keystore.setCertificateEntry(entry.alias(), chosen.certificate()); + } + } + } + + + /* + * Writes a temporary JKS file populated from the pool, for the tests that configure a certificateKeystoreFile + * rather than an in-memory key store. + */ + protected static File keystoreFile(KeystoreEntry... entries) throws Exception { + KeyStore keystore = KeyStore.getInstance("JKS"); + keystore.load(null, null); + populate(keystore, entries); + File file = File.createTempFile("cert-selection-", ".jks"); + file.deleteOnExit(); + try (OutputStream os = new FileOutputStream(file)) { + keystore.store(os, TesterSupport.JKS_PASS.toCharArray()); + } + return file; + } + + protected static KeyStore orderedKeystore(String malformedAlias, KeystoreEntry... entries) throws Exception { return orderedKeystore(sunMSCAPIProvider(), "Windows-MY", KeyMode.EXPORTABLE, malformedAlias, entries); } @@ -249,8 +360,9 @@ public abstract class SSLUtilBaseCertificateSelectionTestBase extends TomcatBase protected static KeyStore orderedKeystore(Provider provider, String type, KeyMode keyMode, String malformedAlias, KeystoreEntry... entries) throws Exception { - File file = TesterKeystoreGenerator.generateKeystore(List.of(entries)); - KeyStore delegate = KeyStore.getInstance(file, TesterSupport.JKS_PASS.toCharArray()); + KeyStore delegate = KeyStore.getInstance("JKS"); + delegate.load(null, null); + populate(delegate, entries); List<String> aliases = Stream.of(entries).map(KeystoreEntry::alias).collect(Collectors.toList()); // JKS type of keystore enumeration order is unspecified. diff --git a/test/org/apache/tomcat/util/net/TestSSLUtilBaseAutomaticCertificateSelection.java b/test/org/apache/tomcat/util/net/TestSSLUtilBaseAutomaticCertificateSelection.java index 776a60e15e..d868c269bb 100644 --- a/test/org/apache/tomcat/util/net/TestSSLUtilBaseAutomaticCertificateSelection.java +++ b/test/org/apache/tomcat/util/net/TestSSLUtilBaseAutomaticCertificateSelection.java @@ -30,7 +30,6 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; -import org.apache.tomcat.util.net.TesterKeystoreGenerator.KeystoreEntry; /** * Tests that, when no {@code certificateKeyAlias} is configured, {@link SSLUtilBase#getKeyManagers()} auto-selects the @@ -43,8 +42,8 @@ public class TestSSLUtilBaseAutomaticCertificateSelection extends SSLUtilBaseCer @Test public void testAutoSelectPrefersServerAuthOverClientAuth() throws Exception { X509Certificate presented = present(null, - key("first", CLIENT_AUTH), - key("second", SERVER_AUTH)); + key("first", CertType.CLIENT_AUTH), + key("second", CertType.SERVER_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -52,8 +51,8 @@ public class TestSSLUtilBaseAutomaticCertificateSelection extends SSLUtilBaseCer @Test public void testAutoSelectAcceptsAnyExtendedKeyUsage() throws Exception { X509Certificate presented = present(null, - key("first", CLIENT_AUTH), - key("any", ANY_AUTH)); + key("first", CertType.CLIENT_AUTH), + key("any", CertType.ANY_AUTH)); assertEku(presented, Constants.ANY_EXTENDED_KEY_USAGE_OID); } @@ -61,8 +60,8 @@ public class TestSSLUtilBaseAutomaticCertificateSelection extends SSLUtilBaseCer @Test public void testAutoSelectAcceptsCertificateWithoutExtendedKeyUsage() throws Exception { X509Certificate presented = present(null, - key("first", CLIENT_AUTH), - key("none", null)); + key("first", CertType.CLIENT_AUTH), + key("none", CertType.NO_EKU)); Assert.assertNull("A certificate with no Extended Key Usage is unrestricted and must be selectable", presented.getExtendedKeyUsage()); } @@ -71,44 +70,44 @@ public class TestSSLUtilBaseAutomaticCertificateSelection extends SSLUtilBaseCer @Test public void testAutoSelectSkipsTrustedEntries() { Assert.assertThrows(IOException.class, () -> present(null, - KeystoreEntry.trusted("ca", HOST, SERVER_AUTH), - key("key", CLIENT_AUTH))); + KeystoreEntry.trusted("ca"), + key("key", CertType.CLIENT_AUTH))); } @Test public void testAutoSelectFailsWhenNoneServerAuth() { Assert.assertThrows(IOException.class, () -> present(null, - key("first", CLIENT_AUTH), - key("second", CLIENT_AUTH))); + key("first", CertType.CLIENT_AUTH), + key("second", CertType.CLIENT_AUTH))); } @Test public void testAutoSelectSkipsMalformedExtendedKeyUsage() throws Exception { - KeyStore keystore = orderedKeystore("malformed", key("malformed", CLIENT_AUTH), key("server", SERVER_AUTH)); + KeyStore keystore = orderedKeystore("malformed", key("malformed", CertType.CLIENT_AUTH), key("server", CertType.SERVER_AUTH)); assertEku(present(null, keystore), Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @Test public void testAutoSelectFailsWhenOnlyMalformedExtendedKeyUsage() throws Exception { - KeyStore keystore = orderedKeystore("malformed", key("malformed", CLIENT_AUTH)); + KeyStore keystore = orderedKeystore("malformed", key("malformed", CertType.CLIENT_AUTH)); Assert.assertThrows(IOException.class, () -> present(null, keystore)); } @Test public void testAutoSelectUsesFirstEligibleKey() throws Exception { - KeyStore keystore = orderedKeystore(null, key("client", CLIENT_AUTH), key("first", ANY_AUTH), - key("second", SERVER_AUTH)); + KeyStore keystore = orderedKeystore(null, key("client", CertType.CLIENT_AUTH), key("first", CertType.ANY_AUTH), + key("second", CertType.SERVER_AUTH)); Assert.assertEquals(keystore.getCertificate("first"), present(null, keystore)); } @Test public void testAutoSelectDoesNotSkipKeyReadFailure() throws Exception { - KeyStore keystore = orderedKeystore(null, key("first", SERVER_AUTH), key("second", SERVER_AUTH)); + KeyStore keystore = orderedKeystore(null, key("first", CertType.SERVER_AUTH), key("second", CertType.SERVER_AUTH)); SSLHostConfig hostConfig = new SSLHostConfig(); SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(hostConfig, Type.UNDEFINED); certificate.setCertificateKeystore(keystore); @@ -121,7 +120,7 @@ public class TestSSLUtilBaseAutomaticCertificateSelection extends SSLUtilBaseCer @Test public void testSSLContextUsesSelectedCertificate() throws Exception { - KeyStore keystore = orderedKeystore(null, key("client", CLIENT_AUTH), key("server", SERVER_AUTH)); + KeyStore keystore = orderedKeystore(null, key("client", CertType.CLIENT_AUTH), key("server", CertType.SERVER_AUTH)); SSLContext context = sslUtil(null, keystore).createSSLContext(List.of()); try { Assert.assertEquals(keystore.getCertificate("server"), context.getCertificateChain("server")[0]); @@ -133,8 +132,7 @@ public class TestSSLUtilBaseAutomaticCertificateSelection extends SSLUtilBaseCer @Test public void testSSLContextLoadsSelectedCertificateFromKeystoreFile() throws Exception { - File file = TesterKeystoreGenerator.generateKeystore( - List.of(key(SSLUtilBase.DEFAULT_KEY_ALIAS, CLIENT_AUTH), key("server", SERVER_AUTH))); + File file = keystoreFile(key(SSLUtilBase.DEFAULT_KEY_ALIAS, CertType.CLIENT_AUTH), key("server", CertType.SERVER_AUTH)); KeyStore keystore = KeyStore.getInstance(file, TesterSupport.JKS_PASS.toCharArray()); SSLHostConfig hostConfig = new SSLHostConfig(); SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(hostConfig, Type.UNDEFINED); diff --git a/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java b/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java index 0a8857ad90..fc4a37f02c 100644 --- a/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java +++ b/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java @@ -28,8 +28,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.apache.tomcat.util.net.TesterKeystoreGenerator.KeystoreEntry; - /** * Tests {@link SSLUtilBase#getKeyManagers()} when a {@code certificateKeyAlias} is configured. The configured alias is * used even if its certificate is not valid for TLS server authentication, except in a SunMSCAPI Windows-MY key store: @@ -42,8 +40,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasIsUsedUnchangedWhenNotServerAuth() throws Exception { X509Certificate presented = present(HOST, - key(HOST, CLIENT_AUTH), - key("localhost-server", SERVER_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + key("localhost-server", CertType.SERVER_AUTH)); assertEku(presented, CLIENT_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -51,8 +49,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasIsUsedWhenServerAuth() throws Exception { X509Certificate presented = present(HOST, - key(HOST, SERVER_AUTH), - key("localhost-client", CLIENT_AUTH)); + key(HOST, CertType.SERVER_AUTH), + key("localhost-client", CertType.CLIENT_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -60,8 +58,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasUsesNumberedDuplicateWhenBaseNotServerAuth() throws Exception { X509Certificate presented = present(HOST, - key(HOST, CLIENT_AUTH), - key(HOST + " (1)", SERVER_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + key(HOST + " (1)", CertType.SERVER_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -69,8 +67,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasUnchangedForNonSunMSCAPIStore() throws Exception { KeyStore keystore = nonSunMSCAPIKeystore( - key(HOST, CLIENT_AUTH), - key(HOST + " (1)", SERVER_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + key(HOST + " (1)", CertType.SERVER_AUTH)); X509Certificate presented = present(HOST, keystore); Assert.assertEquals(keystore.getCertificate(HOST), presented); } @@ -79,8 +77,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasKeepsBaseWhenAlreadyServerAuth() throws Exception { X509Certificate presented = present(HOST, - key(HOST, SERVER_AUTH), - key(HOST + " (1)", CLIENT_AUTH)); + key(HOST, CertType.SERVER_AUTH), + key(HOST + " (1)", CertType.CLIENT_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -88,9 +86,9 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasUsesSecondDuplicateWhenFirstIsNotServerAuth() throws Exception { X509Certificate presented = present(HOST, - key(HOST, CLIENT_AUTH), - key(HOST + " (1)", CLIENT_AUTH), - key(HOST + " (2)", SERVER_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + key(HOST + " (1)", CertType.CLIENT_AUTH), + key(HOST + " (2)", CertType.SERVER_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -98,9 +96,9 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasSelectsFirstOfSeveralServerAuthDuplicates() throws Exception { KeyStore keystore = orderedKeystore(null, - key("tomcat", CLIENT_AUTH), - key("tomcat (1)", SERVER_AUTH), - key("tomcat (2)", SERVER_AUTH)); + key("tomcat", CertType.CLIENT_AUTH), + key("tomcat (1)", CertType.SERVER_AUTH), + key("tomcat (2)", CertType.SERVER_AUTH)); X509Certificate presented = present("tomcat", keystore); Assert.assertEquals(keystore.getCertificate("tomcat (1)"), presented); } @@ -109,9 +107,9 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasSkipsTrustedDuplicateAndUsesNextKeyEntry() throws Exception { X509Certificate presented = present(HOST, - key(HOST, CLIENT_AUTH), - KeystoreEntry.trusted(HOST + " (1)", HOST, SERVER_AUTH), - key(HOST + " (2)", SERVER_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + KeystoreEntry.trusted(HOST + " (1)"), + key(HOST + " (2)", CertType.SERVER_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -119,8 +117,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasFallsBackToBaseWhenNoDuplicateIsServerAuth() throws Exception { X509Certificate presented = present(HOST, - key(HOST, CLIENT_AUTH), - key(HOST + " (1)", CLIENT_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + key(HOST + " (1)", CertType.CLIENT_AUTH)); assertEku(presented, CLIENT_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -128,15 +126,15 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasIgnoresAliasNotMatchingDuplicateFormat() throws Exception { X509Certificate presented = present(HOST, - key(HOST, CLIENT_AUTH), - key(HOST + "(1)", SERVER_AUTH)); + key(HOST, CertType.CLIENT_AUTH), + key(HOST + "(1)", CertType.SERVER_AUTH)); assertEku(presented, CLIENT_AUTH_EXTENDED_KEY_USAGE_OID); } @Test public void testConfiguredAliasWithMalformedExtendedKeyUsageIsUsed() throws Exception { - KeyStore keystore = orderedKeystore("malformed", key("malformed", CLIENT_AUTH), key("server", SERVER_AUTH)); + KeyStore keystore = orderedKeystore("malformed", key("malformed", CertType.CLIENT_AUTH), key("server", CertType.SERVER_AUTH)); Assert.assertEquals(keystore.getCertificateChain("malformed")[0], present("malformed", keystore)); } @@ -144,21 +142,21 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasUsesNumberedDuplicateWhenBaseIsMalformed() throws Exception { KeyStore keystore = - orderedKeystore("malformed", key("malformed", CLIENT_AUTH), key("malformed (1)", SERVER_AUTH)); + orderedKeystore("malformed", key("malformed", CertType.CLIENT_AUTH), key("malformed (1)", CertType.SERVER_AUTH)); assertEku(present("malformed", keystore), Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @Test public void testConfiguredMissingAliasFails() { - Assert.assertThrows(IOException.class, () -> present("missing", key("server", SERVER_AUTH))); + Assert.assertThrows(IOException.class, () -> present("missing", key("server", CertType.SERVER_AUTH))); } @Test public void testConfiguredTrustedAliasFails() { - Assert.assertThrows(IOException.class, () -> present("trusted", KeystoreEntry.trusted("trusted", HOST, SERVER_AUTH), - key("server", SERVER_AUTH))); + Assert.assertThrows(IOException.class, () -> present("trusted", KeystoreEntry.trusted("trusted"), + key("server", CertType.SERVER_AUTH))); } @@ -172,9 +170,9 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testReloadListenerMonitorsTheServedCertificate() throws Exception { Assume.assumeTrue("JSSE".equals(connectorName)); - KeyStore keystore = nonExportableKeystore(key(HOST, CLIENT_AUTH), key(HOST + " (1)", SERVER_AUTH)); + KeyStore keystore = nonExportableKeystore(key(HOST, CertType.CLIENT_AUTH), key(HOST + " (1)", CertType.SERVER_AUTH)); - Set<X509Certificate> monitored = certificatesMonitoredForReload(HOST, keystore); + Set<X509Certificate> monitored = certificatesMonitoredForReload(keystore); X509Certificate served = (X509Certificate) keystore.getCertificateChain(HOST + " (1)")[0]; X509Certificate notServed = (X509Certificate) keystore.getCertificateChain(HOST)[0]; @@ -192,8 +190,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredTrustedBaseAliasUsesServerAuthDuplicate() throws Exception { X509Certificate presented = present(HOST, - KeystoreEntry.trusted(HOST, HOST, SERVER_AUTH), - key(HOST + " (1)", SERVER_AUTH)); + KeystoreEntry.trusted(HOST), + key(HOST + " (1)", CertType.SERVER_AUTH)); assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -201,8 +199,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testConfiguredAliasStopsAtGapAndKeepsBase() throws Exception { X509Certificate presented = present("tomcat", - key("tomcat", CLIENT_AUTH), - key("tomcat (2)", SERVER_AUTH)); + key("tomcat", CertType.CLIENT_AUTH), + key("tomcat (2)", CertType.SERVER_AUTH)); assertEku(presented, CLIENT_AUTH_EXTENDED_KEY_USAGE_OID); } @@ -217,9 +215,9 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testReloadResolvesServedCertificateWithOpenSSL() throws Exception { Assume.assumeTrue(useOpenSSL); - KeyStore keystore = orderedKeystore(null, key(HOST, CLIENT_AUTH), key(HOST + " (1)", SERVER_AUTH)); + KeyStore keystore = orderedKeystore(null, key(HOST, CertType.CLIENT_AUTH), key(HOST + " (1)", CertType.SERVER_AUTH)); - Set<X509Certificate> monitored = certificatesMonitoredForReload(HOST, keystore); + Set<X509Certificate> monitored = certificatesMonitoredForReload(keystore); X509Certificate served = (X509Certificate) keystore.getCertificateChain(HOST + " (1)")[0]; X509Certificate notServed = (X509Certificate) keystore.getCertificateChain(HOST)[0]; @@ -237,8 +235,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testServedAliasIsTheNumberedDuplicate() throws Exception { Assume.assumeTrue("JSSE".equals(connectorName)); - KeyStore keystore = nonExportableKeystore(key(HOST, CLIENT_AUTH), key(HOST + " (1)", SERVER_AUTH)); - Assert.assertEquals(HOST + " (1)", servedAlias(HOST, keystore)); + KeyStore keystore = nonExportableKeystore(key(HOST, CertType.CLIENT_AUTH), key(HOST + " (1)", CertType.SERVER_AUTH)); + Assert.assertEquals(HOST + " (1)", servedAlias(keystore)); } @@ -251,7 +249,7 @@ public class TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific @Test public void testServedAliasResolvesForOpenSSLWithOriginalKeystore() throws Exception { Assume.assumeTrue(useOpenSSL); - KeyStore keystore = wrappedExportableKeystore(key(HOST, CLIENT_AUTH), key(HOST + " (1)", SERVER_AUTH)); - Assert.assertEquals(HOST + " (1)", servedAlias(HOST, keystore)); + KeyStore keystore = wrappedExportableKeystore(key(HOST, CertType.CLIENT_AUTH), key(HOST + " (1)", CertType.SERVER_AUTH)); + Assert.assertEquals(HOST + " (1)", servedAlias(keystore)); } } diff --git a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java index 2e94cf8b13..00f1772fcc 100644 --- a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java +++ b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java @@ -26,8 +26,6 @@ import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.Date; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.Extension; @@ -43,8 +41,6 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; public final class TesterKeystoreGenerator { - private static final AtomicLong SERIAL_COUNTER = new AtomicLong(1); - private TesterKeystoreGenerator() {} @FunctionalInterface @@ -54,113 +50,25 @@ public final class TesterKeystoreGenerator { } /** - * Generate a temporary JKS keystore containing a single self-signed RSA certificate. + * Generate a temporary JKS keystore containing a self-signed RSA certificate. * - * @param cn the Common Name for the certificate subject - * @param alias the keystore alias for the key entry - * @param sanNames DNS Subject Alternative Names to include, or {@code null} for none - * @param customizer callback to add extensions to the certificate, or {@code null} for none + * @param cn the Common Name for the certificate subject + * @param alias the keystore alias for the key entry + * @param sanNames DNS Subject Alternative Names to include, or {@code null} for none + * @param customizer callback to add extensions to the certificate, or {@code null} for none. * - * @return a temporary keystore file with password {@link TesterSupport#JKS_PASS} + * @return a temporary keystore file with password {@link TesterSupport#JKS_PASS} * - * @throws Exception if certificate generation or keystore creation fails + * @throws Exception if certificate generation or keystore creation fails */ public static File generateKeystore(String cn, String alias, String[] sanNames, CertificateExtensionsCustomizer customizer) throws Exception { - CertAndKey generated = generateSelfSignedCertificate(cn, sanNames, customizer); - - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, null); - ks.setKeyEntry(alias, generated.keyPair().getPrivate(), TesterSupport.JKS_PASS.toCharArray(), - new X509Certificate[] { generated.certificate() }); - - return storeToTempFile(ks); - } - - /** - * Generate a temporary JKS keystore from an explicit list of entries. Each entry has its own Common Name and may be - * either a private key entry or a trusted certificate entry, which lets tests reproduce stores that mix certificates - * with different Subjects and certificate-only (keyless) entries. - * - * @param entries the entries to add, in order - * - * @return a temporary keystore file with password {@link TesterSupport#JKS_PASS} - * - * @throws Exception if certificate generation or keystore creation fails - */ - public static File generateKeystore(List<KeystoreEntry> entries) throws Exception { - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, null); - - for (KeystoreEntry entry : entries) { - CertAndKey generated = generateSelfSignedCertificate(entry.cn(), null, entry.customizer()); - if (entry.keyEntry()) { - ks.setKeyEntry(entry.alias(), generated.keyPair().getPrivate(), TesterSupport.JKS_PASS.toCharArray(), - new X509Certificate[] { generated.certificate() }); - } else { - ks.setCertificateEntry(entry.alias(), generated.certificate()); - } - } - - return storeToTempFile(ks); - } - - /** - * A single entry for {@link #generateKeystore(List)}. - */ - public static final class KeystoreEntry { - - private final String alias; - private final String cn; - private final boolean keyEntry; - private final CertificateExtensionsCustomizer customizer; - - /** - * @param alias the keystore alias - * @param cn the certificate Subject Common Name - * @param keyEntry {@code true} for a private key entry, {@code false} for a trusted certificate entry - * @param customizer extensions to add to the certificate, or {@code null} for none - */ - public KeystoreEntry(String alias, String cn, boolean keyEntry, CertificateExtensionsCustomizer customizer) { - this.alias = alias; - this.cn = cn; - this.keyEntry = keyEntry; - this.customizer = customizer; - } - - public String alias() { - return alias; - } - - public String cn() { - return cn; - } - - public boolean keyEntry() { - return keyEntry; - } - - public CertificateExtensionsCustomizer customizer() { - return customizer; - } - - public static KeystoreEntry key(String alias, String cn, CertificateExtensionsCustomizer customizer) { - return new KeystoreEntry(alias, cn, true, customizer); - } - - public static KeystoreEntry trusted(String alias, String cn, CertificateExtensionsCustomizer customizer) { - return new KeystoreEntry(alias, cn, false, customizer); - } - } - - private static CertAndKey generateSelfSignedCertificate(String cn, String[] sanNames, - CertificateExtensionsCustomizer customizer) throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(4096); KeyPair keyPair = kpg.generateKeyPair(); X500Name subject = new X500Name("CN=" + cn); - BigInteger serial = BigInteger.valueOf(SERIAL_COUNTER.getAndIncrement()); + BigInteger serial = BigInteger.valueOf(System.currentTimeMillis()); long oneDay = 86400000L; Date notBefore = new Date(System.currentTimeMillis() - oneDay); Date notAfter = new Date(System.currentTimeMillis() + 365L * oneDay); @@ -182,34 +90,17 @@ public final class TesterKeystoreGenerator { ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate()); X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(signer)); - return new CertAndKey(keyPair, certificate); - } - - private static final class CertAndKey { - - private final KeyPair keyPair; - private final X509Certificate certificate; - private CertAndKey(KeyPair keyPair, X509Certificate certificate) { - this.keyPair = keyPair; - this.certificate = certificate; - } - - private KeyPair keyPair() { - return keyPair; - } - - private X509Certificate certificate() { - return certificate; - } - } + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + ks.setKeyEntry(alias, keyPair.getPrivate(), TesterSupport.JKS_PASS.toCharArray(), new X509Certificate[] { certificate }); - private static File storeToTempFile(KeyStore ks) throws Exception { File keystoreFile = File.createTempFile("test-cert-", ".jks"); keystoreFile.deleteOnExit(); try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { ks.store(fos, TesterSupport.JKS_PASS.toCharArray()); } + return keystoreFile; } diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-anyauth.jks b/test/org/apache/tomcat/util/net/localhost-rsa-anyauth.jks new file mode 100644 index 0000000000..9133e6551b Binary files /dev/null and b/test/org/apache/tomcat/util/net/localhost-rsa-anyauth.jks differ diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-clientauth.jks b/test/org/apache/tomcat/util/net/localhost-rsa-clientauth.jks new file mode 100644 index 0000000000..5c330f1f8e Binary files /dev/null and b/test/org/apache/tomcat/util/net/localhost-rsa-clientauth.jks differ diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-noeku.jks b/test/org/apache/tomcat/util/net/localhost-rsa-noeku.jks new file mode 100644 index 0000000000..4cccd21a4b Binary files /dev/null and b/test/org/apache/tomcat/util/net/localhost-rsa-noeku.jks differ diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-serverauth.jks b/test/org/apache/tomcat/util/net/localhost-rsa-serverauth.jks new file mode 100644 index 0000000000..dae52f78a3 Binary files /dev/null and b/test/org/apache/tomcat/util/net/localhost-rsa-serverauth.jks differ diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-serverauth2.jks b/test/org/apache/tomcat/util/net/localhost-rsa-serverauth2.jks new file mode 100644 index 0000000000..32aa3d834c Binary files /dev/null and b/test/org/apache/tomcat/util/net/localhost-rsa-serverauth2.jks differ --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
