How do I force tomcat to reload trusted certificates?

試著忘記壹切 提交于 2021-02-19 03:22:47

问题


My WebApp uses a Connector for 2-Way SSL (aka "Client Authentication"):

<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"/>

My problem is that while the Tomcat server is running and I update the keystore with new trusted certifictaes , or even delete trusted certificates from it , the connector doesn't notice the changes.

What I've tried so far:

1) Stopping , Re-Initializing (reflection) and starting the Connector - didn't work.

2) Implementing my own SSLContext that reloads the certificates from the keystore. Well , here I'm missing the part of registering this SSLContext with tomcat (so that tomcat will use it in the connector for new incoming connections)

There are many posts on this matter but no real solution:

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

http://jcalcote.wordpress.com/tag/truststore
(This article describes only how to recreate SSLcontext from the client side (missing the server side))

Any Ideas?

There's another related question :

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

but the answer there is not sufficient since I don't want to build a new ClassLoader.

Thanks.


回答1:


There is now a solution to this starting with Tomcat v8.5.24.

They introduced 2 methods named: reloadSslHostConfig(String hostName) - to reload a specific host reloadSslHostConfigs() - reload all

They can be called in various ways:

  1. Using jmx
  2. Using manager service
  3. By making custom protocol - I found this way during my research

Details of way 1 and way 2 are easily available online.

Details of how to go about using way 3:

  1. Make a class extending the protocol of your choice for eg. Http11NioProtocol
  2. Override the required methods and just call super in them to keep default behavior
  3. Make a thread in this class to call reloadSslHostConfigs method time to time
  4. Package this class in a jar and put that jar in tomcat's lib folder
  5. Edit protocol in connector in server.xml to use this custom defined protocol

Find sample code below:

Main protocol class:

    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");
                    }
                }
            }
       }
}

Connector in server.xml should mention this as the protocol:

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

Hope this helps.




回答2:


In case your connector has the bindOnInit property set to false (exist starting Tomcat 6.x), which:

Controls when the socket used by the connector is bound. By default it is bound when the connector is initiated and unbound when the connector is destroyed. If set to false, the socket will be bound when the connector is started and unbound when it is stopped.

Code snippets from org.apache.tomcat.util.net.AbstractEndpoint Tomcat 8.0.29:

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;
    }
}

Then you can stop & start the connector through JMX after updates to your keys and trust stores.




回答3:


The Tomcat HTTP/1.1 protocol handler can reload keystores.

If you are using embedded Tomcat or have some way of accessing the Tomcat Connector, then then you ask the protocol hander to reload the keystores and trust stores on demand without restarting the connector.

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();
    }
}


来源:https://stackoverflow.com/questions/5816239/how-do-i-force-tomcat-to-reload-trusted-certificates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!