On Thu, Nov 5, 2026 at 2:24 PM Rémy Maucherat via dev <[email protected]> wrote:
> On Thu, Nov 5, 2026 at 1:10 PM dsoumis.apache.org via dev > <[email protected]> wrote: > > > > This is an automated email from the ASF dual-hosted git repository. > > > > dsoumis pushed a commit to branch 9.0.x > > in repository https://gitbox.apache.org/repos/asf/tomcat.git > > > > commit 24d49b8a81971ac56989d4a6d6283b56cd5c2517 > > Author: Dimitris Soumis <[email protected]> > > AuthorDate: Wed Oct 28 15:23:07 2026 +0200 > > > > Use the certificate actually served after Windows-MY alias resolution > > At least when I am spamming, it's not about Windows stuff ... > > Rémy > I kept the commits separate rather than squashing them, so the individual changes and their rationale remain clear in the history. I think this is valuable, even if it generates some additional traffic on the list. Dimitris > > > The configured certificateKeyAlias can differ from the certificate > actually > > served once the SunMSCAPI Windows-MY numbered-duplicate resolution > has selected > > a different alias. Consumers that looked the certificate up by the > configured > > alias therefore reported or monitored the wrong certificate. > > --- > > .../apache/catalina/manager/ManagerServlet.java | 7 +- > > .../apache/tomcat/util/net/AbstractEndpoint.java | 19 +++- > > .../apache/tomcat/util/net/LocalStrings.properties | 1 + > > java/org/apache/tomcat/util/net/SSLContext.java | 38 ++++++- > > java/org/apache/tomcat/util/net/SSLHostConfig.java | 6 +- > > .../tomcat/util/net/SSLHostConfigCertificate.java | 25 +++++ > > .../tomcat/util/net/jsse/JSSESSLContext.java | 17 +++ > > .../SSLUtilBaseCertificateSelectionTestBase.java | 123 > +++++++++++++++++++-- > > .../TestSSLUtilBaseConfiguredAliasSelection.java | 59 ++++++++++ > > 9 files changed, 267 insertions(+), 28 deletions(-) > > > > diff --git a/java/org/apache/catalina/manager/ManagerServlet.java > b/java/org/apache/catalina/manager/ManagerServlet.java > > index 4300098f56..cde65b296a 100644 > > --- a/java/org/apache/catalina/manager/ManagerServlet.java > > +++ b/java/org/apache/catalina/manager/ManagerServlet.java > > @@ -73,7 +73,6 @@ import org.apache.tomcat.util.modeler.Registry; > > import org.apache.tomcat.util.net.SSLContext; > > import org.apache.tomcat.util.net.SSLHostConfig; > > import org.apache.tomcat.util.net.SSLHostConfigCertificate; > > -import org.apache.tomcat.util.net.SSLUtilBase; > > import org.apache.tomcat.util.res.StringManager; > > import org.apache.tomcat.util.security.Escape; > > > > @@ -1746,14 +1745,10 @@ public class ManagerServlet extends HttpServlet > implements ContainerServlet { > > sslHostConfigCert.getType(); > > List<String> certList = new ArrayList<>(); > > SSLContext sslContext = > sslHostConfigCert.getSslContext(); > > - String alias = > sslHostConfigCert.getCertificateKeyAlias(); > > - if (alias == null) { > > - alias = SSLUtilBase.DEFAULT_KEY_ALIAS; > > - } > > if (sslContext == null) { > > > certList.add(smClient.getString("managerServlet.certsNotLoaded")); > > } else { > > - X509Certificate[] certs = > sslContext.getCertificateChain(alias); > > + X509Certificate[] certs = > sslContext.getCertificateChain(sslHostConfigCert); > > if (certs == null) { > > > certList.add(smClient.getString("managerServlet.certsNotAvailable")); > > } else { > > diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java > b/java/org/apache/tomcat/util/net/AbstractEndpoint.java > > index a8627e8eec..dea7c8b9a5 100644 > > --- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java > > +++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java > > @@ -517,6 +517,7 @@ public abstract class AbstractEndpoint<S, U> { > > */ > > protected void logCertificate(SSLHostConfigCertificate certificate) > { > > SSLHostConfig sslHostConfig = certificate.getSSLHostConfig(); > > + SSLContext sslContext = certificate.getSslContext(); > > > > String certificateInfo; > > > > @@ -551,13 +552,19 @@ public abstract class AbstractEndpoint<S, U> { > > getLogCertificate().info(sm.getString("endpoint.tls.info", > getName(), sslHostConfig.getHostName(), > > certificate.getType(), certificateInfo, > trustStoreSource)); > > > > - SSLContext sslContext = certificate.getSslContext(); > > - if (getLogCertificate().isDebugEnabled() && sslContext != null) > { > > - String alias = certificate.getCertificateKeyAlias(); > > - if (alias == null) { > > - alias = SSLUtilBase.DEFAULT_KEY_ALIAS; > > + // When a configured alias was resolved to a different alias > that is actually served (e.g. a SunMSCAPI > > + // Windows-MY numbered duplicate), report it so the alias > logged above is not mistaken for the one in use. > > + String configuredAlias = certificate.getCertificateKeyAlias(); > > + if (sslContext != null && configuredAlias != null) { > > + String servedAlias = sslContext.getServedAlias(certificate); > > + if (servedAlias != null && > !servedAlias.equals(configuredAlias)) { > > + getLogCertificate() > > + > .info(sm.getString("endpoint.tls.info.cert.aliasResolved", configuredAlias, > servedAlias)); > > } > > - X509Certificate[] x509Certificates = > sslContext.getCertificateChain(alias); > > + } > > + > > + if (getLogCertificate().isDebugEnabled() && sslContext != null) > { > > + X509Certificate[] x509Certificates = > sslContext.getCertificateChain(certificate); > > if (x509Certificates != null && x509Certificates.length > > 0) { > > > getLogCertificate().debug(generateCertificateDebug(x509Certificates[0])); > > } else { > > diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties > b/java/org/apache/tomcat/util/net/LocalStrings.properties > > index 5821a88c6d..591ac94273 100644 > > --- a/java/org/apache/tomcat/util/net/LocalStrings.properties > > +++ b/java/org/apache/tomcat/util/net/LocalStrings.properties > > @@ -150,6 +150,7 @@ endpoint.tls.cert.encodingError=Certificate > fingerprints not available > > endpoint.tls.cert.noCerts=Certificate details not available as the > certificate chain returned from the SSLContext was empty > > endpoint.tls.enabledGroups=Configured named groups {0} for the TLS > connector > > endpoint.tls.info=Connector [{0}], TLS virtual host [{1}], certificate > type [{2}] configured from {3} with trust store [{4}] > > +endpoint.tls.info.cert.aliasResolved=The configured certificate alias > [{0}] was resolved to alias [{1}] which is valid for TLS server > authentication > > endpoint.tls.info.cert.keystore=keystore [{0}] using alias [{1}] > > endpoint.tls.info.cert.keystore.direct=Set directly - location unknown > > endpoint.tls.info.cert.pem=key [{0}], certificate [{1}] and certificate > chain [{2}] > > diff --git a/java/org/apache/tomcat/util/net/SSLContext.java > b/java/org/apache/tomcat/util/net/SSLContext.java > > index f51287aa80..25adf54622 100644 > > --- a/java/org/apache/tomcat/util/net/SSLContext.java > > +++ b/java/org/apache/tomcat/util/net/SSLContext.java > > @@ -90,13 +90,49 @@ public interface SSLContext { > > SSLParameters getSupportedSSLParameters(); > > > > /** > > - * Returns the certificate chain for the given alias. > > + * Returns the certificate chain for the given key store alias. > This is a low-level lookup by exact alias; callers > > + * that want the certificate actually served for a configured > {@link SSLHostConfigCertificate} should use > > + * {@link #getCertificateChain(SSLHostConfigCertificate)}, which > resolves the served alias (it can differ from the > > + * configured alias, for example after SunMSCAPI Windows-MY > numbered-duplicate resolution). > > * > > * @param alias The alias > > + * > > * @return The certificate chain > > */ > > X509Certificate[] getCertificateChain(String alias); > > > > + > > + /** > > + * Returns the alias the key manager would select to serve for the > given certificate configuration during a > > + * handshake. This can differ from {@link > SSLHostConfigCertificate#getCertificateKeyAlias()}, for example after > > + * SunMSCAPI Windows-MY numbered-duplicate resolution. The default > returns the configured alias (or > > + * {@link SSLUtilBase#DEFAULT_KEY_ALIAS} when none is configured); > implementations backed by a key manager override > > + * it to resolve the alias actually served. > > + * > > + * @param certificate The certificate configuration > > + * > > + * @return The served alias, never {@code null} > > + */ > > + default String getServedAlias(SSLHostConfigCertificate certificate) > { > > + String alias = certificate.getCertificateKeyAlias(); > > + return alias == null ? SSLUtilBase.DEFAULT_KEY_ALIAS : alias; > > + } > > + > > + > > + /** > > + * Returns the certificate chain actually served for the given > certificate configuration. Unlike > > + * {@link #getCertificateChain(String)}, which looks up a fixed > alias, this uses {@link #getServedAlias}, so callers > > + * such as the certificate reload check observe the certificate > that is really in use. This can differ from the > > + * configured alias, for example after SunMSCAPI Windows-MY > duplicate resolution. > > + * > > + * @param certificate The certificate configuration > > + * > > + * @return The served certificate chain, or {@code null} if none > can be determined > > + */ > > + default X509Certificate[] > getCertificateChain(SSLHostConfigCertificate certificate) { > > + return getCertificateChain(getServedAlias(certificate)); > > + } > > + > > /** > > * Returns the accepted issuers. > > * > > diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java > b/java/org/apache/tomcat/util/net/SSLHostConfig.java > > index 2b16861c07..137393210d 100644 > > --- a/java/org/apache/tomcat/util/net/SSLHostConfig.java > > +++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java > > @@ -1873,11 +1873,7 @@ public class SSLHostConfig implements > Serializable { > > for (SSLHostConfigCertificate sslHostConfigCertificate : > sslHostConfigCertificates) { > > SSLContext sslContext = > sslHostConfigCertificate.getSslContext(); > > if (sslContext != null) { > > - String alias = > sslHostConfigCertificate.getCertificateKeyAlias(); > > - if (alias == null) { > > - alias = SSLUtilBase.DEFAULT_KEY_ALIAS; > > - } > > - X509Certificate[] certificates = > sslContext.getCertificateChain(alias); > > + X509Certificate[] certificates = > sslContext.getCertificateChain(sslHostConfigCertificate); > > if (certificates != null && certificates.length > 0) { > > X509Certificate certificate = certificates[0]; > > Date expirationDate = certificate.getNotAfter(); > > diff --git > a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java > b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java > > index 395a1c6dcc..b413f90e13 100644 > > --- a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java > > +++ b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java > > @@ -243,6 +243,31 @@ public class SSLHostConfigCertificate implements > Serializable { > > } > > > > > > + /** > > + * Returns the alias the given key manager would choose to serve > for this certificate, or {@code null} if it has no > > + * suitable entry. When {@link #getType()} is {@link > Type#UNDEFINED} every key type is tried. This reflects the > > + * alias actually used in a handshake, which can differ from {@link > #getCertificateKeyAlias()} - for example after > > + * the SunMSCAPI Windows-MY numbered-duplicate resolution performed > by SSLUtilBase#getKeyManagers(). > > + * > > + * @param keyManager the key manager that will serve this > certificate > > + * > > + * @return the served alias, or {@code null} if the key manager has > no suitable entry > > + */ > > + public String chooseServerAlias(X509KeyManager keyManager) { > > + Type[] candidateTypes = Type.UNDEFINED.equals(type) ? > Type.values() : new Type[] { type }; > > + for (Type candidateType : candidateTypes) { > > + if (candidateType == Type.UNDEFINED) { > > + continue; > > + } > > + String alias = > keyManager.chooseServerAlias(candidateType.getKeyType(), null, null); > > + if (alias != null) { > > + return alias; > > + } > > + } > > + return null; > > + } > > + > > + > > /** > > * Returns the password for the certificate's private key. > > * > > diff --git a/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java > b/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java > > index 088257110b..eada0c2381 100644 > > --- a/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java > > +++ b/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java > > @@ -34,6 +34,7 @@ import javax.net.ssl.X509KeyManager; > > import javax.net.ssl.X509TrustManager; > > > > import org.apache.tomcat.util.net.SSLContext; > > +import org.apache.tomcat.util.net.SSLHostConfigCertificate; > > > > class JSSESSLContext implements SSLContext { > > > > @@ -89,6 +90,22 @@ class JSSESSLContext implements SSLContext { > > return result; > > } > > > > + > > + @Override > > + public String getServedAlias(SSLHostConfigCertificate certificate) { > > + if (kms != null) { > > + for (KeyManager km : kms) { > > + if (km instanceof X509KeyManager) { > > + String alias = > certificate.chooseServerAlias((X509KeyManager) km); > > + if (alias != null) { > > + return alias; > > + } > > + } > > + } > > + } > > + return SSLContext.super.getServedAlias(certificate); > > + } > > + > > @Override > > public X509Certificate[] getAcceptedIssuers() { > > Set<X509Certificate> certs = new HashSet<>(); > > diff --git > a/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java > b/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java > > index 5e28cbbc8a..83ceeb0827 100644 > > --- > a/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java > > +++ > b/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java > > @@ -20,15 +20,19 @@ package org.apache.tomcat.util.net; > > import java.io.File; > > import java.io.FileInputStream; > > import java.io.InputStream; > > +import java.security.Key; > > import java.security.KeyStore; > > import java.security.KeyStoreSpi; > > +import java.security.PrivateKey; > > import java.security.Provider; > > import java.security.cert.CertificateParsingException; > > import java.security.cert.X509Certificate; > > import java.util.Arrays; > > import java.util.Collection; > > import java.util.Collections; > > +import java.util.Date; > > import java.util.List; > > +import java.util.Set; > > import java.util.stream.Collectors; > > import java.util.stream.Stream; > > > > @@ -143,22 +147,87 @@ public abstract class > SSLUtilBaseCertificateSelectionTestBase extends TomcatBase > > } > > > > > > + /* > > + * Builds and registers an SSLContext for the given configured > alias and key store, then returns the certificates > > + * that SSLHostConfig.certificatesExpiringBefore() - the input to > the TLS certificate reload listener - would flag. > > + * 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); > > + SSLContext context = > sslUtil(certificate).createSSLContext(Collections.emptyList()); > > + try { > > + certificate.setSslContext(context); > > + Date farFuture = new Date(System.currentTimeMillis() + > 3650L * 24 * 60 * 60 * 1000); > > + return > certificate.getSSLHostConfig().certificatesExpiringBefore(farFuture); > > + } finally { > > + context.destroy(); > > + } > > + } > > + > > + > > + /* > > + * 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); > > + SSLContext context = > sslUtil(certificate).createSSLContext(Collections.emptyList()); > > + try { > > + certificate.setSslContext(context); > > + return context.getServedAlias(certificate); > > + } finally { > > + context.destroy(); > > + } > > + } > > + > > + > > + private static SSLHostConfigCertificate > registeredCertificate(String alias, 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); > > + } > > + hostConfig.addCertificate(certificate); > > + return certificate; > > + } > > + > > + > > protected static KeyStore orderedKeystore(String malformedAlias, > KeystoreEntry... entries) throws Exception { > > - return orderedKeystore(new Provider("SunMSCAPI", 1.0, "test") { > > - private static final long serialVersionUID = > -4141665633237722962L; > > - }, "Windows-MY", malformedAlias, entries); > > + return orderedKeystore(sunMSCAPIProvider(), "Windows-MY", true, > malformedAlias, entries); > > + } > > + > > + > > + /* > > + * As orderedKeystore(String, KeystoreEntry...) but the private > keys report a null format, like a non-exportable > > + * SunMSCAPI key. This routes SSLUtilBase.getKeyManagers() through > its production path that keeps the original key > > + * store (ksUsed == ks) and wraps each key manager, rather than the > in-memory-copy branch the exportable JKS-backed > > + * mock otherwise takes. > > + */ > > + protected static KeyStore nonExportableKeystore(KeystoreEntry... > entries) throws Exception { > > + return orderedKeystore(sunMSCAPIProvider(), "Windows-MY", > false, null, entries); > > } > > > > > > protected static KeyStore nonSunMSCAPIKeystore(KeystoreEntry... > entries) throws Exception { > > return orderedKeystore(new Provider("SUN", 1.0, "test") { > > private static final long serialVersionUID = > 4434321038151233913L; > > - }, "JKS", null, entries); > > + }, "JKS", true, null, entries); > > + } > > + > > + > > + private static Provider sunMSCAPIProvider() { > > + return new Provider("SunMSCAPI", 1.0, "test") { > > + private static final long serialVersionUID = > -4141665633237722962L; > > + }; > > } > > > > > > - protected static KeyStore orderedKeystore(Provider provider, String > type, String malformedAlias, > > - KeystoreEntry... entries) throws Exception { > > + protected static KeyStore orderedKeystore(Provider provider, String > type, boolean exportableKeys, > > + String malformedAlias, KeystoreEntry... entries) throws > Exception { > > File file = > TesterKeystoreGenerator.generateKeystore(Arrays.asList(entries)); > > KeyStore delegate = KeyStore.getInstance("JKS"); > > try (InputStream is = new FileInputStream(file)) { > > @@ -184,11 +253,14 @@ public abstract class > SSLUtilBaseCertificateSelectionTestBase extends TomcatBase > > }); > > > EasyMock.expect(spi.engineGetCertificateChain(EasyMock.anyString())) > > .andStubAnswer(() -> > delegate.getCertificateChain(EasyMock.getCurrentArgument(0))); > > - EasyMock.expect(spi.engineGetKey(EasyMock.anyString(), > EasyMock.anyObject(char[].class))) > > - .andStubAnswer(() -> > delegate.getKey(EasyMock.getCurrentArgument(0), > EasyMock.getCurrentArgument(1))); > > + EasyMock.expect(spi.engineGetKey(EasyMock.anyString(), > EasyMock.anyObject(char[].class))).andStubAnswer(() -> { > > + Key key = delegate.getKey(EasyMock.getCurrentArgument(0), > EasyMock.getCurrentArgument(1)); > > + return exportableKeys ? key : nonExportableView(key); > > + }); > > EasyMock.replay(spi); > > - // A real JKS backs the mock, so keys report PKCS#8 and > getKeyManagers() takes its in-memory-copy branch > > - // rather than SunMSCAPI's non-exportable-key path; the > provider name and store type are overridden only so > > + // A real JKS backs the mock. With exportableKeys the keys > report PKCS#8, so getKeyManagers() takes its > > + // in-memory-copy branch; with exportableKeys == false they > report a null format (see nonExportableView), so it > > + // takes SunMSCAPI's non-exportable-key path instead. The > provider name and store type are overridden only so > > // isSunMSCAPIWindowsMY() can be exercised off Windows. > > KeyStore result = new KeyStore(spi, provider, type) { }; > > result.load(null, null); > > @@ -196,6 +268,37 @@ public abstract class > SSLUtilBaseCertificateSelectionTestBase extends TomcatBase > > } > > > > > > + /* > > + * A view of a private key that reports a null format, as a > non-exportable SunMSCAPI key does, while staying usable > > + * by the key manager. getEncoded() returns null for the same > reason; these selection tests never sign, so the key > > + * material itself is never needed. > > + */ > > + private static Key nonExportableView(Key key) { > > + if (!(key instanceof PrivateKey)) { > > + return key; > > + } > > + final PrivateKey privateKey = (PrivateKey) key; > > + return new PrivateKey() { > > + private static final long serialVersionUID = 1L; > > + > > + @Override > > + public String getAlgorithm() { > > + return privateKey.getAlgorithm(); > > + } > > + > > + @Override > > + public String getFormat() { > > + return null; > > + } > > + > > + @Override > > + public byte[] getEncoded() { > > + return null; > > + } > > + }; > > + } > > + > > + > > protected static void assertEku(X509Certificate certificate, String > oid) throws Exception { > > List<String> extendedKeyUsage = > certificate.getExtendedKeyUsage(); > > Assert.assertNotNull("Presented certificate has no Extended Key > Usage", extendedKeyUsage); > > diff --git > a/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java > b/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java > > index 911180f643..05189fc0ef 100644 > > --- > a/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java > > +++ > b/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java > > @@ -20,8 +20,10 @@ package org.apache.tomcat.util.net; > > import java.io.IOException; > > import java.security.KeyStore; > > import java.security.cert.X509Certificate; > > +import java.util.Set; > > > > import org.junit.Assert; > > +import org.junit.Assume; > > import org.junit.Test; > > import org.junit.runner.RunWith; > > import org.junit.runners.Parameterized; > > @@ -160,6 +162,27 @@ public class > TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific > > } > > > > > > + /* > > + * After the SunMSCAPI numbered-duplicate swap, the certificate > served to clients is a numbered duplicate of the > > + * configured alias. The TLS certificate reload listener (via > SSLHostConfig.certificatesExpiringBefore()) must > > + * monitor that served certificate, not the certificate at the > configured alias. Non-exportable keys route > > + * getKeyManagers() through the production ksUsed == ks path a real > SunMSCAPI store uses. Scoped to JSSE: for > > + * OpenSSL the served certificate is held in the native layer and > resolved by a separate code path. > > + */ > > + @Test > > + public void testReloadListenerMonitorsTheServedCertificate() throws > Exception { > > + Assume.assumeTrue("JSSE".equals(connectorName)); > > + KeyStore keystore = nonExportableKeystore(key(HOST, > CLIENT_AUTH), key(HOST + " (1)", SERVER_AUTH)); > > + > > + Set<X509Certificate> monitored = > certificatesMonitoredForReload(HOST, keystore); > > + > > + X509Certificate served = (X509Certificate) > keystore.getCertificateChain(HOST + " (1)")[0]; > > + X509Certificate notServed = (X509Certificate) > keystore.getCertificateChain(HOST)[0]; > > + Assert.assertTrue("The served certificate must be monitored for > reload", monitored.contains(served)); > > + Assert.assertFalse("The unserved configured certificate must > not be monitored", monitored.contains(notServed)); > > + } > > + > > + > > /* > > * In a SunMSCAPI Windows-MY store a certificate without a private > key can take the bare friendly name while the > > * key-bearing certificate of the same name is exposed as a > numbered duplicate. The configured alias then names a > > @@ -187,4 +210,40 @@ public class > TestSSLUtilBaseConfiguredAliasSelection extends SSLUtilBaseCertific > > key("tomcat (2)", SERVER_AUTH)); > > assertEku(presented, > Constants.SERVER_AUTH_EXTENDED_KEY_USAGE_OID); > > } > > + > > + > > + /* > > + * For an OpenSSL connector the served certificate is loaded into > the native layer, but the reload check must still > > + * report it. With exportable keys getKeyManagers() copies only the > resolved key into an in-memory store, so > > + * OpenSSLContext.getCertificateChain() finds nothing under the > configured alias and already falls back to the served > > + * alias - no OpenSSL-specific change is needed. (Non-exportable > SunMSCAPI keys cannot be loaded by OpenSSL at all, > > + * so this exportable case is the only one that runs.) > > + */ > > + @Test > > + public void testReloadResolvesServedCertificateWithOpenSSL() throws > Exception { > > + Assume.assumeTrue(useOpenSSL); > > + KeyStore keystore = orderedKeystore(null, key(HOST, > CLIENT_AUTH), key(HOST + " (1)", SERVER_AUTH)); > > + > > + Set<X509Certificate> monitored = > certificatesMonitoredForReload(HOST, keystore); > > + > > + X509Certificate served = (X509Certificate) > keystore.getCertificateChain(HOST + " (1)")[0]; > > + X509Certificate notServed = (X509Certificate) > keystore.getCertificateChain(HOST)[0]; > > + Assert.assertTrue("OpenSSL reload must resolve the served > certificate", monitored.contains(served)); > > + Assert.assertFalse("OpenSSL reload must not report the unserved > configured certificate", > > + monitored.contains(notServed)); > > + } > > + > > + > > + /* > > + * After the SunMSCAPI numbered-duplicate swap the served alias is > the numbered duplicate, not the configured alias. > > + * This is the alias that logCertificate reports and the reload > check resolves. Non-exportable keys route > > + * getKeyManagers() through the production ksUsed == ks path. > Scoped to JSSE: for OpenSSL the served alias is > > + * resolved from the native layer by a separate path. > > + */ > > + @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)); > > + } > > } > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: [email protected] > > For additional commands, e-mail: [email protected] > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] > >
