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

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

commit 497f18240318e623ae608f8d4bdcbb366e2eab54
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Feb 16 13:57:55 2023 +0000

    Add dedicated logger for TLS certifcates
    
    Allows debug logging to be enabled just for certificates
---
 .../apache/tomcat/util/net/AbstractEndpoint.java   | 55 +++++++++++++++++++++-
 .../tomcat/util/net/AbstractJsseEndpoint.java      |  2 +-
 java/org/apache/tomcat/util/net/AprEndpoint.java   |  9 +++-
 .../apache/tomcat/util/net/LocalStrings.properties |  2 +
 java/org/apache/tomcat/util/net/Nio2Endpoint.java  |  7 +++
 java/org/apache/tomcat/util/net/NioEndpoint.java   |  7 +++
 webapps/docs/changelog.xml                         |  6 +++
 7 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java 
b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index 87436023b6..2a14da3d45 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -22,6 +22,10 @@ import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -44,6 +48,7 @@ import javax.management.ObjectName;
 import org.apache.juli.logging.Log;
 import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.buf.HexUtils;
 import org.apache.tomcat.util.collections.SynchronizedStack;
 import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.net.Acceptor.AcceptorState;
@@ -388,8 +393,50 @@ public abstract class AbstractEndpoint<S,U> {
             trustStoreSource = sslHostConfig.getCaCertificatePath();
         }
 
-        getLog().info(sm.getString("endpoint.tls.info", getName(), 
sslHostConfig.getHostName(), certificate.getType(),
-                certificateSource, keyAlias, trustStoreSource));
+        getLogCertificate().info(sm.getString("endpoint.tls.info", getName(), 
sslHostConfig.getHostName(),
+                certificate.getType(), certificateSource, keyAlias, 
trustStoreSource));
+
+        if (getLogCertificate().isDebugEnabled()) {
+            String alias = certificate.getCertificateKeyAlias();
+            if (alias == null) {
+                alias = SSLUtilBase.DEFAULT_KEY_ALIAS;
+            }
+            X509Certificate[] x509Certificates = 
certificate.getSslContext().getCertificateChain(alias);
+            if (x509Certificates != null && x509Certificates.length > 0) {
+                
getLogCertificate().debug(generateCertificateDebug(x509Certificates[0]));
+            } else {
+                
getLogCertificate().debug(sm.getString("endpoint.tls.cert.noCerts"));
+            }
+        }
+    }
+
+
+    protected String generateCertificateDebug(X509Certificate certificate) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("\n[");
+        try {
+            byte[] certBytes = certificate.getEncoded();
+            // SHA-256 fingerprint
+            sb.append("\nSHA-256 fingerprint: ");
+            MessageDigest sha512Digest = MessageDigest.getInstance("SHA-256");
+            sha512Digest.update(certBytes);
+            sb.append(HexUtils.toHexString(sha512Digest.digest()));
+            // SHA-256 fingerprint
+            sb.append("\nSHA-1 fingerprint: ");
+            MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
+            sha1Digest.update(certBytes);
+            sb.append(HexUtils.toHexString(sha1Digest.digest()));
+        } catch (CertificateEncodingException e) {
+            
getLogCertificate().warn(sm.getString("endpoint.tls.cert.encodingError"), e);
+        } catch (NoSuchAlgorithmException e) {
+            // Unreachable code
+            // All JREs are required to support SHA-1 and SHA-256
+            throw new RuntimeException(e);
+        }
+        sb.append("\n");
+        sb.append(certificate);
+        sb.append("\n]");
+        return sb.toString();
     }
 
 
@@ -1403,6 +1450,10 @@ public abstract class AbstractEndpoint<S,U> {
 
     protected abstract Log getLog();
 
+    protected Log getLogCertificate() {
+        return getLog();
+    }
+
     protected LimitLatch initializeConnectionLatch() {
         if (maxConnections==-1) {
             return null;
diff --git a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java 
b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
index a363bef182..4732026c26 100644
--- a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
@@ -107,8 +107,8 @@ public abstract class AbstractJsseEndpoint<S,U> extends 
AbstractEndpoint<S,U> {
                 throw new IllegalArgumentException(e.getMessage(), e);
             }
 
-            logCertificate(certificate);
             certificate.setSslContext(sslContext);
+            logCertificate(certificate);
         }
     }
 
diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java 
b/java/org/apache/tomcat/util/net/AprEndpoint.java
index 150e7b3915..11ac0d901c 100644
--- a/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -90,6 +90,7 @@ public class AprEndpoint extends AbstractEndpoint<Long,Long> 
implements SNICallB
     // -------------------------------------------------------------- Constants
 
     private static final Log log = LogFactory.getLog(AprEndpoint.class);
+    private static final Log logCertificate = 
LogFactory.getLog(AprEndpoint.class.getName() + ".certificate");
 
     // ----------------------------------------------------------------- Fields
 
@@ -474,8 +475,8 @@ public class AprEndpoint extends 
AbstractEndpoint<Long,Long> implements SNICallB
                 sslContext.addCertificate(certificate);
             }
 
-            logCertificate(certificate);
             certificate.setSslContext(sslContext);
+            logCertificate(certificate);
         }
 
         if (certificates.size() > 2) {
@@ -932,6 +933,12 @@ public class AprEndpoint extends 
AbstractEndpoint<Long,Long> implements SNICallB
         return log;
     }
 
+    @Override
+    protected Log getLogCertificate() {
+        return logCertificate;
+    }
+
+
     // -------------------------------------------------- SocketInfo Inner 
Class
 
     public static class SocketInfo {
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties 
b/java/org/apache/tomcat/util/net/LocalStrings.properties
index fc7150cd74..b09b0b0094 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -131,6 +131,8 @@ endpoint.setAttribute=Set [{0}] to [{1}]
 endpoint.setAttributeError=Unable to set attribute [{0}] to [{1}]
 endpoint.socketOptionsError=Error setting socket options
 endpoint.timeout.err=Error processing socket timeout
+endpoint.tls.cert.encodingError=Certificate fingerprints not available
+endpoint.tls.cert.noCerts=Certificate details not available as the certificate 
chain returned from the SSLContext was empty
 endpoint.tls.info=Connector [{0}], TLS virtual host [{1}], certificate type 
[{2}] configured from [{3}] using alias [{4}] and with trust store [{5}]
 endpoint.unknownSslHostName=The SSL host name [{0}] is not recognised for this 
endpoint
 endpoint.warn.executorShutdown=The executor associated with thread pool [{0}] 
has not fully shutdown. Some application threads may still be running.
diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java 
b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
index 5386b6e0bd..7eb12b710f 100644
--- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
+++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
@@ -59,6 +59,7 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
 
 
     private static final Log log = LogFactory.getLog(Nio2Endpoint.class);
+    private static final Log logCertificate = 
LogFactory.getLog(Nio2Endpoint.class.getName() + ".certificate");
     private static final Log logHandshake = 
LogFactory.getLog(Nio2Endpoint.class.getName() + ".handshake");
 
 
@@ -398,6 +399,12 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
     }
 
 
+    @Override
+    protected Log getLogCertificate() {
+        return logCertificate;
+    }
+
+
     @Override
     protected SocketProcessorBase<Nio2Channel> createSocketProcessor(
             SocketWrapperBase<Nio2Channel> socketWrapper, SocketEvent event) {
diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java 
b/java/org/apache/tomcat/util/net/NioEndpoint.java
index e30f4a7c2d..915a057297 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -83,6 +83,7 @@ public class NioEndpoint extends 
AbstractJsseEndpoint<NioChannel,SocketChannel>
 
 
     private static final Log log = LogFactory.getLog(NioEndpoint.class);
+    private static final Log logCertificate = 
LogFactory.getLog(NioEndpoint.class.getName() + ".certificate");
     private static final Log logHandshake = 
LogFactory.getLog(NioEndpoint.class.getName() + ".handshake");
 
 
@@ -568,6 +569,12 @@ public class NioEndpoint extends 
AbstractJsseEndpoint<NioChannel,SocketChannel>
     }
 
 
+    @Override
+    protected Log getLogCertificate() {
+        return logCertificate;
+    }
+
+
     @Override
     protected SocketProcessorBase<NioChannel> createSocketProcessor(
             SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index bd48a8bace..b1b4f0b15f 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -156,6 +156,12 @@
         from timing out when using a Connector configured with
         <code>useAsyncIO=true</code> (the default for NIO and NIO2). (markt)
       </fix>
+      <add>
+        Provided dedicated loggers
+        (<code>org.apache.tomcat.util.net.NioEndpoint.certificate</code> /
+        <code>org.apache.tomcat.util.net.Nio2Endpoint.certificate</code>) for
+        logging of configured TLS certificates. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">


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

Reply via email to