3 Replies Latest reply on Nov 17, 2008 4:04 PM by rgurzhiy

    SSL Certificates Dynamic Loading

    vishal_badha

      Is there any possibility i can update the server keystores and truststores while the server is running (without restarting the server). i am trying to implement ssl security and giving access to my client with proper certificates, but in the current scenario the server needs to be restarted for any new client who needs access with new certificatees. Please help me with this. Thanks in advance.

        • 1. Re: SSL Certificates Dynamic Loading
          rgurzhiy

          Hy.
          I faced the same problem. Have you solve it?

          • 2. Re: SSL Certificates Dynamic Loading
            rgurzhiy

            Hello.
            I found some information, but it still not work.

            Anyway...

            I use JBoss Portal 2.7.0 (with JBoss AS 4.2.3)
            I have configured my security to use jboss implementation.

            This is my ssl connector from [path-to-jboss-default]/deploy/jboss-web.deployer/server.xml

             <Connector port="443" address="${jboss.bind.address}"
             protocol="HTTP/1.1" SSLEnabled="true"
             maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
             emptySessionPath="true"
             scheme="https" secure="true" clientAuth="true"
             securityDomain="java:/jaas/portal-ssl"
             SSLImplementation="org.jboss.net.ssl.JBossImplementation"
             sslProtocol="TLS">
            
             <Factory className="org.apache.catalina.net.SSLServerSocketFactory" />
            
             </Connector>
            


            this is my new JbossSecurityDomain from [path-to-jboss-default]/conf/jboss-service.xml
             <mbean code="org.jboss.security.plugins.JaasSecurityDomain"
             name="jboss.security:service=JaasSecurityDomain,domain=portal-ssl">
             <depends>jboss.security:service=JaasSecurityManager</depends>
             <constructor>
             <arg type="java.lang.String" value="portal-ssl" />
             </constructor>
            
             <attribute name="ManagerServiceName">jboss.security:service=JaasSecurityManager</attribute>
             <attribute name="KeyStoreURL">D:/server.keystore</attribute>
             <attribute name="KeyStorePass">server</attribute>
             <attribute name="TrustStoreURL">D:/trusted.keystore</attribute>
             <attribute name="TrustStorePass">trusted</attribute>
             </mbean>
            

            So, my security uses keystores from this mbean.

            after that I inserted in my code, that updates keystores
             ObjectName jaasMgr = new ObjectName("jboss.security:service=JaasSecurityDomain,domain=portal-ssl");
             Object[] params = {};
             String[] signature = {};
            
             MBeanServer server = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
             server.invoke(jaasMgr, "reloadKeyAndTrustStore", params, signature);
            


            I don't know, what reloadKeyAndTrustStore mbean method do, but I suppose, that it reloads cached keystores.

            Well, result is - keystores does not reload if I invoke reloadKeyAndTrustStore either from my code or from jmx-console. Changes does not apply until restart jboss.


            Can anybody help me with my configuration?

            • 3. Re: SSL Certificates Dynamic Loading
              rgurzhiy

              Hello again.
              After some debug and source code research, I found solution. It is not so beautiful and seems to be workaround, but it works. Well, I hope it would be useful not only for me.

              What have I found.
              JaasSecurityDomain realy updates keystores, but only in itself. Ssl JBossImplementation in server.xml used only to create and return JBossSocketFactory. Factory gets SecurityDomain by JNDI to init and create socket. But... There is so called SSLContext, that takes care about keystore usage and ssl-handshakes, and it uses SecurityDomain only in initialisation. So, after socket building SSLContext use cached keystores.

              Well, my solution.
              I wrote custom implementation of 3 classes


              org.jboss.net.ssl.JBossReloadableImplementation

              package org.jboss.net.ssl;
              
              import org.apache.tomcat.util.net.ServerSocketFactory;
              
              public class JBossReloadableImplementation extends JBossImplementation {
              
               public JBossReloadableImplementation() throws ClassNotFoundException {
               super();
               }
              
               public String getImplementationName() {
               return "JBossReloadable";
               }
              
               public ServerSocketFactory getServerSocketFactory() {
               return new JBossReloadableSocketFactory();
               }
              }
              

              as I said, it used only to provide new JBossReloadableSocketFactory


              org.jboss.security.plugins.JaasSecurityDomainReloadable

              package org.jboss.security.plugins;
              
              
              
              import javax.security.auth.callback.CallbackHandler;
              
              
              
              import org.jboss.net.ssl.JBossReloadableSocketFactory;
              
              
              
              public class JaasSecurityDomainReloadable extends JaasSecurityDomain {
              
              
              
               public JaasSecurityDomainReloadable() {
              
               super();
              
               }
              
               public JaasSecurityDomainReloadable(String securityDomain) {
              
               super(securityDomain);
              
               }
              
               public JaasSecurityDomainReloadable(String securityDomain, CallbackHandler handler){
              
               super(securityDomain, handler);
              
               }
              
              
              
               @Override
              
               public void reloadKeyAndTrustStore() throws Exception {
              
               super.reloadKeyAndTrustStore();
              
               //if keystores reloaded successfully, reload them in socket factory
              
               socketFactory.reload();
              
               }
              
              
              
               //Socket factory, where we want to reload keystores
              
               private JBossReloadableSocketFactory socketFactory;
              
               public void setSocketFactory(JBossReloadableSocketFactory socketFactory) {
              
               this.socketFactory = socketFactory;
              
               }
              
              
              
              
              
              }
              
              

              in this class I added method setSocketFactory to post SocketFactory to SecurityDomain, and changed method reloadKeyAndTrustStore. Now it executes SessionFactories method reload() to reload keystores in SSLContext.


              org.jboss.net.ssl.JBossReloadableSocketFactory

              package org.jboss.net.ssl;
              
              
              
              import java.io.IOException;
              
              import java.net.InetAddress;
              
              import java.net.ServerSocket;
              
              import java.security.KeyManagementException;
              
              import java.security.SecureRandom;
              
              
              
              import javax.naming.InitialContext;
              
              import javax.naming.NamingException;
              
              import javax.net.ssl.KeyManagerFactory;
              
              import javax.net.ssl.SSLContext;
              
              import javax.net.ssl.TrustManagerFactory;
              
              
              
              import org.jboss.security.SecurityDomain;
              
              import org.jboss.security.plugins.JaasSecurityDomainReloadable;
              
              
              
              public class JBossReloadableSocketFactory extends JBossSocketFactory {
              
              
              
               @Override
              
               public void setSecurityDomainName(String jndiName) throws NamingException, IOException {
              
               super.setSecurityDomainName(jndiName);
              
              
              
               //We can'n get parents securityDomain, cause it's private. Get it from JNDI.
              
               InitialContext iniCtx = new InitialContext();
              
               SecurityDomain securityDomain = (SecurityDomain) iniCtx.lookup(jndiName);
              
              
              
               //If we use reloadable domain, set socket factory (when mbeans reloadKeyAndTrustStore method invokes, we reload keystores in factory)
              
               if (securityDomain instanceof JaasSecurityDomainReloadable) {
              
               ((JaasSecurityDomainReloadable) securityDomain).setSocketFactory(this);
              
               }
              
               }
              
              
              
              
              
               //Changed init() method from JSSESocketFactory (we can't override cause it is default)
              
               static String defaultProtocol = "TLS";
              
               static String defaultKeystoreType = "JKS";
              
               private SSLContext context = null;
              
               void initSsl() throws IOException {
              
               try {
              
               String clientAuthStr = (String) attributes.get("clientauth");
              
               if("true".equalsIgnoreCase(clientAuthStr) ||
              
               "yes".equalsIgnoreCase(clientAuthStr)) {
              
               requireClientAuth = true;
              
               } else if("want".equalsIgnoreCase(clientAuthStr)) {
              
               wantClientAuth = true;
              
               }
              
              
              
               String protocol = (String) attributes.get("protocol");
              
               if (protocol == null) {
              
               protocol = defaultProtocol;
              
               }
              
              
              
               String algorithm = (String) attributes.get("algorithm");
              
               if (algorithm == null) {
              
               algorithm = KeyManagerFactory.getDefaultAlgorithm();;
              
               }
              
              
              
               String keystoreType = (String) attributes.get("keystoreType");
              
               if (keystoreType == null) {
              
               keystoreType = defaultKeystoreType;
              
               }
              
              
              
               String trustAlgorithm = (String)attributes.get("truststoreAlgorithm");
              
               if( trustAlgorithm == null ) {
              
               trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
              
               }
              
              
              
               //Changes - we use only one SSLContext. If it is not null use old (just load new keystores in init method)
              
               if (context==null) context = SSLContext.getInstance(protocol);
              
               context.init(getKeyManagers(keystoreType, algorithm, (String) attributes.get("keyAlias")),
              
               getTrustManagers(keystoreType, trustAlgorithm),
              
               new SecureRandom());
              
               sslProxy = context.getServerSocketFactory();
              
              
              
               String requestedCiphers = (String)attributes.get("ciphers");
              
               enabledCiphers = getEnabledCiphers(requestedCiphers, sslProxy.getSupportedCipherSuites());
              
              
              
               //Set initialized = true to avoid execution of init() method from JSSESocketFactory
              
               initialized = true;
              
               } catch(Exception e) {
              
               if( e instanceof IOException ) throw (IOException)e;
              
               throw new IOException (e.getMessage());
              
               }
              
               }
              
              
              
               public void reload() throws KeyManagementException, Exception {
              
               initSsl();
              
               }
              
              
              
               //Updated methods createSocket (use initSsl() instead of init())
              
               @Override
              
               public ServerSocket createSocket(int port) throws IOException {
              
               if(!initialized) initSsl();
              
               return super.createSocket(port);
              
               }
              
               @Override
              
               public ServerSocket createSocket(int port, int backlog) throws IOException {
              
               if(!initialized) initSsl();
              
               return super.createSocket(port, backlog);
              
               }
              
               @Override
              
               public ServerSocket createSocket(int port, int backlog, InetAddress ifAddress) throws IOException {
              
               if(!initialized) initSsl();
              
               return super.createSocket(port, backlog, ifAddress);
              
               }
              
              }
              
              

              here I created methods initSsl() and reload(), overrode 3 createSocket methods (to use my initSsl instead of default init) and remembered SSLContext, using by our sockets (to update keystores in it)

              I have packed these classes in jar and put it to my [path-to-jboss-default]/deploy/jboss-web.deployer (JBoss AS 4.2.3).


              Now configuration:

              SSL connector in [path-to-jboss-default]/deploy/jboss-web.deployer/server.xml
               <Connector port="443" address="${jboss.bind.address}"
               protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
               emptySessionPath="true"
               scheme="https" secure="true" clientAuth="true"
               securityDomain="java:/jaas/portal-ssl"
               SSLImplementation="org.jboss.net.ssl.JBossReloadableImplementation"
               sslProtocol="TLS" />
              


              SecurityDomain mbean in [path-to-jboss-default]/deploy/jboss-web.deployer/META-INF/jboss-service.xml
               <mbean code="org.jboss.security.plugins.JaasSecurityDomainReloadable"
               name="jboss.security:service=JaasSecurityDomain,domain=portal-ssl">
               <depends>jboss.security:service=JaasSecurityManager</depends>
               <constructor>
               <arg type="java.lang.String" value="portal-ssl" />
               </constructor>
              
               <attribute name="ManagerServiceName">jboss.security:service=JaasSecurityManager</attribute>
               <attribute name="KeyStoreURL">D:/server.keystore</attribute>
               <attribute name="KeyStorePass">server</attribute>
               <attribute name="TrustStoreURL">D:/trusted.keystore</attribute>
               <attribute name="TrustStorePass">trusted</attribute>
               </mbean>
              


              And finally, I use mbeans reloadKeyAndTrustStore method invocation from previous post. You may also invoke this method from jmx-console.


              Hope this post will help someone )))