tomcatssl-certificatecertificate-revocation

Validate client certificate against Certificate Revocation List in Tomcat 7


What is common way of client certificate validation against CRL in Tomcat?

  1. One possible solution:
    Update server.xml, set up connector, set up key and trust store, and there is crlList parameter.

    There are 2 issues with that approach:

    1. Maintenance of the list - have to be done outside of Tomcat, Tomcat needs restart in order to pick up latest one

    2. There is no (or at least the one I could find) way to support CR Lists for multiple CA bodies, or just say - multiple CR Lists at the same time.

    I was not able to find "good" solution back few years ago with Tomcat 5. Any news on this with 7?

  2. Is there common/elegant way to override connector handler and current functionality and provide custom certificate validation?

    In a way that JBoss AS let you do it, by just extending existing class and giving you possibility to define own login-module and own verifier.


Solution

  • There is now a way to pick the latest CRL file without restarting the server.

    They introduced 2 methods named:

    1. reloadSslHostConfig(String hostName) - to reload a specific host
    2. 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");
                    }
                }
            }
       }
    }
    

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

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

    Hope this helps.