This is an automated email from the ASF dual-hosted git repository.

markt-asf 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 b7e857feb0 Doc and Javadoc corrections / clarifications
b7e857feb0 is described below

commit b7e857feb089e25583ce283c7a230720d29ad080
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Nov 5 15:02:33 2026 +0000

    Doc and Javadoc corrections / clarifications
    
    Co-authored by Claude / Sonnet 5.5
---
 java/org/apache/tomcat/util/net/SSLUtilBase.java   |  3 ++-
 .../SSLUtilBaseCertificateSelectionTestBase.java   | 29 ++++++++++++----------
 .../TestSSLUtilBaseConfiguredAliasSelection.java   | 20 ++++++++-------
 webapps/docs/ssl-howto.xml                         |  7 +++---
 4 files changed, 33 insertions(+), 26 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/SSLUtilBase.java 
b/java/org/apache/tomcat/util/net/SSLUtilBase.java
index f9969a09eb..cccefeb043 100644
--- a/java/org/apache/tomcat/util/net/SSLUtilBase.java
+++ b/java/org/apache/tomcat/util/net/SSLUtilBase.java
@@ -453,7 +453,8 @@ public abstract class SSLUtilBase implements SSLUtil {
                 ksUsed.load(null, null);
                 ksUsed.setKeyEntry(keyAlias, k, keyPassArray, 
ks.getCertificateChain(keyAlias));
             }
-            // Non-PKCS#8 key stores will use the original key store
+            // Keys that do not report PKCS#8 will use the original key store. 
This includes every SunMSCAPI
+            // (Windows-MY) key, which reports neither a format nor an 
encoding and so can only be used with JSSE.
         }
 
 
diff --git 
a/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java 
b/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java
index 0071964603..01f5398dd7 100644
--- 
a/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java
+++ 
b/test/org/apache/tomcat/util/net/SSLUtilBaseCertificateSelectionTestBase.java
@@ -274,10 +274,11 @@ public abstract class 
SSLUtilBaseCertificateSelectionTestBase extends TomcatBase
 
 
     /*
-     * 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.
+     * As orderedKeystore(String, KeystoreEntry...) but the private keys 
report a null format and a null encoding, as
+     * every real SunMSCAPI key does (CPrivateKey.getFormat() and getEncoded() 
always return null, whether or not the
+     * Windows key is marked exportable). 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
+     * PKCS#8 reporting JKS-backed mock otherwise takes. OpenSSL cannot load 
such keys.
      */
     protected static KeyStore nonExportableKeystore(KeystoreEntry... entries) 
throws Exception {
         return orderedKeystore(sunMSCAPIProvider(), "Windows-MY", 
KeyMode.NON_EXPORTABLE, null, entries);
@@ -286,9 +287,10 @@ public abstract class 
SSLUtilBaseCertificateSelectionTestBase extends TomcatBase
 
     /*
      * As nonExportableKeystore(KeystoreEntry...) - the keys report a null 
format so the original key store is kept and
-     * each key manager is wrapped - but getEncoded() still returns the real 
key material, so OpenSSL can load it. This
-     * reaches the wrapped path (ksUsed == ks) for the OpenSSL 
implementations, where a lookup of the configured alias
-     * would otherwise resolve the wrong certificate.
+     * each key manager is wrapped - but getEncoded() still returns the real 
key material, so OpenSSL can load it. A real
+     * SunMSCAPI key never does this; the mode is synthetic and exists so that 
the wrapped path (ksUsed == ks) can be
+     * exercised for the OpenSSL implementations, where a lookup of the 
configured alias would otherwise resolve the
+     * wrong certificate.
      */
     protected static KeyStore wrappedExportableKeystore(KeystoreEntry... 
entries) throws Exception {
         return orderedKeystore(sunMSCAPIProvider(), "Windows-MY", 
KeyMode.WRAPPED_EXPORTABLE, null, entries);
@@ -313,9 +315,10 @@ public abstract class 
SSLUtilBaseCertificateSelectionTestBase extends TomcatBase
 
     /*
      * How the mocked SunMSCAPI private keys present themselves, which selects 
the branch of
-     * SSLUtilBase.getKeyManagers() under test. EXPORTABLE keys report PKCS#8 
(in-memory copy); NON_EXPORTABLE and
-     * WRAPPED_EXPORTABLE report a null format (original key store kept, key 
managers wrapped). WRAPPED_EXPORTABLE
-     * additionally exposes the key encoding so the OpenSSL implementations 
can load it.
+     * SSLUtilBase.getKeyManagers() under test. EXPORTABLE keys report PKCS#8 
(in-memory copy); this is synthetic for
+     * SunMSCAPI, whose keys never report an encoding. NON_EXPORTABLE keys 
report a null format and encoding, as all
+     * real SunMSCAPI keys do (original key store kept, key managers wrapped). 
WRAPPED_EXPORTABLE reports a null format
+     * but is synthetic: it additionally exposes the key encoding so the 
OpenSSL implementations can load it.
      */
     protected enum KeyMode {
         EXPORTABLE,
@@ -372,7 +375,7 @@ 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
+     * A view of a private key that reports a null format, as every 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.
      */
@@ -403,8 +406,8 @@ public abstract class 
SSLUtilBaseCertificateSelectionTestBase extends TomcatBase
 
 
     /*
-     * As nonExportableView(Key) but getEncoded() returns the real key 
material, modeling a SunMSCAPI key that keeps
-     * the original key store in use (null format) yet can still be exported 
into OpenSSL.
+     * As nonExportableView(Key) but getEncoded() returns the real key 
material. This models a key that keeps the
+     * original key store in use (null format) yet can still be exported into 
OpenSSL, which no real SunMSCAPI key can.
      */
     private static Key wrappedExportableView(Key key) {
         if (!(key instanceof PrivateKey privateKey)) {
diff --git 
a/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java 
b/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java
index fc4a37f02c..0de28fb503 100644
--- 
a/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java
+++ 
b/test/org/apache/tomcat/util/net/TestSSLUtilBaseConfiguredAliasSelection.java
@@ -163,8 +163,9 @@ 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
+     * monitor that served certificate, not the certificate at the configured 
alias. Keys with a null format and
+     * encoding 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
@@ -207,10 +208,10 @@ public class TestSSLUtilBaseConfiguredAliasSelection 
extends SSLUtilBaseCertific
 
     /*
      * 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
+     * report it. With keys that report PKCS#8 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.)
+     * alias - no OpenSSL-specific change is needed. (Real SunMSCAPI keys 
never report an encoding and cannot be loaded
+     * by OpenSSL at all, so this synthetic PKCS#8 case exercises the OpenSSL 
path.)
      */
     @Test
     public void testReloadResolvesServedCertificateWithOpenSSL() throws 
Exception {
@@ -229,8 +230,8 @@ public class TestSSLUtilBaseConfiguredAliasSelection 
extends SSLUtilBaseCertific
 
     /*
      * 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.
+     * This is the alias that logCertificate reports and the reload check 
resolves. Keys with a null format and
+     * encoding route getKeyManagers() through the production ksUsed == ks 
path.
      */
     @Test
     public void testServedAliasIsTheNumberedDuplicate() throws Exception {
@@ -241,8 +242,9 @@ public class TestSSLUtilBaseConfiguredAliasSelection 
extends SSLUtilBaseCertific
 
 
     /*
-     * A SunMSCAPI key that reports a null format but is still exportable 
keeps the original key store in use
-     * (ksUsed == ks) and wraps each key manager, like a real non-exportable 
key, while remaining loadable by OpenSSL.
+     * A synthetic key that reports a null format but still exposes its 
encoding keeps the original key store in use
+     * (ksUsed == ks) and wraps each key manager, like a real SunMSCAPI key, 
while remaining loadable by OpenSSL (which
+     * a real SunMSCAPI key is not).
      * In that wrapped path a lookup of the configured alias still resolves 
its own (clientAuth) chain, so the served
      * alias - not that lookup - must drive what OpenSSL installs.
      */
diff --git a/webapps/docs/ssl-howto.xml b/webapps/docs/ssl-howto.xml
index e35a268913..eb86169426 100644
--- a/webapps/docs/ssl-howto.xml
+++ b/webapps/docs/ssl-howto.xml
@@ -543,12 +543,13 @@ with a configured alias, ensure that the
 renewed certificate is available under that alias. Reloading does not select a
 different alias or prefer a certificate with a later expiry date.</p>
 
-<p>If the certificate's private key is marked non-exportable, pin the JSSE 
implementation by adding
+<p>Private keys held in a Windows certificate store are not exposed to Java in
+encoded form, whether or not the key is marked exportable, so the OpenSSL
+implementations cannot use them. Pin the JSSE implementation by adding
 
<code>sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"</code>
 to the <strong>Connector</strong>. A connector otherwise selects an OpenSSL
 implementation automatically when one is available, and OpenSSL cannot use a
-private key it is unable to read, so initialization fails. An exportable key 
works
-with either implementation.</p>
+private key it is unable to read, so initialization fails.</p>
 
 <p>Two points to note:</p>
 


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

Reply via email to