[jboss-user] [Security & JAAS/JBoss] - Re: SSL Certificates Dynamic Loading

rgurzhiy do-not-reply at jboss.com
Mon Nov 17 16:05:08 EST 2008


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 )))

View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4189909#4189909

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4189909



More information about the jboss-user mailing list