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

michaelo pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 3d8db8cd57 BZ 66670: Add SSLHostConfig#certificateKeyPasswordFile and 
SSLHostConfig#certificateKeystorePasswordFile
3d8db8cd57 is described below

commit 3d8db8cd57b1e89be75b004b7401eb9581f531a2
Author: Michael Osipov <micha...@apache.org>
AuthorDate: Wed Sep 27 11:23:19 2023 +0200

    BZ 66670: Add SSLHostConfig#certificateKeyPasswordFile and 
SSLHostConfig#certificateKeystorePasswordFile
---
 .../coyote/http11/AbstractHttp11Protocol.java      | 34 ++++++++++++++++
 java/org/apache/tomcat/util/net/SSLHostConfig.java | 34 +++++++++++++++-
 .../tomcat/util/net/SSLHostConfigCertificate.java  | 27 ++++++++++++-
 java/org/apache/tomcat/util/net/SSLUtilBase.java   | 47 ++++++++++++++++++----
 java/org/apache/tomcat/util/net/jsse/PEMFile.java  | 31 ++++++++++++--
 .../tomcat/util/net/openssl/OpenSSLContext.java    | 17 +++++++-
 test/org/apache/tomcat/util/net/TestSsl.java       | 22 +++++++++-
 test/org/apache/tomcat/util/net/TesterSupport.java | 12 +++++-
 .../apache/tomcat/util/net/jsse/TestPEMFile.java   | 39 +++++++++++++++---
 test/org/apache/tomcat/util/net/jsse/key-password  |  1 +
 test/org/apache/tomcat/util/net/key-password       |  1 +
 test/org/apache/tomcat/util/net/keystore-password  |  1 +
 webapps/docs/changelog.xml                         |  4 ++
 webapps/docs/config/http.xml                       | 20 ++++++++-
 14 files changed, 267 insertions(+), 23 deletions(-)

diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java 
b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
index cc6988d308..9f12ae8ec4 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@@ -1009,6 +1009,17 @@ public abstract class AbstractHttp11Protocol<S> extends 
AbstractProtocol<S> {
     }
 
 
+    public String getKeystorePassFile() {
+        registerDefaultSSLHostConfig();
+        return defaultSSLHostConfig.getCertificateKeystorePasswordFile();
+    }
+
+    public void setKeystorePassFile(String certificateKeystorePasswordFile) {
+        registerDefaultSSLHostConfig();
+        
defaultSSLHostConfig.setCertificateKeystorePasswordFile(certificateKeystorePasswordFile);
+    }
+
+
     public String getKeyPass() {
         registerDefaultSSLHostConfig();
         return defaultSSLHostConfig.getCertificateKeyPassword();
@@ -1019,6 +1030,18 @@ public abstract class AbstractHttp11Protocol<S> extends 
AbstractProtocol<S> {
         defaultSSLHostConfig.setCertificateKeyPassword(certificateKeyPassword);
     }
 
+
+    public String getKeyPassFile() {
+        registerDefaultSSLHostConfig();
+        return defaultSSLHostConfig.getCertificateKeyPasswordFile();
+    }
+
+    public void setKeyPassFile(String certificateKeyPasswordFile) {
+        registerDefaultSSLHostConfig();
+        
defaultSSLHostConfig.setCertificateKeyPasswordFile(certificateKeyPasswordFile);
+    }
+
+
     public String getSSLPassword() {
         registerDefaultSSLHostConfig();
         return defaultSSLHostConfig.getCertificateKeyPassword();
@@ -1030,6 +1053,17 @@ public abstract class AbstractHttp11Protocol<S> extends 
AbstractProtocol<S> {
     }
 
 
+    public String getSSLPasswordFile() {
+        registerDefaultSSLHostConfig();
+        return defaultSSLHostConfig.getCertificateKeyPasswordFile();
+    }
+
+    public void setSSLPasswordFile(String certificateKeyPasswordFile) {
+        registerDefaultSSLHostConfig();
+        
defaultSSLHostConfig.setCertificateKeyPasswordFile(certificateKeyPasswordFile);
+    }
+
+
     public String getCrlFile() {
         registerDefaultSSLHostConfig();
         return defaultSSLHostConfig.getCertificateRevocationListFile();
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java 
b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index b2d944ee9b..af320dfbb6 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -305,12 +305,29 @@ public class SSLHostConfig implements Serializable {
             return defaultCertificate.getCertificateKeyPassword();
         }
     }
+
+
     public void setCertificateKeyPassword(String certificateKeyPassword) {
         registerDefaultCertificate();
         defaultCertificate.setCertificateKeyPassword(certificateKeyPassword);
     }
 
 
+    public String getCertificateKeyPasswordFile() {
+        if (defaultCertificate == null) {
+            return null;
+        } else {
+            return defaultCertificate.getCertificateKeyPasswordFile();
+        }
+    }
+
+
+    public void setCertificateKeyPasswordFile(String 
certificateKeyPasswordFile) {
+        registerDefaultCertificate();
+        
defaultCertificate.setCertificateKeyPasswordFile(certificateKeyPasswordFile);
+    }
+
+
     public void setCertificateRevocationListFile(String 
certificateRevocationListFile) {
         this.certificateRevocationListFile = certificateRevocationListFile;
     }
@@ -586,6 +603,19 @@ public class SSLHostConfig implements Serializable {
     }
 
 
+    public String getCertificateKeystorePasswordFile() {
+        if (defaultCertificate == null) {
+            return null;
+        } else {
+            return defaultCertificate.getCertificateKeystorePasswordFile();
+        }
+    }
+    public void setCertificateKeystorePasswordFile(String 
certificateKeystorePasswordFile) {
+        registerDefaultCertificate();
+        
defaultCertificate.setCertificateKeystorePasswordFile(certificateKeystorePasswordFile);
+    }
+
+
     public String getCertificateKeystoreProvider() {
         if (defaultCertificate == null) {
             return null;
@@ -743,7 +773,7 @@ public class SSLHostConfig implements Serializable {
             if (truststoreFile != null){
                 try {
                     result = SSLUtilBase.getStore(getTruststoreType(), 
getTruststoreProvider(),
-                            getTruststoreFile(), getTruststorePassword());
+                            getTruststoreFile(), getTruststorePassword(), 
null);
                 } catch (IOException ioe) {
                     Throwable cause = ioe.getCause();
                     if (cause instanceof UnrecoverableKeyException) {
@@ -752,7 +782,7 @@ public class SSLHostConfig implements Serializable {
                                 cause);
                         // Re-try
                         result = SSLUtilBase.getStore(getTruststoreType(), 
getTruststoreProvider(),
-                                getTruststoreFile(), null);
+                                getTruststoreFile(), null, null);
                     } else {
                         // Something else went wrong - re-throw
                         throw ioe;
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java 
b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
index ff635bf588..0c0ebb7274 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
@@ -59,10 +59,12 @@ public class SSLHostConfigCertificate implements 
Serializable {
     private final SSLHostConfig sslHostConfig;
     private final Type type;
     private String certificateKeyPassword = null;
+    private String certificateKeyPasswordFile = null;
 
     // JSSE
     private String certificateKeyAlias;
     private String certificateKeystorePassword = DEFAULT_KEYSTORE_PASSWORD;
+    private String certificateKeystorePasswordFile = null;
     private String certificateKeystoreFile = DEFAULT_KEYSTORE_FILE;
     private String certificateKeystoreProvider = DEFAULT_KEYSTORE_PROVIDER;
     private String certificateKeystoreType = DEFAULT_KEYSTORE_TYPE;
@@ -131,6 +133,16 @@ public class SSLHostConfigCertificate implements 
Serializable {
     }
 
 
+    public String getCertificateKeyPasswordFile() {
+        return certificateKeyPasswordFile;
+    }
+
+
+    public void setCertificateKeyPasswordFile(String 
certificateKeyPasswordFile) {
+        this.certificateKeyPasswordFile = certificateKeyPasswordFile;
+    }
+
+
     // JSSE
 
     public void setCertificateKeyAlias(String certificateKeyAlias) {
@@ -171,6 +183,19 @@ public class SSLHostConfigCertificate implements 
Serializable {
     }
 
 
+    public void setCertificateKeystorePasswordFile(String 
certificateKeystorePasswordFile) {
+        sslHostConfig.setProperty(
+                "Certificate.certificateKeystorePasswordFile", 
SSLHostConfig.Type.JSSE);
+        setStoreType("Certificate.certificateKeystorePasswordFile", 
StoreType.KEYSTORE);
+        this.certificateKeystorePasswordFile = certificateKeystorePasswordFile;
+    }
+
+
+    public String getCertificateKeystorePasswordFile() {
+        return certificateKeystorePasswordFile;
+    }
+
+
     public void setCertificateKeystoreProvider(String 
certificateKeystoreProvider) {
         sslHostConfig.setProperty(
                 "Certificate.certificateKeystoreProvider", 
SSLHostConfig.Type.JSSE);
@@ -208,7 +233,7 @@ public class SSLHostConfigCertificate implements 
Serializable {
         if (result == null && storeType == StoreType.KEYSTORE) {
             result = SSLUtilBase.getStore(getCertificateKeystoreType(),
                     getCertificateKeystoreProvider(), 
getCertificateKeystoreFile(),
-                    getCertificateKeystorePassword());
+                    getCertificateKeystorePassword(), 
getCertificateKeystorePasswordFile());
         }
 
         return result;
diff --git a/java/org/apache/tomcat/util/net/SSLUtilBase.java 
b/java/org/apache/tomcat/util/net/SSLUtilBase.java
index b07f4fe7ff..99d214c759 100644
--- a/java/org/apache/tomcat/util/net/SSLUtilBase.java
+++ b/java/org/apache/tomcat/util/net/SSLUtilBase.java
@@ -16,9 +16,12 @@
  */
 package org.apache.tomcat.util.net;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.security.DomainLoadStoreParameter;
 import java.security.Key;
 import java.security.KeyStore;
@@ -182,10 +185,10 @@ public abstract class SSLUtilBase implements SSLUtil {
 
 
     /*
-     * Gets the key- or truststore with the specified type, path, and password.
+     * Gets the key- or truststore with the specified type, path, password and 
password file.
      */
     static KeyStore getStore(String type, String provider, String path,
-            String pass) throws IOException {
+            String pass, String passFile) throws IOException {
 
         KeyStore ks = null;
         InputStream istream = null;
@@ -218,9 +221,21 @@ public abstract class SSLUtilBase implements SSLUtil {
                 // - for JKS or PKCS12 only use null if pass is null
                 //   (because JKS will auto-switch to PKCS12)
                 char[] storePass = null;
-                if (pass != null && (!"".equals(pass) ||
+                String passToUse = null;
+                if (passFile != null) {
+                    try (BufferedReader reader =
+                            new BufferedReader(new InputStreamReader(
+                            
ConfigFileLoader.getSource().getResource(passFile).getInputStream(),
+                                StandardCharsets.UTF_8))) {
+                        passToUse = reader.readLine();
+                    }
+                } else {
+                    passToUse = pass;
+                }
+
+                if (passToUse != null && (!"".equals(passToUse) ||
                         "JKS".equalsIgnoreCase(type) || 
"PKCS12".equalsIgnoreCase(type))) {
-                    storePass = pass.toCharArray();
+                    storePass = passToUse.toCharArray();
                 }
                 KeyStoreUtil.load(ks, istream, storePass);
             }
@@ -279,9 +294,13 @@ public abstract class SSLUtilBase implements SSLUtil {
     public KeyManager[] getKeyManagers() throws Exception {
         String keyAlias = certificate.getCertificateKeyAlias();
         String algorithm = sslHostConfig.getKeyManagerAlgorithm();
+        String keyPassFile = certificate.getCertificateKeyPasswordFile();
         String keyPass = certificate.getCertificateKeyPassword();
         // This has to be here as it can't be moved to SSLHostConfig since the
         // defaults vary between JSSE and OpenSSL.
+        if (keyPassFile == null) {
+            keyPassFile = certificate.getCertificateKeystorePasswordFile();
+        }
         if (keyPass == null) {
             keyPass = certificate.getCertificateKeystorePassword();
         }
@@ -300,8 +319,22 @@ public abstract class SSLUtilBase implements SSLUtil {
          * required key works around that.
          * Other keys stores (hardware, MS, etc.) will be used as is.
          */
+        char[] keyPassArray = null;
+        String keyPassToUse = null;
+        if (keyPassFile != null) {
+            try (BufferedReader reader =
+                    new BufferedReader(new InputStreamReader(
+                    
ConfigFileLoader.getSource().getResource(keyPassFile).getInputStream(),
+                        StandardCharsets.UTF_8))) {
+                keyPassToUse = reader.readLine();
+            }
+        } else {
+            keyPassToUse = keyPass;
+        }
 
-        char[] keyPassArray = keyPass.toCharArray();
+        if (keyPassToUse != null) {
+            keyPassArray = keyPassToUse.toCharArray();
+        }
 
         KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
         if (kmf.getProvider().getInfo().contains("FIPS")) {
@@ -320,7 +353,7 @@ public abstract class SSLUtilBase implements SSLUtil {
 
             PEMFile privateKeyFile = new PEMFile(
                     certificate.getCertificateKeyFile() != null ? 
certificate.getCertificateKeyFile() : certificate.getCertificateFile(),
-                    keyPass);
+                    keyPass, keyPassFile, null);
             PEMFile certificateFile = new 
PEMFile(certificate.getCertificateFile());
 
             Collection<Certificate> chain = new 
ArrayList<>(certificateFile.getCertificates());
@@ -336,7 +369,7 @@ public abstract class SSLUtilBase implements SSLUtil {
             // Switch to in-memory key store
             ksUsed = KeyStore.getInstance("JKS");
             ksUsed.load(null,  null);
-            ksUsed.setKeyEntry(keyAlias, privateKeyFile.getPrivateKey(), 
keyPass.toCharArray(),
+            ksUsed.setKeyEntry(keyAlias, privateKeyFile.getPrivateKey(), 
keyPassArray,
                     chain.toArray(new Certificate[0]));
         } else {
             if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
diff --git a/java/org/apache/tomcat/util/net/jsse/PEMFile.java 
b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
index c374e402e6..bd5ec514c4 100644
--- a/java/org/apache/tomcat/util/net/jsse/PEMFile.java
+++ b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
@@ -102,15 +102,30 @@ public class PEMFile {
         this(filename, 
ConfigFileLoader.getSource().getResource(filename).getInputStream(), password, 
keyAlgorithm);
     }
 
+    public PEMFile(String filename, String password, String passwordFilename, 
String keyAlgorithm)
+            throws IOException, GeneralSecurityException {
+        this(filename, 
ConfigFileLoader.getSource().getResource(filename).getInputStream(), password,
+             passwordFilename, passwordFilename != null ? 
ConfigFileLoader.getSource().getResource(passwordFilename).getInputStream() : 
null,
+             keyAlgorithm);
+    }
+
+    public PEMFile(String filename, InputStream fileStream, String password, 
String keyAlgorithm)
+            throws IOException, GeneralSecurityException {
+        this(filename, fileStream, password, null, null, keyAlgorithm);
+    }
+
     /**
      * @param filename the filename to mention in error messages, not used for 
anything else.
      * @param fileStream the stream containing the pem(s).
      * @param password password to load the pem objects.
+     * @param passwordFilename the password filename to mention in error 
messages, not used for anything else.
+     * @param passwordFileStream stream containing the password to load the 
pem objects.
      * @param keyAlgorithm the algorithm to help to know how to load the 
objects (guessed if null).
      * @throws IOException if input can't be read.
      * @throws GeneralSecurityException if input can't be parsed/loaded.
      */
-    public PEMFile(String filename, InputStream fileStream, String password, 
String keyAlgorithm)
+    public PEMFile(String filename, InputStream fileStream, String password, 
String passwordFilename,
+                   InputStream passwordFileStream, String keyAlgorithm)
             throws IOException, GeneralSecurityException {
         List<Part> parts = new ArrayList<>();
         try (BufferedReader reader =
@@ -141,6 +156,16 @@ public class PEMFile {
             }
         }
 
+        String passwordToUse = null;
+        if (passwordFileStream != null) {
+            try (BufferedReader reader =
+                    new BufferedReader(new 
InputStreamReader(passwordFileStream, StandardCharsets.UTF_8))) {
+                passwordToUse = reader.readLine();
+            }
+        } else {
+            passwordToUse = password;
+        }
+
         for (Part part : parts) {
             switch (part.type) {
                 case Part.PRIVATE_KEY:
@@ -150,7 +175,7 @@ public class PEMFile {
                     privateKey = part.toPrivateKey(null, "EC", Format.RFC5915, 
filename);
                     break;
                 case Part.ENCRYPTED_PRIVATE_KEY:
-                    privateKey = part.toPrivateKey(password, keyAlgorithm, 
Format.PKCS8, filename);
+                    privateKey = part.toPrivateKey(passwordToUse, 
keyAlgorithm, Format.PKCS8, filename);
                     break;
                 case Part.RSA_PRIVATE_KEY:
                     if (part.algorithm == null) {
@@ -158,7 +183,7 @@ public class PEMFile {
                         // (probably default) key password provided.
                         privateKey = part.toPrivateKey(null, keyAlgorithm, 
Format.PKCS1, filename);
                     } else {
-                        privateKey = part.toPrivateKey(password, keyAlgorithm, 
Format.PKCS1, filename);
+                        privateKey = part.toPrivateKey(passwordToUse, 
keyAlgorithm, Format.PKCS1, filename);
                     }
                     break;
                 case Part.CERTIFICATE:
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java 
b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
index c05b816de8..5d8ca84d6c 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
@@ -16,6 +16,9 @@
  */
 package org.apache.tomcat.util.net.openssl;
 
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.SecureRandom;
@@ -464,10 +467,22 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
         // Load Server key and certificate
         if (certificate.getCertificateFile() != null) {
             // Set certificate
+            String passwordToUse = null;
+            if (certificate.getCertificateKeyPasswordFile() != null) {
+                try (BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(
+                            new FileInputStream(
+                                
SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyPasswordFile())),
+                                StandardCharsets.UTF_8))) {
+                    passwordToUse = reader.readLine();
+                }
+            } else {
+                passwordToUse = certificate.getCertificateKeyPassword();
+            }
             SSLContext.setCertificate(ctx,
                     
SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
                     
SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()),
-                    certificate.getCertificateKeyPassword(), 
getCertificateIndex(certificate));
+                    passwordToUse, getCertificateIndex(certificate));
             // Set certificate chain file
             SSLContext.setCertificateChainFile(ctx,
                     
SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false);
diff --git a/test/org/apache/tomcat/util/net/TestSsl.java 
b/test/org/apache/tomcat/util/net/TestSsl.java
index f6f25d122c..fc7cd5fafa 100644
--- a/test/org/apache/tomcat/util/net/TestSsl.java
+++ b/test/org/apache/tomcat/util/net/TestSsl.java
@@ -173,7 +173,7 @@ public class TestSsl extends TomcatBaseTest {
         ctxt.addApplicationListener(WsContextListener.class.getName());
 
         TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS,
-                TesterSupport.JKS_PASS, TesterSupport.JKS_KEY_PASS);
+                TesterSupport.JKS_PASS, null, TesterSupport.JKS_KEY_PASS, 
null);
 
         tomcat.start();
         ByteChunk res = getUrl("https://localhost:"; + getPort() +
@@ -183,6 +183,26 @@ public class TestSsl extends TomcatBaseTest {
                 TesterSupport.getLastClientAuthRequestedIssuerCount() == 0);
     }
 
+    @Test
+    public void testKeyPassFile() throws Exception {
+        TesterSupport.configureClientSsl();
+
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File(getBuildDirectory(), "webapps/examples");
+        Context ctxt  = tomcat.addWebapp(null, "/examples", 
appDir.getAbsolutePath());
+        ctxt.addApplicationListener(WsContextListener.class.getName());
+
+        TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS,
+                      null, TesterSupport.JKS_PASS_FILE, null, 
TesterSupport.JKS_KEY_PASS_FILE);
+
+        tomcat.start();
+        ByteChunk res = getUrl("https://localhost:"; + getPort() +
+            "/examples/servlets/servlet/HelloWorldExample");
+        Assert.assertTrue(res.toString().indexOf("<a 
href=\"../helloworld.html\">") > 0);
+        Assert.assertTrue("Checking no client issuer has been requested",
+                TesterSupport.getLastClientAuthRequestedIssuerCount() == 0);
+    }
 
     @Test
     public void testRenegotiateWorks() throws Exception {
diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java 
b/test/org/apache/tomcat/util/net/TesterSupport.java
index d4b14a6ec6..4e9838698d 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -77,7 +77,9 @@ public final class TesterSupport {
     public static final String LOCALHOST_RSA_JKS = SSL_DIR + 
"localhost-rsa.jks";
     public static final String LOCALHOST_KEYPASS_JKS = SSL_DIR + 
"localhost-rsa-copy1.jks";
     public static final String JKS_PASS = "changeit";
+    public static final String JKS_PASS_FILE = SSL_DIR + "keystore-password";
     public static final String JKS_KEY_PASS = "tomcatpass";
+    public static final String JKS_KEY_PASS_FILE = SSL_DIR + "key-password";
     public static final String CA_CERT_PEM = SSL_DIR + CA_ALIAS + "-cert.pem";
     public static final String LOCALHOST_EC_CERT_PEM = SSL_DIR + 
"localhost-ec-cert.pem";
     public static final String LOCALHOST_EC_KEY_PEM = SSL_DIR + 
"localhost-ec-key.pem";
@@ -132,11 +134,11 @@ public final class TesterSupport {
     }
 
     public static void initSsl(Tomcat tomcat) {
-        initSsl(tomcat, LOCALHOST_RSA_JKS, null, null);
+        initSsl(tomcat, LOCALHOST_RSA_JKS, null, null, null, null);
     }
 
     protected static void initSsl(Tomcat tomcat, String keystore,
-            String keystorePass, String keyPass) {
+            String keystorePass, String keystorePassFile, String keyPass, 
String keyPassFile) {
 
         Connector connector = tomcat.getConnector();
         connector.setSecure(true);
@@ -160,9 +162,15 @@ public final class TesterSupport {
             sslHostConfig.setSslProtocol("tls");
             certificate.setCertificateKeystoreFile(new 
File(keystore).getAbsolutePath());
             sslHostConfig.setTruststoreFile(new 
File(CA_JKS).getAbsolutePath());
+            if (keystorePassFile != null) {
+                certificate.setCertificateKeystorePasswordFile(new 
File(keystorePassFile).getAbsolutePath());
+            }
             if (keystorePass != null) {
                 certificate.setCertificateKeystorePassword(keystorePass);
             }
+            if (keyPassFile != null) {
+                certificate.setCertificateKeyPasswordFile(new 
File(keyPassFile).getAbsolutePath());
+            }
             if (keyPass != null) {
                 certificate.setCertificateKeyPassword(keyPass);
             }
diff --git a/test/org/apache/tomcat/util/net/jsse/TestPEMFile.java 
b/test/org/apache/tomcat/util/net/jsse/TestPEMFile.java
index 2ee54e26e1..6dd125c7a8 100644
--- a/test/org/apache/tomcat/util/net/jsse/TestPEMFile.java
+++ b/test/org/apache/tomcat/util/net/jsse/TestPEMFile.java
@@ -19,13 +19,21 @@ package org.apache.tomcat.util.net.jsse;
 import java.io.File;
 import java.io.IOException;
 import java.security.PrivateKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 
+@RunWith(Parameterized.class)
 public class TestPEMFile {
 
     private static final String KEY_PASSWORD = "changeit";
+    private static final String KEY_PASSWORD_FILE = "key-password";
 
     private static final String KEY_PKCS1 = "key-pkcs1.pem";
     private static final String KEY_ENCRYPTED_PKCS1_DES_CBC = 
"key-encrypted-pkcs1-des-cbc.pem";
@@ -33,16 +41,34 @@ public class TestPEMFile {
     private static final String KEY_ENCRYPTED_PKCS1_AES256 = 
"key-encrypted-pkcs1-aes256.pem";
     private static final String KEY_ENCRYPTED_PKCS8 = 
"key-encrypted-pkcs8.pem";
 
+    @Parameterized.Parameters
+    public static Collection<Object[]> parameters() {
+        List<Object[]> parameterSets = new ArrayList<>();
+
+        parameterSets.add(new Object[] { KEY_PASSWORD, null });
+        parameterSets.add(new Object[] { null, KEY_PASSWORD_FILE });
+        parameterSets.add(new Object[] { KEY_PASSWORD, KEY_PASSWORD_FILE });
+
+        return parameterSets;
+    }
+
+
+    @Parameter(0)
+    public String password;
+
+    @Parameter(1)
+    public String passwordFile;
+
 
     @Test
     public void testKeyPkcs1() throws Exception {
-        testKey(KEY_PKCS1, null);
+        testKey(KEY_PKCS1, null, null);
     }
 
 
     @Test
     public void testKeyPkcs1WithUnnecessaryPassword() throws Exception {
-        testKey(KEY_PKCS1, "ignore-me");
+        testKey(KEY_PKCS1, "ignore-me", null);
     }
 
 
@@ -71,18 +97,21 @@ public class TestPEMFile {
 
 
     private void testKeyEncrypted(String file) throws Exception {
-        testKey(file, KEY_PASSWORD);
+        testKey(file, password, passwordFile);
     }
 
 
-    private void testKey(String file, String password) throws Exception {
-        PEMFile pemFile = new PEMFile(getPath(file), password);
+    private void testKey(String file, String password, String passwordFile) 
throws Exception {
+        PEMFile pemFile = new PEMFile(getPath(file), password, 
getPath(passwordFile), null);
         PrivateKey pk = pemFile.getPrivateKey();
         Assert.assertNotNull(pk);
     }
 
 
     private String getPath(String file) throws IOException {
+        if (file == null) {
+            return null;
+        }
         String packageName = this.getClass().getPackage().getName();
         String path = packageName.replace(".", File.separator);
         File f = new File("test" + File.separator + path + File.separator + 
file);
diff --git a/test/org/apache/tomcat/util/net/jsse/key-password 
b/test/org/apache/tomcat/util/net/jsse/key-password
new file mode 100644
index 0000000000..5bbaf87581
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/jsse/key-password
@@ -0,0 +1 @@
+changeit
\ No newline at end of file
diff --git a/test/org/apache/tomcat/util/net/key-password 
b/test/org/apache/tomcat/util/net/key-password
new file mode 100644
index 0000000000..05557f9168
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/key-password
@@ -0,0 +1 @@
+tomcatpass
\ No newline at end of file
diff --git a/test/org/apache/tomcat/util/net/keystore-password 
b/test/org/apache/tomcat/util/net/keystore-password
new file mode 100644
index 0000000000..5bbaf87581
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/keystore-password
@@ -0,0 +1 @@
+changeit
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 0075fdbcd4..2a908b046a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -107,6 +107,10 @@
 <section name="Tomcat 9.0.83 (remm)" rtext="in development">
   <subsection name="Catalina">
     <changelog>
+      <add>
+        <bug>66670</bug>: Add 
<code>SSLHostConfig#certificateKeyPasswordFile</code> and
+        <code>SSLHostConfig#certificateKeystorePasswordFile</code>. (michaelo)
+      </add>
       <fix>
         <bug>67667</bug>: <code>TLSCertificateReloadListener</code> prints 
unreadable
         rendering of <code>X509Certificate#getNotAfter()</code>. (michaelo)
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index d856f40aa3..cb12d17f4b 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1603,7 +1603,18 @@
       certificate from the specified file.</p>
       <p>If not specified, the default behaviour for JSSE is to use the
       <strong>certificateKeystorePassword</strong>. For OpenSSL the default
-      behaviour is not to use a password.</p>
+      behaviour is not to use a password, but OpenSSL will prompt for one,
+      if required.</p>
+    </attribute>
+
+    <attribute name="certificateKeyPasswordFile" required="false">
+      <p>The password file used to access the private key associated with the 
server
+      certificate from the specified file. This attribute takes precedence over
+      <strong>certificateKeyPassword</strong>.</p>
+      <p>If not specified, the default behaviour for JSSE is to use the
+      <strong>certificateKeystorePasswordFile</strong>. For OpenSSL the default
+      behaviour is not to use a password (file), but OpenSSL will prompt for 
one,
+      if required.</p>
     </attribute>
 
     <attribute name="certificateKeystoreFile" required="false">
@@ -1629,6 +1640,13 @@
       <code>changeit</code> will be used.</p>
     </attribute>
 
+    <attribute name="certificateKeystorePasswordFile" required="false">
+      <p>JSSE only.</p>
+      <p>The password file to use to access the keystore containing the 
server&apos;s
+      private key and certificate. This attribute takes precedence over
+      <strong>certificateKeystorePassword</strong>.</p>
+    </attribute>
+
     <attribute name="certificateKeystoreProvider" required="false">
       <p>JSSE only.</p>
       <p>The name of the keystore provider to be used for the server


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to