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

markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/main by this push:
     new 64f7c282ca Review "Refactor certificate loading"
64f7c282ca is described below

commit 64f7c282caabbab090fe6ecea364d20f92dcd384
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Nov 3 11:01:53 2026 +0000

    Review "Refactor certificate loading"
    
    Restore support for loading keys and certs via URLs
    
    Co-authored by: Claude / Sonnet 5.5
---
 .../util/net/openssl/panama/CertificateLoader.java | 54 ++++++++++++++--------
 .../util/net/openssl/panama/OpenSSLContext.java    |  4 +-
 .../tomcat/util/net/TestSSLHostConfigCompat.java   | 26 +++++++++++
 3 files changed, 64 insertions(+), 20 deletions(-)

diff --git 
a/java/org/apache/tomcat/util/net/openssl/panama/CertificateLoader.java 
b/java/org/apache/tomcat/util/net/openssl/panama/CertificateLoader.java
index 1c9b8655db..0dc416c8d1 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/CertificateLoader.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/CertificateLoader.java
@@ -19,6 +19,7 @@ package org.apache.tomcat.util.net.openssl.panama;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -149,8 +150,9 @@ public final class CertificateLoader {
      *
      * @param certificate     the certificate configuration
      * @param arena           arena for the native allocations
-     * @param engineKeyLoader fallback invoked with the adjusted key file path
-     *                        when PEM parsing fails, returning a private key
+     * @param engineKeyLoader fallback invoked with the path of the key file
+     *                        (relative paths resolved against the Catalina
+     *                        base) when PEM parsing fails, returning a 
private key
      *                        loaded through an OpenSSL ENGINE or
      *                        {@code MemorySegment.NULL} when none is
      *                        available; may be {@code null}
@@ -175,9 +177,11 @@ public final class CertificateLoader {
                 return null;
             }
 
+            // The configured locations are resolved by the ConfigFileLoader 
when the
+            // files are read so, as well as paths relative to the Catalina 
base,
+            // URLs it supports can be used. Do not adjust them here.
             String certFile = certificate.getCertificateFile();
             if (certFile != null) {
-                certFile = SSLHostConfig.adjustRelativePath(certFile);
                 if (certFile.toLowerCase(Locale.ENGLISH).endsWith(".pkcs12")) {
                     return loadPkcs12(certificate, hostName, certFile, 
keyPassToUse, arena);
                 }
@@ -268,7 +272,7 @@ public final class CertificateLoader {
      * @param certificate  The certificate configuration
      * @param hostName     The host name the certificate belongs to, used for
      *                     logging
-     * @param certFile     The (adjusted) path to the certificate file
+     * @param certFile     The location of the certificate file
      * @param keyPassToUse The key passphrase, or {@code null} if none
      * @param arena        Arena for the native allocations
      *
@@ -291,11 +295,12 @@ public final class CertificateLoader {
      * @param certificate     The certificate configuration
      * @param hostName        The host name the certificate belongs to, used
      *                        for logging
-     * @param certFile        The (adjusted) path to the certificate file
+     * @param certFile        The location of the certificate file
      * @param keyPassToUse    The key passphrase, or {@code null} if none
      * @param arena           Arena for the native allocations
-     * @param engineKeyLoader fallback invoked with the adjusted key file
-     *                        path when PEM parsing fails, returning a
+     * @param engineKeyLoader fallback invoked with the path of the key file
+     *                        (relative paths resolved against the Catalina
+     *                        base) when PEM parsing fails, returning a
      *                        private key loaded through an OpenSSL ENGINE or
      *                        {@code MemorySegment.NULL} when none is
      *                        available; may be {@code null}
@@ -311,13 +316,8 @@ public final class CertificateLoader {
         String keyFile = certificate.getCertificateKeyFile();
         if (keyFile == null) {
             keyFile = certFile;
-        } else {
-            keyFile = SSLHostConfig.adjustRelativePath(keyFile);
         }
         String chainFile = certificate.getCertificateChainFile();
-        if (chainFile != null) {
-            chainFile = SSLHostConfig.adjustRelativePath(chainFile);
-        }
 
         byte[] certBytes = readConfigFileBytes(certFile);
         if (certBytes == null) {
@@ -356,7 +356,10 @@ public final class CertificateLoader {
         if (key.equals(MemorySegment.NULL) && engineKeyLoader != null) {
             // Not a parseable PEM key: try the configured OpenSSL ENGINE.
             // The loader returns NULL when no ENGINE is available.
-            key = engineKeyLoader.apply(keyFile);
+            String engineKeyFile = adjustForEngine(keyFile);
+            if (engineKeyFile != null) {
+                key = engineKeyLoader.apply(engineKeyFile);
+            }
         }
         if (key.equals(MemorySegment.NULL)) {
             for (MemorySegment extra : chain) {
@@ -392,7 +395,7 @@ public final class CertificateLoader {
      * @param certificate  The certificate configuration
      * @param hostName     The host name the certificate belongs to, used for
      *                     logging
-     * @param certFile     The (adjusted) path to the PKCS#12 file
+     * @param certFile     The location of the PKCS#12 file
      * @param keyPassToUse The bundle password, or {@code null} if none
      * @param arena        Arena for the native allocations
      *
@@ -603,6 +606,23 @@ public final class CertificateLoader {
 
     // ------------------------------------- File and native reading helpers
 
+    /**
+     * An OpenSSL ENGINE needs a real file. Resolves a configured location, 
relative to the Catalina base if
+     * necessary, to the path of an existing file.
+     *
+     * @param location The configured location of the file
+     *
+     * @return the path of the file, or {@code null} if the location is not an 
existing file (for example, a URL)
+     */
+    private static String adjustForEngine(String location) {
+        try {
+            return SSLHostConfig.adjustRelativePath(location);
+        } catch (FileNotFoundException e) {
+            return null;
+        }
+    }
+
+
     /**
      * Reads a configuration file (relative paths are resolved against the
      * Catalina base directory) into a byte array.
@@ -839,7 +859,6 @@ public final class CertificateLoader {
         try {
             String certFile = certificate.getCertificateFile();
             if (certFile != null) {
-                certFile = SSLHostConfig.adjustRelativePath(certFile);
                 if (certFile.toLowerCase(Locale.ENGLISH).endsWith(".pkcs12")) {
                     return getCertificateChainPkcs12(certificate, certFile);
                 }
@@ -858,7 +877,7 @@ public final class CertificateLoader {
      * leaf; a separately configured chain file is appended to the chain.
      *
      * @param certificate The certificate entry the file belongs to
-     * @param certFile    The (adjusted) path to the certificate file
+     * @param certFile    The location of the certificate file
      *
      * @return The certificate chain, or {@code null} if it could not be read
      */
@@ -876,7 +895,6 @@ public final class CertificateLoader {
 
         String chainFile = certificate.getCertificateChainFile();
         if (chainFile != null) {
-            chainFile = SSLHostConfig.adjustRelativePath(chainFile);
             byte[] chainBytes = readConfigFileBytes(chainFile);
             if (chainBytes != null) {
                 addX509Certificates(chain, factory, chainBytes);
@@ -908,7 +926,7 @@ public final class CertificateLoader {
      * key entry in the bundle.
      *
      * @param certificate The certificate entry the file belongs to
-     * @param certFile    The (adjusted) path to the PKCS#12 file
+     * @param certFile    The location of the PKCS#12 file
      *
      * @return The certificate chain, or {@code null} if it could not be read
      */
diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java 
b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
index d093bba4d7..121e95fe7d 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
@@ -1120,8 +1120,8 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
             }
             // Try to read DH parameters from the SSLCertificateFile, and
             // similarly the ECDH curve name
-            byte[] certificateFileBytes = 
CertificateLoader.readConfigFileBytes(
-                    
SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()));
+            byte[] certificateFileBytes =
+                    
CertificateLoader.readConfigFileBytes(certificate.getCertificateFile());
             MemorySegment certificateBIO = certificateFileBytes == null ? 
MemorySegment.NULL
                     : createMemoryBio(localArena, certificateFileBytes);
             if (!MemorySegment.NULL.equals(certificateBIO)) {
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java 
b/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java
index 1f5e51fc89..c223dc3af9 100644
--- a/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java
@@ -240,6 +240,27 @@ public class TestSSLHostConfigCompat extends 
TomcatBaseTest {
     }
 
 
+    /*
+     * The certificate and key files are read using the ConfigFileLoader so, 
as well as file paths, URLs are accepted.
+     * The OpenSSL implementation that uses Tomcat Native needs a file path so 
is not tested here.
+     */
+    @Test
+    public void testHostRSAWithPemFileUrls() throws Exception {
+        Assume.assumeTrue("Only applies to PEM files", storeType == 
StoreType.PEM);
+        Assume.assumeFalse("Tomcat Native requires file paths",
+                
"org.apache.tomcat.util.net.openssl.OpenSSLImplementation".equals(sslImplementationName));
+
+        SSLHostConfig sslHostConfig = getSSLHostConfig();
+        SSLHostConfigCertificate certificate = new 
SSLHostConfigCertificate(sslHostConfig, Type.RSA);
+        
certificate.setCertificateFile(getFileUrl(TesterSupport.LOCALHOST_RSA_CERT_PEM));
+        
certificate.setCertificateKeyFile(getFileUrl(TesterSupport.LOCALHOST_RSA_KEY_PEM));
+        certificate.setCertificateKeyPassword(TesterSupport.JKS_PASS);
+        sslHostConfig.addCertificate(certificate);
+
+        doTest();
+    }
+
+
     private void configureHostRSA() {
         SSLHostConfig sslHostConfig = getSSLHostConfig();
         switch (storeType) {
@@ -337,6 +358,11 @@ public class TestSSLHostConfigCompat extends 
TomcatBaseTest {
     }
 
 
+    private static String getFileUrl(String relativePath) {
+        return new File(relativePath).toURI().toString();
+    }
+
+
     private static String getPath(String relativePath) {
         File f = new File(relativePath);
         return f.getAbsolutePath();


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

Reply via email to