This is an automated email from the ASF dual-hosted git repository. dsoumis pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 4fd8f8e5a94eb4cae6025e9dc031a4d5fc17ff0e Author: Dimitris Soumis <[email protected]> AuthorDate: Fri Mar 27 14:38:54 2026 +0200 Add generateKeystore method in TesterSupport which utilizes BouncyCastle to generate a temporary JKS keystore containing a self-signed RSA certificate. --- test/org/apache/tomcat/util/net/TesterSupport.java | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java b/test/org/apache/tomcat/util/net/TesterSupport.java index 9c3e0d2095..31a1c73240 100644 --- a/test/org/apache/tomcat/util/net/TesterSupport.java +++ b/test/org/apache/tomcat/util/net/TesterSupport.java @@ -18,11 +18,15 @@ package org.apache.tomcat.util.net; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigInteger; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -36,6 +40,7 @@ import java.security.cert.PKIXRevocationChecker; import java.security.cert.TrustAnchor; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; +import java.util.Date; import java.util.EnumSet; import java.util.Enumeration; import java.util.HashSet; @@ -79,6 +84,15 @@ import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; import org.apache.tomcat.util.net.jsse.JSSEImplementation; import org.apache.tomcat.util.net.openssl.OpenSSLImplementation; import org.apache.tomcat.util.net.openssl.OpenSSLStatus; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; public final class TesterSupport { @@ -412,6 +426,68 @@ public final class TesterSupport { new X500Principal(lastRequestedIssuers[0].getName())); } + @FunctionalInterface + public interface CertificateExtensionsCustomizer { + void customize(KeyPair keyPair, X509v3CertificateBuilder certBuilder) + throws Exception; + } + + /** + * 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. + * + * @return a temporary keystore file with password {@link #JKS_PASS} + * + * @throws Exception if certificate generation or keystore creation fails + */ + public static File generateKeystore(String cn, String alias, 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(System.currentTimeMillis()); + long oneDay = 86400000L; + Date notBefore = new Date(System.currentTimeMillis() - oneDay); + Date notAfter = new Date(System.currentTimeMillis() + 365L * oneDay); + + X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serial, notBefore, notAfter, + subject, keyPair.getPublic()); + + if (sanNames != null && sanNames.length > 0) { + GeneralName[] generalNames = new GeneralName[sanNames.length]; + for (int i = 0; i < sanNames.length; i++) { + generalNames[i] = new GeneralName(GeneralName.dNSName, sanNames[i]); + } + certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(generalNames)); + } + + if (customizer != null) { + customizer.customize(keyPair, certBuilder); + } + + ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate()); + X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(signer)); + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + ks.setKeyEntry(alias, keyPair.getPrivate(), JKS_PASS.toCharArray(), new X509Certificate[] { certificate }); + + File keystoreFile = File.createTempFile("test-cert-", ".jks"); + keystoreFile.deleteOnExit(); + try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { + ks.store(fos, JKS_PASS.toCharArray()); + } + + return keystoreFile; + } + + public static final byte DATA = (byte)33; public static class SimpleServlet extends HttpServlet { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
