【问题标题】:How do I force tomcat to reload trusted certificates?如何强制 tomcat 重新加载受信任的证书?
【发布时间】:2011-04-28 09:09:20
【问题描述】:

我的 WebApp 使用 2-Way SSL 连接器(又名“客户端身份验证”):

<Connector port="8084" SSLEnabled="true" maxThreads="10" minSpareThreads="3" maxSpareThreads="5"
             enableLookups="false" disableUploadTimeout="true" acceptCount="100" scheme="https" secure="true"
             clientAuth="true" truststoreFile="conf/keystore.kst" truststoreType="JCEKS" sslProtocol="TLS" URIEncoding="UTF-8"
             keystoreFile="conf/keystore.kst" keystoreType="JCEKS" keyAlias="myAlias"
             ciphers="TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_3DES_EDE_CBC_SHA,SSL_RSA_WITH_3DES_EDE_CBC_SHA"/>

我的问题是,当 Tomcat 服务器正在运行时,我使用新的受信任证书更新密钥库,甚至从中删除受信任的证书,连接器不会注意到这些更改。

到目前为止我已经尝试过:

1) 停止、重新初始化(反射)和启动连接器 - 不起作用。

2) 实现我自己的 SSLContext,从密钥库重新加载证书。 好吧,这里我错过了用 tomcat 注册这个 SSLContext 的部分(以便 tomcat 将在连接器中使用它来进行新的传入连接)

关于这个问题有很多帖子,但没有真正的解决方案:

http://www.delphifaq.com/faq/f5003.shtml

http://jcalcote.wordpress.com/tag/truststore
(本文仅介绍如何从客户端重新创建 SSLcontext(缺少服务器端))

有什么想法吗?

还有一个相关的问题:

How do I force a tomcat web application reload the trust store after I update it

但答案还不够,因为我不想构建新的 ClassLoader。

谢谢。

【问题讨论】:

    标签: java security tomcat ssl keystore


    【解决方案1】:

    现在有一个从 Tomcat v8.5.24 开始的解决方案。

    他们引入了 2 种方法,命名为: reloadSslHostConfig(String hostName) - 重新加载特定主机 reloadSslHostConfigs() - 重新加载所有

    可以通过多种方式调用它们:

    1. 使用 jmx
    2. 使用经理服务
    3. 通过制作自定义协议 - 我在研究期间发现了这种方式

    方式 1 和方式 2 的详细信息可轻松在线获得。

    方式3的具体使用方法:

    1. 创建一个类来扩展您选择的协议,例如。 Http11NioProtocol
    2. 覆盖所需的方法并在其中调用 super 以保持默认行为
    3. 在此类中创建一个线程以不时调用 reloadSslHostConfigs 方法
    4. 把这个类打包到一个jar中,然后把那个jar放到tomcat的lib文件夹中
    5. 在 server.xml 的连接器中编辑协议以使用此自定义协议

    在下面找到示例代码:

    主协议类:

        package com.myown.connector;
    
        import java.io.File;
        import java.io.InputStream;
        import java.lang.reflect.Field;
        import java.net.URL;
        import java.net.URLConnection;
        import java.nio.file.StandardCopyOption;
        import java.util.ArrayList;
        import java.util.List;
        import java.util.concurrent.ConcurrentMap;
    
        import javax.management.MalformedObjectNameException;
        import javax.management.ObjectName;
        import javax.net.ssl.SSLSessionContext;
    
        import org.apache.coyote.http11.Http11NioProtocol;
        import org.apache.juli.logging.Log;
        import org.apache.juli.logging.LogFactory;
        import org.apache.tomcat.util.modeler.Registry;
        import org.apache.tomcat.util.net.AbstractEndpoint;
        import org.apache.tomcat.util.net.AbstractJsseEndpoint;
        import org.apache.tomcat.util.net.GetSslConfig;
        import org.apache.tomcat.util.net.SSLContext;
        import org.apache.tomcat.util.net.SSLHostConfig;
        import org.apache.tomcat.util.net.SSLHostConfigCertificate;
        import org.apache.tomcat.util.net.SSLImplementation;
        import org.apache.tomcat.util.net.SSLUtil;
    
        public class ReloadProtocol extends Http11NioProtocol {
    
            private static final Log log = LogFactory.getLog(Http12ProtocolSSL.class);
    
            public ReloadProtocol() {
                super();
                RefreshSslConfigThread refresher = new 
                      RefreshSslConfigThread(this.getEndpoint(), this);
                refresher.start();
            }
    
            @Override
            public void setKeystorePass(String s) {
                super.setKeystorePass(s);
            }
    
            @Override
            public void setKeyPass(String s) {
                super.setKeyPass(s);
            }
    
            @Override
            public void setTruststorePass(String p) {
                super.setTruststorePass(p);
            }
    
            class RefreshSslConfigThread extends Thread {
    
                AbstractJsseEndpoint<?> abstractJsseEndpoint = null;
                Http11NioProtocol protocol = null;
    
                public RefreshSslConfigThread(AbstractJsseEndpoint<?> abstractJsseEndpoint, Http11NioProtocol protocol) {
                    this.abstractJsseEndpoint = abstractJsseEndpoint;
                    this.protocol = protocol;
                }
    
                public void run() {
                    int timeBetweenRefreshesInt = 1000000; // time in milli-seconds
                    while (true) {
                        try {
                                abstractJsseEndpoint.reloadSslHostConfigs();
                                System.out.println("Config Updated");
                        } catch (Exception e) {
                            System.out.println("Problem while reloading.");
                        }
                        try {
                            Thread.sleep(timeBetweenRefreshesInt);
                        } catch (InterruptedException e) {
                            System.out.println("Error while sleeping");
                        }
                    }
                }
           }
    }
    

    server.xml 中的连接器应将此作为协议提及:

    <Connector protocol="com.myown.connector.ReloadProtocol"
     ..........
    

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      如果您的连接器将 bindOnInit 属性设置为 false(从 Tomcat 6.x 开始存在),则:

      控制连接器使用的套接字何时被绑定。默认情况下它 连接器启动时绑定,连接器启动时解除绑定 连接器被破坏。如果设置为 false,则套接字将被绑定 连接器在停止时启动并解除绑定。

      来自 org.apache.tomcat.util.net.AbstractEndpoint Tomcat 8.0.29 的代码 sn-ps:

      public final void start() throws Exception {
          if (bindState == BindState.UNBOUND) {
              bind();
              bindState = BindState.BOUND_ON_START;
          }
          startInternal();
      }
      
      public final void stop() throws Exception {
          stopInternal();
          if (bindState == BindState.BOUND_ON_START) {
              unbind();
              bindState = BindState.UNBOUND;
          }
      }
      

      然后您可以在更新您的密钥和信任存储后通过 JMX 停止和启动连接器。

      【讨论】:

        【解决方案3】:

        Tomcat HTTP/1.1 协议处理程序可以重新加载密钥库。

        如果您使用嵌入式 Tomcat 或通过某种方式访问​​ Tomcat 连接器,那么您要求协议处理程序按需重新加载密钥库和信任库,而无需重新启动连接器。

        void addConnector(TomcatEmbedded tomcatEmbedded) {
            // Create a connector using the HTTP/1.1 protocol handler
            Connector connector = new Connector("HTTP/1.1");
            connector.setPort(8080);
        
            tomcatEmbedded.addConnector(connector);
        }
        
        
        void reload(Connector connector) {
            ProtocolHandler protocolHandler = connector.getProtocolHandler();
            if (protocolHandler instanceof Http11NioProtocol) {
                Http11NioProtocol http11NioProtocol = (Http11NioProtocol)protocolHandler;
                // reload the key and trust stores
                http11NioProtocol.reloadSslHostConfigs();
            }
        }
        

        【讨论】:

        • 欢迎来到 StackOverflow!请为您的代码添加解释。这将提高您回答的质量,使其对以后访问该网站的访问者更有用。
        猜你喜欢
        • 2012-03-08
        • 2023-03-26
        • 2011-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-25
        • 2020-02-13
        相关资源
        最近更新 更多