Accessing_EJB3s_over_SSL_Without_TrustStore

Introduction

 

The aim of this wiki is to explain how to access EJB3 beans over SSL when trust stores cannot be configured on the client side, in which case, the only alternative that is available is to trust all servers. This wiki will show how to create an SSL socket factory that trusts any server as well as required client side and server side configuration.

 

Requirements

 

Instructions on this wiki will only work correctly if a JBoss Remoting version is used that includes fix for JBREM-1014.

 

Initial Setup

 

Step 1. Grab a copy of JBoss AS 4.2.x+ or JBoss EAP 4.2.x/4.3.x+ and create a new server configuration (based on /default).

 

Step 2. Follow instructions in SSL setup wiki to generate the necessary SSL certificates for the server key store. There's no need to create a trust store for the client because it cannot use it.

 

Custom SSL Socket Factory

 

Step 3. The next step is to create a custom SSL socket factory configured with a trust manager that trusts any server, Here's the code that just does that:

 

package com.acme.security;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.jboss.logging.Logger;

/**
 * TrustEveryoneSSLFactory
 * 
 * @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
 */
public class TrustEveryoneSSLSocketFactory extends SSLSocketFactory
{
   private static final Logger log = Logger.getLogger(TrustEveryoneSSLSocketFactory.class);
   
   private static final boolean isTraceEnabled = log.isTraceEnabled();
   
   private final SSLSocketFactory factory;

   public TrustEveryoneSSLSocketFactory() throws Exception
   {      
      SSLContext sslcontext = SSLContext.getInstance("TLS");
      TrustManager[] trustManagerThatTrustsEveryone = createTrustManagerThatTrustsEveryone();
      sslcontext.init(null, trustManagerThatTrustsEveryone, null);
      factory = sslcontext.getSocketFactory();
   }
   
   @Override public Socket createSocket() throws IOException
   {
      if (isTraceEnabled) log.trace("createSocket() to factory " + factory);
      return factory.createSocket();
   }

   @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException
   {
      return factory.createSocket(s, host, port, autoClose);
   }

   @Override public String[] getDefaultCipherSuites()
   {
      return factory.getDefaultCipherSuites();
   }

   @Override public String[] getSupportedCipherSuites()
   {
      return factory.getSupportedCipherSuites();
   }

   @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException
   {
      return factory.createSocket(host, port);
   }

   @Override public Socket createSocket(InetAddress address, int port) throws IOException
   {
      return factory.createSocket(address, port);
   }

   @Override public Socket createSocket(String host, int port, InetAddress clientAddress, int clientPort) 
      throws IOException, UnknownHostException
   {
      return factory.createSocket(host, port, clientAddress, clientPort);
   }

   @Override public Socket createSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort) throws IOException
   {
      return factory.createSocket(address, port, clientAddress, clientPort);
   }
   
   /**
    * Create TrustManager that trusts everyone.
    */
   protected TrustManager[] createTrustManagerThatTrustsEveryone() 
   {
      return new TrustManager[] 
      {
          new X509TrustManager() 
          {
              public X509Certificate[] getAcceptedIssuers() 
              {
                  return null;
              }

              public void checkClientTrusted(X509Certificate[] certs, String authType) 
              {
              }

              public void checkServerTrusted(X509Certificate[] certs, String authType) 
              {
              }
          }
      };
   }
}

 

Server Side Setup

 

Step 4. Go to $JBOSS_HOME/server//deploy/ejb3.deployer/META-INF/jboss-service.xml and add a JBoss Remoting SSL Connector that is configured with the custom SSL socket factory class name:

 

   <!-- Custom EJB3 SSL connector. Any parameters that are for client consumption need to be passed via the InvokerLocator. 
        See https://jira.jboss.org/jira/browse/JBREM-1014 for more information. 
   -->
   <mbean code="org.jboss.remoting.transport.Connector"
      name="jboss.remoting:service=connector,transport=ssl,target=ejb3,type=trusteveryone">
      <depends>jboss.aop:service=AspectDeployer</depends>
      <attribute name="InvokerLocator">sslsocket://${jboss.bind.address}:3853/?socketFactoryClassName=com.acme.security.TrustEveryoneSSLSocketFactory</attribute>
      <attribute name="Configuration">
         <handlers>
            <handler subsystem="AOP">org.jboss.aspects.remoting.AOPRemotingInvocationHandler</handler>
         </handlers>
      </attribute>      
   </mbean>   

 

Tip! It's not obligatory to add this MBean on the server side. You could alternatively create a jboss-service.xml file at the root of the jar containing the EJB3 beans, and add the MBean definition between a <server></server> XML elements.

 

Deployment Setup

 

Step 5. For the EJB3 bean to be accessed via SSL, its client bind url needs to be defined matching the invoker locator definition from the previous step. It is recommended that client bind urls are defined via META-INF/jboss.xml rather than via annotations in order to take advantage of system property substitution. Otherwise, using annotations, you're forcing the bind address to be a specific IP or host name:

 

<?xml version="1.0" encoding="UTF-8"?>
<jboss xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss_5_0.xsd" version="3.0">
  <enterprise-beans>
    <session>
      <ejb-name>TimeTellerBean</ejb-name>
      <remote-binding>
        <jndi-name>TimeTellerBean/ssl/trusteveryone</jndi-name>
        <client-bind-url>
          <![CDATA[sslsocket://${jboss.bind.address}:3853/?socketFactoryClassName=com.acme.security.TrustEveryoneSSLSocketFactory\]\]\>
        </client-bind-url>
      </remote-binding>
    </session>
  </enterprise-beans>
</jboss>

 

Note: It's of paramount importance that the client bind url is identical to the invoker locator in the step before.

 

Server Startup

 

Step 6. Make sure you pass the appropiate -Djavax.net.ssl.keyStore and -Djavax.net.ssl.keyStorePassword parameters at startup as shown in Accessing EJB3 beans over SSL wiki.

 

Client Code and Setup

 

Step 7. On the client side, as hinted earlier, there's no need to pass any trust store information. However, the client code needs to tell the JVM which SSL socket factory provider to use:

 

java.security.Security.setProperty("ssl.SocketFactory.provider", "com.acme.security.TrustEveryoneSSLSocketFactory");
InitialContext ctx = new InitialContext();
TimeTeller timeTeller = (TimeTeller) ctx.lookup("TimeTellerBean/ssl/trusteveryone");
System.out.println(timeTeller.whatsTheTime("boo"));