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

commit 77f1211a667f71a066af5dcc1a454362f749b5cd
Author: Dimitris Soumis <[email protected]>
AuthorDate: Thu Oct 29 23:32:41 2026 +0200

    Add TestSSLContextWrapper
---
 .../tomcat/util/net/TestSSLContextWrapper.java     | 156 +++++++++++++++++++++
 1 file changed, 156 insertions(+)

diff --git a/test/org/apache/tomcat/util/net/TestSSLContextWrapper.java 
b/test/org/apache/tomcat/util/net/TestSSLContextWrapper.java
new file mode 100644
index 0000000000..a9f897be69
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/TestSSLContextWrapper.java
@@ -0,0 +1,156 @@
+/*
+ * 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.net.Socket;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests expiry monitoring for a supplied {@code javax.net.ssl.SSLContext} (see
+ * {@link SSLUtil#createSSLContext}) whose custom key manager selects 
different aliases for the socket and engine
+ * server-alias callbacks. Tomcat serves over an {@code SSLEngine}, so 
monitoring must track the engine selection, not
+ * the socket one.
+ */
+public class TestSSLContextWrapper {
+
+    private static final char[] PASSWORD = 
TesterSupport.JKS_PASS.toCharArray();
+
+    private static final String ENGINE_KEYSTORE = 
"test/org/apache/tomcat/util/net/localhost-rsa-serverauth.jks";
+    private static final String SOCKET_KEYSTORE = 
"test/org/apache/tomcat/util/net/localhost-rsa-serverauth2.jks";
+
+    private static final X509TrustManager TRUST = new X509TrustManager() {
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String 
authType) {
+            // No-op: the test never validates a peer.
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String 
authType) {
+            // No-op: the test never validates a peer.
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[0];
+        }
+    };
+
+    /*
+     * A key manager that deliberately disagrees between the socket and engine 
server-alias callbacks, as the JDK
+     * contract permits. The engine callback reflects the alias an SSLEngine 
handshake actually serves.
+     */
+    private static final class SplitKeyManager extends X509ExtendedKeyManager {
+        private final X509KeyManager delegate;
+
+        SplitKeyManager(X509KeyManager delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public String chooseServerAlias(String keyType, Principal[] issuers, 
Socket socket) {
+            return "RSA".equals(keyType) ? "socket" : null;
+        }
+
+        @Override
+        public String chooseEngineServerAlias(String keyType, Principal[] 
issuers, SSLEngine engine) {
+            return "RSA".equals(keyType) ? "engine" : null;
+        }
+
+        @Override
+        public X509Certificate[] getCertificateChain(String alias) {
+            return delegate.getCertificateChain(alias);
+        }
+
+        @Override
+        public PrivateKey getPrivateKey(String alias) {
+            return delegate.getPrivateKey(alias);
+        }
+
+        @Override
+        public String[] getServerAliases(String keyType, Principal[] issuers) {
+            return delegate.getServerAliases(keyType, issuers);
+        }
+
+        @Override
+        public String[] getClientAliases(String keyType, Principal[] issuers) {
+            return delegate.getClientAliases(keyType, issuers);
+        }
+
+        @Override
+        public String chooseClientAlias(String[] keyType, Principal[] issuers, 
Socket socket) {
+            return delegate.chooseClientAlias(keyType, issuers, socket);
+        }
+    }
+
+    private static void copyKey(KeyStore target, String alias, String 
keystoreFile) throws Exception {
+        KeyStore source = KeyStore.getInstance(new File(keystoreFile), 
PASSWORD);
+        String original = source.aliases().nextElement();
+        target.setKeyEntry(alias, source.getKey(original, PASSWORD), PASSWORD, 
source.getCertificateChain(original));
+    }
+
+    @Test
+    public void testExpiryMonitoringTracksEngineServedCertificate() throws 
Exception {
+        KeyStore store = KeyStore.getInstance("JKS");
+        store.load(null, null);
+        copyKey(store, "engine", ENGINE_KEYSTORE);
+        copyKey(store, "socket", SOCKET_KEYSTORE);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(store, PASSWORD);
+        X509ExtendedKeyManager keyManager = new 
SplitKeyManager((X509KeyManager) kmf.getKeyManagers()[0]);
+
+        javax.net.ssl.SSLContext jsse = 
javax.net.ssl.SSLContext.getInstance("TLS");
+        jsse.init(new KeyManager[] { keyManager }, new TrustManager[] { TRUST 
}, null);
+
+        SSLContext context = SSLUtil.createSSLContext(jsse, keyManager, TRUST);
+        SSLHostConfig host = new SSLHostConfig();
+        SSLHostConfigCertificate certificate = new 
SSLHostConfigCertificate(host, SSLHostConfigCertificate.Type.RSA);
+        certificate.setCertificateKeyAlias("engine");
+        certificate.setSslContext(context);
+        host.addCertificate(certificate);
+
+        X509Certificate engineCertificate = 
context.getCertificateChain("engine")[0];
+        X509Certificate socketCertificate = 
context.getCertificateChain("socket")[0];
+        Assert.assertNotEquals("Fixtures must provide two distinct 
certificates", engineCertificate,
+                socketCertificate);
+
+        Set<X509Certificate> monitored = host.certificatesExpiringBefore(new 
Date(Long.MAX_VALUE));
+
+        Assert.assertTrue("The engine-served certificate must be monitored for 
expiry",
+                monitored.contains(engineCertificate));
+        Assert.assertFalse("The socket-selected certificate must not be 
monitored",
+                monitored.contains(socketCertificate));
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to