This is an automated email from the ASF dual-hosted git repository.
dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new 297f127377 Revert "Select a JSSE certificate valid for TLS server
authentication"
297f127377 is described below
commit 297f1273776ae0d550e30d923a12d778496e31f7
Author: Dimitris Soumis <[email protected]>
AuthorDate: Wed Oct 21 23:26:14 2026 +0300
Revert "Select a JSSE certificate valid for TLS server authentication"
This reverts commit 6bd6ec5c11bc31f0379f0cea6f80c6ab737f0cf6.
---
java/org/apache/tomcat/util/net/Constants.java | 7 -
.../apache/tomcat/util/net/LocalStrings.properties | 4 -
java/org/apache/tomcat/util/net/SSLUtilBase.java | 107 -------------
.../util/net/TestSSLUtilBaseKeyUsageSelection.java | 178 ---------------------
.../tomcat/util/net/TesterKeystoreGenerator.java | 84 ++--------
webapps/docs/changelog.xml | 7 -
webapps/docs/config/http.xml | 16 +-
webapps/docs/ssl-howto.xml | 94 -----------
8 files changed, 15 insertions(+), 482 deletions(-)
diff --git a/java/org/apache/tomcat/util/net/Constants.java
b/java/org/apache/tomcat/util/net/Constants.java
index 80fcbca6a9..1669adbfb3 100644
--- a/java/org/apache/tomcat/util/net/Constants.java
+++ b/java/org/apache/tomcat/util/net/Constants.java
@@ -68,13 +68,6 @@ public class Constants {
*/
public static final String SSL_PROTO_SSLv2Hello = "SSLv2Hello";
- /**
- * EKU OIDs.
- */
- public static final String SERVER_AUTH_EXTENDED_KEY_USAGE_OID =
"1.3.6.1.5.5.7.3.1";
- public static final String CLIENT_AUTH_EXTENDED_KEY_USAGE_OID =
"1.3.6.1.5.5.7.3.2";
- public static final String ANY_EXTENDED_KEY_USAGE_OID = "2.5.29.37.0";
-
/**
* Private constructor to prevent instantiation.
*/
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties
b/java/org/apache/tomcat/util/net/LocalStrings.properties
index 404193ea40..44c9810470 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -182,16 +182,12 @@ sslUtilBase.alias_no_key_entry=Alias name [{0}] does not
identify a key entry
sslUtilBase.fipsPemNotSupported=FIPS enabled so PEM format certificate files
are not supported. Configure the certificate using a key store rather than the
certificateFile attribute
sslUtilBase.invalidTrustManagerClassName=The trustManagerClassName provided
[{0}] does not implement javax.net.ssl.TrustManager
sslUtilBase.keystore_load_failed=Failed to load keystore type [{0}] with path
[{1}] due to [{2}]
-sslUtilBase.multipleServerAuthCerts=Starting from alias [{0}], multiple
certificates valid for TLS server authentication were found: [{1}]. The first
one will be used.
sslUtilBase.noCertFile=SSLHostConfig attribute certificateFile must be defined
when using an SSL connector
sslUtilBase.noCrlSupport=The truststoreProvider [{0}] does not support the
certificateRevocationFile configuration option
sslUtilBase.noKeys=No aliases for private keys found in key store
-sslUtilBase.noServerAuthCert=No certificate valid for TLS server
authentication was found for alias [{0}]. The configured alias will be used
unchanged.
sslUtilBase.noVerificationDepth=The truststoreProvider [{0}] does not support
the certificateVerificationDepth configuration option
sslUtilBase.noneSupported=None of the [{0}] specified are supported by the SSL
engine : [{1}]
-sslUtilBase.selectedServerAuthCert=Selected the certificate with alias [{0}]
for TLS server authentication in preference to the configured alias [{1}]
sslUtilBase.skipped=Tomcat interprets the [{0}] attribute in a manner
consistent with the latest OpenSSL development branch. Some of the specified
[{0}] are not supported by the configured SSL engine for this connector (which
may use JSSE or an older OpenSSL version) and have been skipped: [{1}]
-sslUtilBase.skippedKeyUsage=Skipping certificate with alias [{0}] as it is not
valid for TLS server authentication
sslUtilBase.ssl3=SSLv3 has been explicitly enabled. This protocol is known to
be insecure.
sslUtilBase.tls13.auth=The JSSE TLS 1.3 implementation does not support post
handshake authentication (PHA) and is therefore incompatible with optional
certificate authentication
sslUtilBase.trustedCertNotChecked=The validity dates of the trusted
certificate with alias [{0}] were not checked as the certificate was of an
unknown type
diff --git a/java/org/apache/tomcat/util/net/SSLUtilBase.java
b/java/org/apache/tomcat/util/net/SSLUtilBase.java
index f8ecd64192..7c40846786 100644
--- a/java/org/apache/tomcat/util/net/SSLUtilBase.java
+++ b/java/org/apache/tomcat/util/net/SSLUtilBase.java
@@ -25,7 +25,6 @@ import java.nio.charset.StandardCharsets;
import java.security.DomainLoadStoreParameter;
import java.security.Key;
import java.security.KeyStore;
-import java.security.KeyStoreException;
import java.security.cert.CRL;
import java.security.cert.CRLException;
import java.security.cert.CertPathParameters;
@@ -37,7 +36,6 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.CertificateParsingException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXRevocationChecker;
@@ -61,7 +59,6 @@ import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
-import javax.security.auth.x500.X500Principal;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -414,8 +411,6 @@ public abstract class SSLUtilBase implements SSLUtil {
}
}
- keyAlias = selectServerAuthAlias(ks, keyAlias,
certificate.getCertificateKeyAlias() != null);
-
Key k = ks.getKey(keyAlias, keyPassArray);
if (k != null &&
!"DKS".equalsIgnoreCase(certificate.getCertificateKeystoreType()) &&
"PKCS#8".equalsIgnoreCase(k.getFormat())) {
@@ -455,108 +450,6 @@ public abstract class SSLUtilBase implements SSLUtil {
}
- /*
- * Prefer a key entry whose certificate is usable for TLS server
authentication. This handles key stores that can
- * expose several certificates sharing a Subject (and therefore colliding
on similar aliases).
- * The certificate currently identified by the alias provides the Subject
to match.
- * When no alias was configured every key entry is a candidate.
- * When no usable certificate is found, the configured alias is returned
unchanged.
- */
- private String selectServerAuthAlias(KeyStore ks, String currentAlias,
boolean aliasConfigured)
- throws KeyStoreException, CertificateParsingException {
-
- Certificate current = ks.getCertificate(currentAlias);
- X509Certificate currentX509 = current instanceof X509Certificate x509
? x509 : null;
-
- // If the current selection is already usable there is nothing to do.
- if (currentX509 != null && isServerAuthCapable(currentX509)) {
- return currentAlias;
- }
-
- X500Principal subject = currentX509 == null ? null :
currentX509.getSubjectX500Principal();
-
- List<String> matches = new ArrayList<>();
- // Defense-in-depth so no misbehaving provider can ever hang a
TLS-config path.
- int remaining = ks.size();
- Enumeration<String> aliases = ks.aliases();
- while (aliases.hasMoreElements() && remaining-- > 0) {
- String alias = aliases.nextElement();
- if (!ks.isKeyEntry(alias)) {
- continue;
- }
- Certificate candidate = ks.getCertificate(alias);
- if (!(candidate instanceof X509Certificate x509Candidate)) {
- continue;
- }
- // When the user named an alias, only consider certificates that
share its Subject.
- if (aliasConfigured && subject != null &&
!subject.equals(x509Candidate.getSubjectX500Principal())) {
- continue;
- }
- if (isServerAuthCapable(x509Candidate)) {
- matches.add(alias);
- } else if (log.isDebugEnabled()) {
- log.debug(sm.getString("sslUtilBase.skippedKeyUsage", alias));
- }
- }
-
- if (matches.isEmpty()) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("sslUtilBase.noServerAuthCert",
currentAlias));
- }
- return currentAlias;
- }
- if (matches.size() > 1 && log.isDebugEnabled()) {
- log.debug(sm.getString("sslUtilBase.multipleServerAuthCerts",
currentAlias, matches));
- }
- String selected = matches.get(0);
- if (!selected.equals(currentAlias) && log.isDebugEnabled()) {
- log.debug(sm.getString("sslUtilBase.selectedServerAuthCert",
selected, currentAlias));
- }
- return selected;
- }
-
-
- /**
- * Determines whether the given certificate may be used for TLS server
authentication, based on its Extended Key
- * Usage (EKU) extension. Called by {@link #selectServerAuthAlias} to
choose between certificates that share a
- * Subject.
- * <p>
- * The policy is deliberately lenient about absent usage and strict about
declared usage:
- * <ul>
- * <li>A certificate with <strong>no EKU extension</strong> ({@link
X509Certificate#getExtendedKeyUsage()} returns
- * {@code null}) is treated as usable. An absent EKU places no
restriction on the certificate's purpose - the
- * way TLS implementations generally interpret such certificates - so
rejecting them would needlessly exclude
- * the many server certificates issued without an EKU.</li>
- * <li>A certificate that <strong>does</strong> carry an EKU extension is
usable only if that extension explicitly
- * lists the {@code id-kp-serverAuth} OID ({@value
Constants#SERVER_AUTH_EXTENDED_KEY_USAGE_OID}) or the
- * {@code anyExtendedKeyUsage} OID ({@value
Constants#ANY_EXTENDED_KEY_USAGE_OID}). Any other declared usage
- * - for example a certificate limited to client authentication, such
as a Remote Desktop authentication
- * certificate - is rejected, which is what lets this feature skip the
wrong certificate.</li>
- * </ul>
- *
- * @param cert the certificate to test
- *
- * @return {@code true} if the certificate may be used for TLS server
authentication
- *
- * @throws CertificateParsingException if the Extended Key Usage extension
cannot be decoded
- */
- private static boolean isServerAuthCapable(X509Certificate cert) throws
CertificateParsingException {
- List<String> objectIdentifiers = cert.getExtendedKeyUsage();
-
- if (objectIdentifiers == null) {
- return true;
- }
-
- for (String objectIdentifier : objectIdentifiers) {
- if
(objectIdentifier.equals(Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID) ||
-
objectIdentifier.equals(Constants.ANY_EXTENDED_KEY_USAGE_OID)) {
- return true;
- }
- }
-
- return false;
- }
-
@Override
public String[] getEnabledProtocols() {
return enabledProtocols;
diff --git
a/test/org/apache/tomcat/util/net/TestSSLUtilBaseKeyUsageSelection.java
b/test/org/apache/tomcat/util/net/TestSSLUtilBaseKeyUsageSelection.java
deleted file mode 100644
index a64224c80b..0000000000
--- a/test/org/apache/tomcat/util/net/TestSSLUtilBaseKeyUsageSelection.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.util.net;
-
-import java.io.File;
-import java.security.cert.X509Certificate;
-import java.util.List;
-
-import javax.net.ssl.X509KeyManager;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-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.apache.tomcat.util.net.jsse.JSSEUtil;
-import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.KeyPurposeId;
-
-/**
- * Tests selection of a TLS server authentication certificate when a key store
holds several certificates that share a
- * Subject.
- */
-public class TestSSLUtilBaseKeyUsageSelection {
-
- private static final String HOST = "localhost";
-
- @SuppressWarnings("unused")
- private static final CertificateExtensionsCustomizer SERVER_AUTH =
(keyPair, builder) -> builder
- .addExtension(Extension.extendedKeyUsage, true, new
ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth));
- @SuppressWarnings("unused")
- private static final CertificateExtensionsCustomizer CLIENT_AUTH =
(keyPair, builder) -> builder
- .addExtension(Extension.extendedKeyUsage, true, new
ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth));
- @SuppressWarnings("unused")
- private static final CertificateExtensionsCustomizer ANY_AUTH = (keyPair,
builder) -> builder
- .addExtension(Extension.extendedKeyUsage, true, new
ExtendedKeyUsage(KeyPurposeId.anyExtendedKeyUsage));
-
-
- private static KeystoreEntry key(String alias,
CertificateExtensionsCustomizer eku) {
- return KeystoreEntry.key(alias, HOST, eku);
- }
-
-
- /*
- * Builds a key store from the given entries, configures a JSSE
certificate pointed at alias (null for none), and
- * returns the leaf certificate the key manager actually presents.
- */
- private static X509Certificate present(String alias, KeystoreEntry...
entries) throws Exception {
- File keystore =
TesterKeystoreGenerator.generateKeystore(List.of(entries));
-
- SSLHostConfig hostConfig = new SSLHostConfig();
- SSLHostConfigCertificate certificate = new
SSLHostConfigCertificate(hostConfig, Type.UNDEFINED);
- certificate.setCertificateKeystoreFile(keystore.getAbsolutePath());
- certificate.setCertificateKeystorePassword(TesterSupport.JKS_PASS);
- if (alias != null) {
- certificate.setCertificateKeyAlias(alias);
- }
-
- X509KeyManager keyManager = (X509KeyManager) new
JSSEUtil(certificate).getKeyManagers()[0];
- X509Certificate[] chain =
keyManager.getCertificateChain(keyManager.chooseServerAlias("RSA", null, null));
- Assert.assertNotNull("Key manager returned no certificate chain",
chain);
- return chain[0];
- }
-
-
- private static void assertEku(X509Certificate certificate, String oid)
throws Exception {
- List<String> extendedKeyUsage = certificate.getExtendedKeyUsage();
- Assert.assertNotNull("Presented certificate has no Extended Key
Usage", extendedKeyUsage);
- Assert.assertTrue("Presented certificate must be valid for Extended
Key Usage " + oid,
- extendedKeyUsage.contains(oid));
- }
-
-
- @Test
- public void
testSelectsServerAuthCertificateOverConfiguredClientAuthAlias() throws
Exception {
- X509Certificate presented = present(HOST,
- key(HOST, CLIENT_AUTH),
- key("localhost-server", SERVER_AUTH));
- assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testKeepsConfiguredAliasWhenItIsAlreadyServerAuth() throws
Exception {
- X509Certificate presented = present(HOST,
- key(HOST, SERVER_AUTH),
- key("localhost-client", CLIENT_AUTH));
- assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testSelectsCertificateWithAnyExtendedKeyUsage() throws
Exception {
- X509Certificate presented = present(HOST,
- key(HOST, CLIENT_AUTH),
- key("localhost-any", ANY_AUTH));
- assertEku(presented, Constants.ANY_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testSelectsCertificateWithoutExtendedKeyUsage() throws
Exception {
- X509Certificate presented = present(HOST,
- key(HOST, CLIENT_AUTH),
- key("localhost-none", null));
- Assert.assertNull("A certificate with no Extended Key Usage is
unrestricted and must be selectable",
- presented.getExtendedKeyUsage());
- }
-
-
- @Test
- public void testSelectsServerAuthCertificateWhenNoAliasConfigured() throws
Exception {
- X509Certificate presented = present(null,
- key("first", CLIENT_AUTH),
- key("second", SERVER_AUTH));
- assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testSelectsServerAuthCertificateWhenMultipleMatch() throws
Exception {
- // Two server-auth certificates share the Subject. Which one wins
depends on key-store enumeration order,
- // which JKS does not guarantee, so this only asserts that the
ambiguity resolves to a server-auth
- // certificate rather than the client-auth one - not which of the two
is chosen.
- X509Certificate presented = present(HOST,
- key(HOST, CLIENT_AUTH),
- key("localhost-s1", SERVER_AUTH),
- key("localhost-s2", SERVER_AUTH));
- assertEku(presented, Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testFallsBackToConfiguredAliasWhenNoServerAuthCertificate()
throws Exception {
- X509Certificate presented = present(HOST,
- key(HOST, CLIENT_AUTH),
- key("localhost-2", CLIENT_AUTH));
- assertEku(presented, Constants.CLIENT_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testIgnoresServerAuthCertificateWithDifferentSubject() throws
Exception {
- X509Certificate presented = present(HOST,
- key(HOST, CLIENT_AUTH),
- KeystoreEntry.key("other-server", "other.example",
SERVER_AUTH));
- assertEku(presented, Constants.CLIENT_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-
-
- @Test
- public void testIgnoresTrustedCertificateEntries() throws Exception {
- // The only server-auth-capable certificate is a trusted (keyless)
entry, so it cannot be used as a server key.
- // Selection must skip it and fall back to the configured key entry.
If trusted entries were NOT skipped, the
- // keyless "ca" would be chosen and the resulting key manager would
have no chain for it.
- X509Certificate presented = present(HOST,
- KeystoreEntry.trusted("ca", HOST, SERVER_AUTH),
- key(HOST, CLIENT_AUTH));
- assertEku(presented, Constants.CLIENT_AUTH_EXTENDED_KEY_USAGE_OID);
- }
-}
diff --git a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java
b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java
index 480ddf045b..00f1772fcc 100644
--- a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java
+++ b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java
@@ -26,7 +26,6 @@ import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Date;
-import java.util.List;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
@@ -51,84 +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)}.
- *
- * @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 record KeystoreEntry(String alias, String cn, boolean keyEntry,
CertificateExtensionsCustomizer 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(System.nanoTime());
+ BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
long oneDay = 86400000L;
Date notBefore = new Date(System.currentTimeMillis() - oneDay);
Date notAfter = new Date(System.currentTimeMillis() + 365L * oneDay);
@@ -150,17 +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 record CertAndKey(KeyPair keyPair, X509Certificate 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/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a6725128b4..afc7c04bf6 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -407,13 +407,6 @@
directly via the configured trust store) is not subject to OCSP
checking. (markt)
</fix>
- <update>
- When configuring a JSSE <code>Certificate</code> from a key store that
- exposes several certificates sharing a Subject, ignore certificates
- that are not valid for TLS server authentication so that a usable
- certificate is selected rather than relying on the key store to
- return the correct one for the configured alias. (dsoumis)
- </update>
</changelog>
</subsection>
<subsection name="Jasper">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 55378cae6e..acffcc5d7a 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1702,12 +1702,6 @@
the same order as they were added. If more than one key is present in the
keystore it is strongly recommended that a keyAlias is configured to
ensure that the correct key is used.</p>
- <p>When several certificates in the key store share a Subject, Tomcat
automatically ignores any that are
- not valid for TLS server authentication - i.e. whose Extended Key Usage
- excludes both <code>serverAuth</code> and
<code>anyExtendedKeyUsage</code> -
- so a usable certificate is selected. Certificates with no Extended Key
Usage
- extension are unrestricted and remain eligible. If none qualify, the
- configured alias is used unchanged.</p>
</attribute>
<attribute name="certificateKeyFile" required="false">
@@ -1898,13 +1892,9 @@
<subsection name="Key store types">
<p>In addition to the standard key store types (JKS and PKCS12), most Java
- runtimes support additional key store types such as Windows-MY,
Windows-ROOT,
- DKS as well as hardware security modules. On Windows, Windows-MY and
- Windows-ROOT access the current user's certificate store, while
- Windows-MY-LOCALMACHINE and Windows-ROOT-LOCALMACHINE access the local
machine
- store (reading the local machine store from a non-elevated process requires
- Java 17.0.17, 21.0.9, 24 or later). Generally, to use these additional key
- store types with a TLS Connector in Tomcat:</p>
+ runtimes support additional key store types such as Windows-ROOT,
+ Windows-My, DKS as well as hardware security modules. Generally, to use
+ these additional keystore types with a TLS Connector in Tomcat:</p>
<ul>
<li>Set the certificateKeystoreType and/or truststoreType Connector
diff --git a/webapps/docs/ssl-howto.xml b/webapps/docs/ssl-howto.xml
index 968655fa57..8ac7e61dfe 100644
--- a/webapps/docs/ssl-howto.xml
+++ b/webapps/docs/ssl-howto.xml
@@ -451,100 +451,6 @@ mailing list</a>.</p>
</subsection>
</section>
-<section name="Automatic certificate selection and reloading">
-
-<p>When a key store contains more than one certificate, Tomcat must choose
which
-one to present to clients, and certificates must be replaced before they
expire.
-Two features help to automate this so that a single configuration can be
deployed
-to many hosts without per-host customization: selection of a certificate by key
-usage, and automatic reloading of renewed certificates.</p>
-
-<subsection name="Selecting a certificate by key usage">
-
-<p>When a key store holds several certificates - for example one intended for
TLS
-server authentication alongside one intended for another purpose, which may
share
-the same Subject - Tomcat selects a certificate that is valid for TLS server
-authentication. A certificate qualifies if its Extended Key Usage includes
-<code>serverAuth</code> or <code>anyExtendedKeyUsage</code>, or if it has no
-Extended Key Usage extension at all. Certificates that declare a different
purpose,
-such as client authentication, are ignored.</p>
-
-<p>Because of this, it is usually not necessary to set
-<strong>certificateKeyAlias</strong>: Tomcat picks a usable server certificate
-automatically. When <strong>certificateKeyAlias</strong> is set, only
certificates
-that share the Subject of that alias are considered.</p>
-
-<p>If no certificate valid for server authentication is found, the configured
-alias (or, when none is configured, the first key in the store) is used
unchanged,
-so existing configurations are not affected.</p>
-
-</subsection>
-
-<subsection name="Reloading certificates before they expire">
-
-<p>When certificates are renewed by an external process the
-TLS certificate reload listener can load the renewed certificate without
-restarting Tomcat. Add it to the <strong>Server</strong> element:</p>
-
-<source><![CDATA[<Server ...>
- <Listener
className="org.apache.catalina.security.TLSCertificateReloadListener" />
- ...
-</Server>]]></source>
-
-<p>The listener periodically checks each certificate and, when one is within
-<code>daysBefore</code> days of expiry (14 by default), reloads the associated
-<code>SSLHostConfig</code> so that the renewed certificate is picked up. See
the
-<a href="config/listeners.html">listener documentation</a> for the available
-attributes.</p>
-
-</subsection>
-
-<subsection name="Example: the Windows certificate store">
-
-<p>On Windows, Tomcat can read certificates directly from the Windows
certificate
-store. Set <strong>certificateKeystoreType</strong> to
-<code>Windows-MY-LOCALMACHINE</code> for the local machine store, or to
-<code>Windows-MY</code> for the current user's store. Reading the local machine
-store from a Tomcat service that is not running with elevated privileges
requires
-Java 17.0.17, 21.0.9, 24, or a later release; on earlier releases the service
must
-run with elevated privileges. This works well with AD CS autoenrollment, which
-issues and renews the machine's certificates automatically.</p>
-
-<p>The local machine store usually contains several certificates issued for the
-machine's name, such as a TLS server certificate and a Remote Desktop
-authentication certificate. Because Tomcat selects the certificate that is
valid
-for server authentication, the same configuration can be deployed to every host
-with no <strong>certificateKeyAlias</strong>:</p>
-
-<source><![CDATA[<Connector port="8443" protocol="HTTP/1.1"
- SSLEnabled="true" scheme="https" secure="true">
- <SSLHostConfig>
- <Certificate certificateKeystoreType="Windows-MY-LOCALMACHINE"
- certificateKeystoreFile=""
- certificateKeystorePassword=""
- protocols="TLSv1.2,TLSv1.3" />
- </SSLHostConfig>
-</Connector>]]></source>
-
-<p>Combined with the reload listener described above this gives fully automated
-certificate handling: AD CS issues and renews the certificate, while Tomcat
-selects the correct one and reloads it before it expires.</p>
-
-<p>Two points to note:</p>
-
-<ul>
- <li>The account running Tomcat must have read access to the certificate's
private
- key. A renewed certificate may use a new key that does not inherit this
- access, so grant it as part of the enrollment or renewal process.</li>
- <li>If a host holds more than one certificate valid for server
authentication,
- set a unique friendly name on the intended certificate and use it as the
- <strong>certificateKeyAlias</strong>.</li>
-</ul>
-
-</subsection>
-
-</section>
-
<section name="Using OCSP Certificates">
<p>Tomcat supports the Online Certificate Status Protocol (OCSP) to verify the
status of client provided certificates for a sub-set of connector
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]