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]