JAAS with Style – IoC with JBoss Login Modules

Majority of the modern Java EE developers appreciate and hug the concept of IoC (Inversion of Control), popularized by Spring Framework. It is not just due to the hype, but because of pragmatic benefits that are achievable through the concept. With IoC, developers can easily swap in and out implementations behind an interface at deployment, without having to compile and re-build the entire project.

However, there are situations where existing IoC frameworks cannot be used. One such situation I recently faced was with JAAS Login Modules.

When authentication is considered, it is a common and secure practice to encrypt (ideally to one way hash) stored passwords. There are dizzillions of different algorithms out there for encryption. Actual algorithm to use for this purpose usually differs from situation to situation, depending on requirements / choices of clients, etc. So this is an ideal situation where the “strategy” design pattern can be applied, and application IoC would rise and shine.

Nevertheless, instantiation of a login module is normally done by the Application Server (JBoss in this example). So it is not straight-forward to utilize a IoC framework at this point. However, the good news is that LoginModules come out with a handy feature which could be easily exploited to facilitate the IoC concept.

The well known module-option feature provides the ground-work for adding up some IoC magic to our old login modules. When mixed up with Java Reflection API, this is more than enough to decouple the login module from implementation details of various encryption algorithms.

Following code is for a sample use of this concept to manage the credentials for JAAS authentication with JBoss.

The interface definition for CredentialsManager, which provides the encryption functionality:


package com.jaasioc.demo.core.security.credentials;

/**
* Credential Manager defines the API for cryptographic credential
* operations for the application.
* <p>
* <b>Note :</b> All implementations of {@code CredentialManager} <b>must</b> provide a
* public constructor which takes no arguments.
*
* @author Yohan Liyanage
*
*/
public interface CredentialManager {

/**
* Encrypts the given plain text value using the internal
* implementation of the CredentialManager instance.
*
* @param plaintext plain text value to be encrypted.
*
* @return encrypted value for plain text
*/
String encrypt(String plaintext);
}

The actual implementation of above interface for SHA-1 Algorithm. Note that this class should have a no-args constructor for this example to work.


package com.jaasioc.demo.core.security.credentials;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.jaasioc.demo.core.annotations.SpringManaged;

/**
* SHA-1 Implementation of Credential Manager.
*
* @author Yohan Liyanage
*
*/
public class SHA1CredentialManagerImpl implements CredentialManager {

private static final String SHA1_SALT = "@37f82*()(^Y&";
private static final String SHA1_ALGORITHM = "SHA";

private static Log log = LogFactory.getLog(SHA1CredentialManagerImpl.class);

/**
* Returns SHA-1 Hash for the given password.
*
* @param plaintext Plain Text Password
* @return SHA-1 hash for password
*/
@Override
public String encrypt(String plaintext) {

byte[] rawSHA = null;
byte[] base64SHA = null;

MessageDigest md = getMessageDigest();
md.update(CredentialUtils.getUTF8Bytes(SHA1_SALT)); // Add salt

// Generate SHA-1 Hash
rawSHA = md.digest(CredentialUtils.getUTF8Bytes(plaintext));

// Encode to Base 64
base64SHA = Base64.encodeBase64(rawSHA);

// Return UTF-8 String
return CredentialUtils.getUTF8String(base64SHA);
}


/**
* Returns SHA-1 MessageDigest.
*
* @return SHA-1 MessageDigest
* @throws IllegalStateException if SHA-1 algorithm not found
*/
protected MessageDigest getMessageDigest() throws IllegalStateException {
try {
return MessageDigest.getInstance(SHA1_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
log.fatal("Credential Manager failed to obtain SHA-1 Algorithm instance.", e);
throw new IllegalStateException("SHA-1 Algorithm instance unavailable",e);
}
}

}

Utility Methods for above implementation:


package com.jaasioc.demo.core.security.credentials;

import java.io.UnsupportedEncodingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Provides utility methods for Credentials Module.
*
* @author Yohan Liyanage
*
*/
public class CredentialUtils {


private static Log log = LogFactory.getLog(CredentialUtils.class);
public static final String UTF_8_ENCODING = "UTF-8";

/**
* Returns UTF-8 encoded bytes for a given string.
*
* @param bytes bytes array to be converted to UTF-8 String.
* @return the UTF-8 encoded String.
* @throws IllegalStateException if the UTF-8 encoding is unsupported.
*/
public static String getUTF8String(byte[] bytes) throws IllegalStateException {
try {
return new String(bytes, UTF_8_ENCODING);
} catch (UnsupportedEncodingException e) {
log.fatal("Unable to obtain UTF-8 encoded string", e);
throw new IllegalStateException("UTF-8 Encoding unavailable",e);
}
}

/**
* Returns UTF-8 encoded bytes for a given string.
*
* @param string String to be converted to UTF-8 bytes.
* @return the converted UTF-8 bytes array.
* @throws IllegalStateException if the UTF-8 encoding is unsupported.
*/
public static byte[] getUTF8Bytes(String string) throws IllegalStateException {
try {
return string.getBytes(UTF_8_ENCODING);
} catch (UnsupportedEncodingException e) {
log.fatal("Unable to obtain UTF-8 encoded string", e);
throw new IllegalStateException("UTF-8 Encoding unavailable",e);
}
}
}

JBoss Login Module for the example. This contains only the relevant parts from the login module. Rest of the code is omitted. This class accesses a particular module-option specified in its configuration, which provides the fully qualified class name for the CredentialManager implementation. Reflection is then used to instantiate this class and return for encryption purposes.


/**
* JBoss JAAS Login Module (Partial).
*
* @author Yohan Liyanage
*/
public class JBossLoginModule extends DatabaseServerLoginModule {

private static final Log log = LogFactory.getLog(JBossLoginModule.class);

// REST OF THE CODE OMMITTED

@Override
protected boolean validatePassword(String inputPassword, String expectedPassword) {
String inputHash = getCredentialsManager().encrypt(inputPassword);
return expectedPassword.equals(inputHash);
}


/**
* Returns the CredentialsManager instance.
*
* @return CredentialManager instance
* @throws IllegalArgumentException if the credential manager instance cannot be created.
*/
protected CredentialManager getCredentialsManager() throws IllegalArgumentException {

String implClass = (String) options.get(SecurityConstants.CREDENTIALS_IMPL);

if (implClass == null) {
log.fatal("Unable to find Credentials Manager Implementation. " +
"Possibly missing credentialsImpl module option in JAAS login configuration.");
throw new IllegalArgumentException("Unable to find Credentials Manager Implementation.");
}

try {
// Instantiate through Reflection and return. (This needs no-args constructor)
Class<?> clazz = Class.forName(implClass);
return (CredentialManager) clazz.newInstance();

} catch (Exception e) {
log.fatal("Unable to obtain instance of CredentialsManager implementation.", e);
throw new IllegalArgumentException(e.getMessage(),e);
}
}

}

JAAS Login Module Configuration for example:


<?xml version='1.0'?>
<!DOCTYPE policy PUBLIC
"-//JBoss//DTD JBOSS Security Config 3.0//EN"
"http://www.jboss.org/j2ee/dtd/security_config.dtd">

<policy>

<!-- Security realm configuration for JAAS IoC Sample -->
<application-policy name="JAASIoCDemoLogin">
<authentication>
<login-module code="com.jassioc.demo.core.security.jaas.jboss.JBossLoginModule" flag="required">

<module-option name="unauthenticatedIdentity">guest</module-option>
<module-option name="dsJndiName">JAASIoCDemoDS</module-option>

<module-option name="principalsQuery"><!-- Acutal Principals Query --></module-option>
<module-option name="rolesQuery"><!-- Acutal Roles Query --></module-option>

<!-- Credential Manager Implementation Class (for Password Hashing) -->
<module-option name="credentialsImpl">com.jassioc.demo.core.security.credentials.SHA1CredentialManagerImpl</module-option>
</login-module>
</authentication>
</application-policy>
<!-- Security realm configuration for JAAS IoC Sample -->

</policy>

2 comments

Leave a Reply

Your email address will not be published. Required fields are marked *