package org.jboss.security.plugins;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.StringTokenizer;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.callback.CallbackHandler;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.security.SecurityDomain;
import org.jboss.security.Util;
import org.jboss.security.auth.callback.SecurityAssociationHandler;
public class JaasSecurityDomain
extends JaasSecurityManager
implements SecurityDomain, JaasSecurityDomainMBean
{
private static final RuntimePermission encodePermission =
new RuntimePermission("org.jboss.security.plugins.JaasSecurityDomain.encode");
private static final RuntimePermission decodePermission =
new RuntimePermission("org.jboss.security.plugins.JaasSecurityDomain.decode");
private KeyStore keyStore;
private KeyManagerFactory keyMgr;
private String keyStoreType = "JKS";
private URL keyStoreURL;
private char[] keyStorePassword;
private String keyStorePasswordCmd;
private String keyStorePasswordCmdType;
private SecretKey cipherKey;
private String cipherAlgorithm = "PBEwithMD5andDES";
private byte[] salt = {1, 2, 3, 4, 5, 6, 7, 8};
private int iterationCount = 103;
private PBEParameterSpec cipherSpec;
private ObjectName managerServiceName = JaasSecurityManagerServiceMBean.OBJECT_NAME;
private KeyStore trustStore;
private String trustStoreType = "JKS";
private char[] trustStorePassword;
private URL trustStoreURL;
private TrustManagerFactory trustMgr;
public JaasSecurityDomain()
{
super();
}
public JaasSecurityDomain(String securityDomain)
{
this(securityDomain, new SecurityAssociationHandler());
}
public JaasSecurityDomain(String securityDomain, CallbackHandler handler)
{
super(securityDomain, handler);
}
public KeyStore getKeyStore() throws SecurityException
{
return keyStore;
}
public KeyManagerFactory getKeyManagerFactory() throws SecurityException
{
return keyMgr;
}
public KeyStore getTrustStore() throws SecurityException
{
return trustStore;
}
public TrustManagerFactory getTrustManagerFactory() throws SecurityException
{
return trustMgr;
}
public ObjectName getManagerServiceName()
{
return this.managerServiceName;
}
public void setManagerServiceName(ObjectName managerServiceName)
{
this.managerServiceName = managerServiceName;
}
public String getKeyStoreType()
{
return this.keyStoreType;
}
public void setKeyStoreType(String type)
{
this.keyStoreType = type;
}
public String getKeyStoreURL()
{
String url = null;
if( keyStoreURL != null )
url = keyStoreURL.toExternalForm();
return url;
}
public void setKeyStoreURL(String storeURL) throws IOException
{
this.keyStoreURL = this.validateStoreURL(storeURL);
log.debug("Using KeyStore=" + keyStoreURL.toExternalForm());
}
public void setKeyStorePass(String password)
{
this.keyStorePassword = null;
if( password.charAt(0) == '{' )
{
StringTokenizer tokenizer = new StringTokenizer(password, "{}");
this.keyStorePasswordCmdType = tokenizer.nextToken();
this.keyStorePasswordCmd = tokenizer.nextToken();
}
else
{
this.keyStorePassword = password.toCharArray();
}
}
public String getTrustStoreType()
{
return this.trustStoreType;
}
public void setTrustStoreType(String type)
{
this.trustStoreType = type;
}
public void setTrustStorePass(String password)
{
this.trustStorePassword = password.toCharArray();
}
public String getTrustStoreURL()
{
String url = null;
if( trustStoreURL != null )
url = trustStoreURL.toExternalForm();
return url;
}
public void setTrustStoreURL(String storeURL) throws IOException
{
this.trustStoreURL = validateStoreURL(storeURL);
}
public void setSalt(String salt)
{
this.salt = salt.getBytes();
}
public void setIterationCount(int iterationCount)
{
this.iterationCount = iterationCount;
}
public String getCipherAlgorithm()
{
return cipherAlgorithm;
}
public void setCipherAlgorithm(String cipherAlgorithm)
{
this.cipherAlgorithm = cipherAlgorithm;
}
public String getName()
{
return "JaasSecurityDomain(" + getSecurityDomain() + ")";
}
public byte[] encode(byte[] secret)
throws Exception
{
SecurityManager sm = System.getSecurityManager();
if( sm != null )
{
System.out.println("Checking: "+encodePermission);
sm.checkPermission(encodePermission);
}
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, cipherKey, cipherSpec);
byte[] encoding = cipher.doFinal(secret);
return encoding;
}
public byte[] decode(byte[] secret)
throws Exception
{
SecurityManager sm = System.getSecurityManager();
if( sm != null )
sm.checkPermission(decodePermission);
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherSpec);
byte[] decode = cipher.doFinal(secret);
return decode;
}
public String encode64(byte[] secret)
throws Exception
{
byte[] encoding = encode(secret);
String b64 = Util.tob64(encoding);
return b64;
}
public byte[] decode64(String secret)
throws Exception
{
byte[] encoding = Util.fromb64(secret);
byte[] decode = decode(encoding);
return decode;
}
public void reloadKeyAndTrustStore()
throws Exception
{
loadKeyAndTrustStore();
}
protected void startService()
throws Exception
{
loadKeystorePassword();
loadKeyAndTrustStore();
MBeanServer server = MBeanServerLocator.locateJBoss();
Object[] params = {getSecurityDomain(), this};
String[] signature = new String[]{"java.lang.String", "org.jboss.security.SecurityDomain"};
server.invoke(managerServiceName, "registerSecurityDomain", params, signature);
}
protected void stopService()
{
if( keyStorePassword != null )
{
Arrays.fill(keyStorePassword, '\0');
keyStorePassword = null;
}
cipherKey = null;
}
private void loadKeystorePassword()
throws Exception
{
if( keyStorePassword == null )
{
if( keyStorePasswordCmdType.equals("EXT") )
execPasswordCmd();
else if( keyStorePasswordCmdType.equals("CLASS") )
invokePasswordClass();
else
throw new IllegalArgumentException("Unknown keyStorePasswordCmdType: "+keyStorePasswordCmdType);
}
cipherSpec = new PBEParameterSpec(salt, iterationCount);
PBEKeySpec keySpec = new PBEKeySpec(keyStorePassword);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEwithMD5andDES");
cipherKey = factory.generateSecret(keySpec);
}
private void loadKeyAndTrustStore()
throws Exception
{
if( keyStoreURL != null )
{
keyStore = KeyStore.getInstance(keyStoreType);
InputStream is = keyStoreURL.openStream();
keyStore.load(is, keyStorePassword);
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
keyMgr = KeyManagerFactory.getInstance(algorithm);
keyMgr.init(keyStore, keyStorePassword);
}
if( trustStoreURL != null )
{
trustStore = KeyStore.getInstance(trustStoreType);
InputStream is = trustStoreURL.openStream();
trustStore.load(is, trustStorePassword);
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
trustMgr = TrustManagerFactory.getInstance(algorithm);
trustMgr.init(trustStore);
}
else if( keyStore != null )
{
trustStore = keyStore;
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
trustMgr = TrustManagerFactory.getInstance(algorithm);
trustMgr.init(trustStore);
}
}
private void execPasswordCmd()
throws Exception
{
log.debug("Executing command: "+keyStorePasswordCmd);
Runtime rt = Runtime.getRuntime();
Process p = rt.exec(keyStorePasswordCmd);
InputStream stdin = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdin));
String password = reader.readLine();
stdin.close();
int exitCode = p.waitFor();
log.debug("Command exited with: "+exitCode);
keyStorePassword = password.toCharArray();
}
private void invokePasswordClass()
throws Exception
{
keyStorePassword = null;
String classname = keyStorePasswordCmd;
String ctorArg = null;
int colon = keyStorePasswordCmd.indexOf(':');
if( colon > 0 )
{
classname = keyStorePasswordCmd.substring(0, colon);
ctorArg = keyStorePasswordCmd.substring(colon+1);
}
log.debug("Loading class: "+classname+", ctorArg="+ctorArg);
ClassLoader loader = SubjectActions.getContextClassLoader();
Class c = loader.loadClass(classname);
Object instance = null;
if( ctorArg != null )
{
Class[] sig = {String.class};
Constructor ctor = c.getConstructor(sig);
Object[] args = {ctorArg};
instance = ctor.newInstance(args);
}
else
{
instance = c.newInstance();
}
try
{
log.debug("Checking for toCharArray");
Class[] sig = {};
Method toCharArray = c.getMethod("toCharArray", sig);
Object[] args = {};
log.debug("Invoking toCharArray");
keyStorePassword = (char[]) toCharArray.invoke(instance, args);
}
catch(NoSuchMethodException e)
{
log.debug("No toCharArray found, invoking toString");
String tmp = instance.toString();
if( tmp != null )
keyStorePassword = tmp.toCharArray();
}
}
private URL validateStoreURL(String storeURL) throws IOException
{
URL url = null;
try
{
url = new URL(storeURL);
}
catch(MalformedURLException e)
{
}
if( url == null )
{
File tst = new File(storeURL);
if( tst.exists() == true )
url = tst.toURL();
}
if( url == null )
{
ClassLoader loader = SubjectActions.getContextClassLoader();
url = loader.getResource(storeURL);
}
if( url == null )
{
String msg = "Failed to find url=" + storeURL + " as a URL, file or resource";
throw new MalformedURLException(msg);
}
return url;
}
}