This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 20e3d21601eae29f875d76f164fce3be161bb31d 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 | 7 +++ 7 files changed, 85 insertions(+), 4 deletions(-) diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java index 0db6a6206c..0686b516a5 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; @@ -42,6 +46,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; @@ -386,8 +391,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(); } @@ -1397,6 +1444,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 ab8767b10b..5ad2076cbd 100644 --- a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java +++ b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java @@ -108,8 +108,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 5c5b61fd3b..b7ea79c56f 100644 --- a/java/org/apache/tomcat/util/net/AprEndpoint.java +++ b/java/org/apache/tomcat/util/net/AprEndpoint.java @@ -84,6 +84,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 @@ -408,8 +409,8 @@ public class AprEndpoint extends AbstractEndpoint<Long,Long> implements SNICallB sslContext.addCertificate(certificate); } - logCertificate(certificate); certificate.setSslContext(sslContext); + logCertificate(certificate); } if (certificates.size() > 2) { @@ -866,6 +867,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 ea7bdc04f7..7629f62dee 100644 --- a/java/org/apache/tomcat/util/net/LocalStrings.properties +++ b/java/org/apache/tomcat/util/net/LocalStrings.properties @@ -127,6 +127,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 e453fc67e0..4600348320 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"); @@ -401,6 +402,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 74e75d744b..f5696b1284 100644 --- a/java/org/apache/tomcat/util/net/NioEndpoint.java +++ b/java/org/apache/tomcat/util/net/NioEndpoint.java @@ -75,6 +75,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"); @@ -477,6 +478,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 83fb26edbe..d1bf1cce2b 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -151,6 +151,13 @@ <code>WINDOW_UPDATE</code> frame on an HTTP/2 connection where the flow control window for the overall connection has been exhausted. (markt) </fix> + <add> + Provided dedicated loggers + (<code>org.apache.tomcat.util.net.NioEndpoint.certificate</code> / + <code>org.apache.tomcat.util.net.Nio2Endpoint.certificate</code> / + <code>org.apache.tomcat.util.net.AprEndpoint.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