java - 如何强制 tomcat 重新加载受信任的证书?

标签 java security tomcat ssl keystore

我的 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 服务器正在运行并且我用新的可信证书更新 keystore ,甚至从中删除可信证书时,连接器没有注意到这些变化。

到目前为止我尝试了什么:

1) 停止、重新初始化(反射)和启动连接器 - 无效。

2) 实现我自己的 SSLContext,从 keystore 重新加载证书。 好吧,这里我遗漏了向 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。

谢谢。

最佳答案

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

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

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

  1. 使用jmx
  2. 使用经理服务
  3. 通过制定自定义协议(protocol) - 我在研究过程中发现了这种方式

方式 1 和方式 2 的详细信息很容易在线获得。

关于如何使用方式 3 的详细信息:

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

在下面找到示例代码:

主要协议(protocol)类:

    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 中的连接器应该将此作为协议(protocol)提及:

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

希望这对您有所帮助。

关于java - 如何强制 tomcat 重新加载受信任的证书?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5816239/

相关文章:

java - 如何在 eclipse 导出到 war 时将所有外部 jar 添加到库

java - 从本地网络 (WLAN) 中的 Android 设备调用 Tomcat 实例

java - 网址无法转换为字符串

java - 在java中读取带有数组的配置文件

security - 防止将 Telegram 机器人添加到任何组或 channel (允许将其添加到白名单组/ channel )

php - Codeigniter MySQL 安全性、htmlspecialcharacters 和 mysql_real_escape_string?

Java/Tomcat : ServletContext & getResourceAsStream Problems

java - Android单元测试onclick方法

java - Camel路由测试错误: IllegalArgumentException: Data format 'jaxb' could not be created

sql-server - 防止对某些表执行 SQL Server 表操作(INSERT 和 DELETE)