SSL secured EJBs with Elytron

Elytron security framework enables developers to use an unified security infrastructure to authenticate/authorize your business methods but also to encrypt the communication. In this tutorial we will see how to do that both in an EJB client application which uses remote+https to secure each remote method call.

The starting point of this tutorial is a client-server EJB application which is made up of the following EJB implementations:

package com.itbuzzpress.chapter16.ejb;

import java.util.concurrent.Future;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.*;

import com.itbuzzpress.chapter16.exception.InsufficientFundsException;
 

@Stateful
@Remote(Account.class)
@RolesAllowed("employee")
@org.jboss.ejb3.annotation.SecurityDomain("other")
public class AccountEJB implements Account {

    long money;
 
    
	@Override
	public long getMoney() {
		return money;

	}

	public void createAccount(long amount)  
	{
		this.money= amount;
		 
	}

	@Override
	public void deposit(long amount)  
	{
		 
		this.money+= amount;
			 
		System.out.println("Money deposit. total is "+money);
	}
 
	 
	@Override
	public void withdraw(long amount) throws InsufficientFundsException {
		
		long newAmount = money - amount;
		if (newAmount < 0) {
			throw new InsufficientFundsException("Unsufficient funds for account! ");
		}
		
		money = newAmount;	 
		System.out.println("Money withdrawal. total is "+money);

	}
}
package com.itbuzzpress.chapter16.ejb;

import java.util.List;
import java.util.Timer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;

@Stateless
@Remote(Calculator.class)
@RolesAllowed("employee")
@org.jboss.ejb3.annotation.SecurityDomain("other")
public class CalculatorEJB implements Calculator {
   
	float interest=5;
 
	@Override
	public float calculateInterest(long money) {
	 
	    return money * (1+ (interest/100));
	    
	   
   }
	
	 
	
}

So as you could see, both EJBs are secured using the @javax.annotation.security.RolesAllowed annotation which states that only members of “employee” group are allowed. Also the @org.jboss.ejb3.annotation.SecurityDomain is the default one, that is “other”.

We will now develop a simple Remote EJB Client to access this application:

package com.itbuzzpress.chapter16.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.itbuzzpress.chapter16.ejb.Account;
import com.itbuzzpress.chapter16.ejb.Calculator;
import com.itbuzzpress.chapter16.exception.InsufficientFundsException;

import java.util.Hashtable;

public class RemoteEJBClient {

	public static void main(String[] args) throws Exception {

		Account account = lookupAccountEJB();
		Calculator calculator = lookupCalculatorEJB();
		System.out.println("Going to deposit 1000$ ");

		account.createAccount(1000l);

		account.deposit(250);

		try {
			System.out.println("Going to withdraw 500$ ");
			account.withdraw(500);
		} catch (InsufficientFundsException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		long money = account.getMoney();
		System.out.println("Money left " + money);
		float totalMoney = calculator.calculateInterest(money);
		System.out.println("Money plus interests " + totalMoney);

	}

	private static Account lookupAccountEJB() throws NamingException {
		final Hashtable jndiProperties = new Hashtable();
                jndiProperties.put(Context.PROVIDER_URL, "remote+https://127.0.0.1:8443");
                jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");

		final Context context = new InitialContext(jndiProperties);

		return (Account) context
				.lookup("ejb:/javaee7-ejb-server-ssl-elytron/AccountEJB!com.itbuzzpress.chapter16.ejb.Account?stateful");
	}

	private static Calculator lookupCalculatorEJB() throws NamingException {
		final Hashtable jndiProperties = new Hashtable();
                jndiProperties.put(Context.PROVIDER_URL, "remote+https://127.0.0.1:8443");
                jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
		final Context context = new InitialContext(jndiProperties);

		return (Calculator) context
				.lookup("ejb:/javaee7-ejb-server-ssl-elytron/CalculatorEJB!com.itbuzzpress.chapter16.ejb.Calculator");
	}
}

The most interesting lines are highlighted. As you can see, we are using “remote+https” as protocol on the port 8443. Another valid option is using “remoting+ssl” connector on port 4448.

Also, please note that the Context.INITIAL_CONTEXT_FACTORY is now org.wildfly.naming.client.WildFlyInitialContextFactory which differes from older WildFly versions.

Next step will be creating client and server certificates for this example.

Creating keystores and truststores

In order to perform client-server mutual authentication we will create the server and client certificates and import the client public key in the server truststore and the server public key into the client trustore.

Let’s start from creating the server keystore:

keytool -genkey -v -alias jbossalias -keyalg RSA -keysize 1024 -keystore server.keystore -validity 3650 -keypass 123456 -storepass 123456 -dname "cn=Server Administrator,o=Acme,c=GB" 

Now we will export the Server’s Public Key in a file named server.cer

keytool -export -keystore server.keystore -alias jbossalias -file server.cer -keypass 123456 -storepass 123456

Then we export Client Key Store into the file client.keystore

keytool -genkey -v -alias clientalias -keyalg RSA -keysize 1024 -keystore client.keystore -validity 3650 -keypass abcdef -storepass abcdef -dname "cn=Server Administrator,o=Acme,c=GB" 

Now we will be exporting the Client’s Public Key in the file client.cer

keytool -export -keystore client.keystore -alias clientalias -file client.cer -keypass abcdef -storepass abcdef

We are almost done. Now we import the Client’s Public key into server’s truststore

keytool -import -v -trustcacerts -alias clientalias -file client.cer -keystore server.truststore -keypass 123456 -storepass 123456

Last, we will be importing the Server’s Public key into client’s truststore:

keytool -import -v -trustcacerts -alias jbossalias -file server.cer -keystore client.truststore -keypass abcdef -storepass abcdef

Now copy the keystore and trustore files into the JBOSS_HOME/standalone/configuration folder and you are done.

Changes in the server configuration

Done with the server and client certificates, we will create the elytron key-store, key-manager, trust-manager and server-ssl-context to store reference the client and server certificates:

/subsystem=elytron/key-store=server-key-store:add(path=server.keystore, relative-to=jboss.server.config.dir, credential-reference={clear-text=123456}, type=JKS)
/subsystem=elytron/key-store=server-trust-store:add(path=server.truststore, relative-to=jboss.server.config.dir, credential-reference={clear-text=123456}, type=JKS)
/subsystem=elytron/key-manager=example-key-manager:add(key-store=server-key-store, credential-reference={clear-text=123456})
/subsystem=elytron/trust-manager=example-trust-manager:add(key-store=server-trust-store)
/subsystem=elytron/server-ssl-context=example-ssl-context:add(trust-manager=example-trust-manager, key-manager=example-key-manager, need-client-auth=true, want-client-auth=true)
/subsystem=ejb3/application-security-domain=other:add(security-domain=ApplicationDomain)

We are almost done. Before deploying running the client application we need to provide the authentication mechanism and users which will connect to the remote EJB. Also, as we didn’t specify in the client application the System Properties for the client-keystore and client-trustore, we will specify it in the wildfly-config.xml file placed in the src/main/resources/META-INF folder of your client application:

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
            <rule use-configuration="default"/>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default">
	        <sasl-mechanism-selector selector="DIGEST-MD5"/>
                <set-user-name name="francesco"/>
                <credentials>
                    <clear-password password="Password1!"/>
                </credentials>
            </configuration>
        </authentication-configurations>
        <key-stores>
            <key-store name="client-keystore" type="JKS">
                <file name="/home/jboss/wildfly-12.0.0.Final/standalone/configuration/client.keystore"/>
                <key-store-clear-password password="abcdef"/>
            </key-store>
            <key-store name="client-truststore" type="JKS">
                <file name="/home/jboss/wildfly-12.0.0.Final/standalone/configuration/client.truststore"/>
            </key-store>
        </key-stores>
        <ssl-contexts>
            <ssl-context name="client-ssl-context">
                <trust-store key-store-name="client-truststore"/>
                <key-store-ssl-certificate key-store-name="client-keystore" alias="clientalias">
                    <key-store-clear-password password="abcdef"/>
                </key-store-ssl-certificate>
            </ssl-context>
        </ssl-contexts>
        <ssl-context-rules>
            <rule use-ssl-context="client-ssl-context"/>
        </ssl-context-rules>
    </authentication-client>
</configuration>

So the user “francesco” with password “Password1!” will be used to connect remotely. We will add this user into the “other” security domain by simply using the add-user.sh script:

$ add-user.sh -a -g employee -u francesco -p Password1!

The full source code is available on: https://github.com/fmarchioni/practical-javaee7-development-wildfly/tree/javaee8/code/chapter16/javaee7-ejb-elytron-ssl

Within the folder https://github.com/fmarchioni/practical-javaee7-development-wildfly/tree/javaee8/code/chapter16/javaee7-ejb-elytron-ssl/ssl you will find also the scripts to configure elytron subsystem using the default Application Realm or a more advanced example using a custom FileSystem Security Realm.

Found the article helpful? if so please follow us on Socials