<Context path="/myProject" docBase="myProject" debug="1" reloadable="true">
<Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@server:1523:TEST"
username="muralim" password="helloworld" maxActive="4" maxIdle="2" maxWait="-1"
logAbandoned="true" removeAbandoned="true" removeAbandonedTimeout="60"/>
</Context>
So I use the Cipher encryption to encrypt my username and password and replace the clear text of username and password as below:
<Context path="/myProject" docBase="myProject" debug="1" reloadable="true">
<Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@server:1523:TEST"
username="Ql6u3CAl988=" password="CgrtRRVz6A3aaGAfDpRRuQ==" maxActive="4" maxIdle="2" maxWait="-1"
logAbandoned="true" removeAbandoned="true" removeAbandonedTimeout="60"/>
</Context>
I used a common key to encrypt my username and password and it's all encrypted now. But how does Tomcat recognise that these credentials are encrypted and know to decrypted then when making the actual connection???...
<Context path="/myProject" docBase="myProject" debug="1" reloadable="true">
<Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"
factory="com.mycomp.util.CompEncryptedDataSourceFactory"
driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@server:1523:TEST"
username="Ql6u3CAl988=" password="CgrtRRVz6A3aaGAfDpRRuQ==" maxActive="4" maxIdle="2" maxWait="-1"
logAbandoned="true" removeAbandoned="true" removeAbandonedTimeout="60"/>
</Context>
In the above code snippet, you might notice the attribute
factory with some value of a class name. By default DBCP factory is used to handle the connection resources and hence the attribute value is implicit. To read more on custom factories read on...
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.*;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class CompEncryptedDataSourceFactory extends BasicDataSourceFactory{
// common key for encryption and decryption
private static final String ENC_KEY ="CompEncryptedDataSourceFactory";
public CompEncryptedDataSourceFactory()
{}
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment)
throws Exception
{
if(obj instanceof Reference)
{
setUsername((Reference)obj);
setPassword((Reference)obj);
}
return super.getObjectInstance(obj, name, nameCtx, environment);
}
private void setUsername(Reference ref)
throws Exception
{
findDecryptAndReplace("username", ref);
}
private void setPassword(Reference ref)
throws Exception
{
findDecryptAndReplace("password", ref);
}
private void findDecryptAndReplace(String refType, Reference ref)
throws Exception
{
int idx = find(refType, ref);
String decrypted = decrypt(idx, ref);
replace(idx, refType, decrypted, ref);
}
private void replace(int idx, String refType, String newValue, Reference ref)
throws Exception
{
ref.remove(idx);
ref.add(idx, new StringRefAddr(refType, newValue));
}
private String decrypt(int idx, Reference ref)throws Exception
{
return new CipherEncrypter(ENC_KEY).decrypt(ref.get(idx).getContent().toString());
}
private int find(String addrType, Reference ref)
throws Exception
{
Enumeration enu = ref.getAll();
for(int i = 0; enu.hasMoreElements(); i++)
{
RefAddr addr = (RefAddr)enu.nextElement();
if(addr.getType().compareTo(addrType) == 0)
return i;
}
throw new Exception("The \"" + addrType + "\" name/value pair was not found" + " in the Reference object. The reference Object is" + " " + ref.toString());
}
}
For now don't worry about the working of this class as it's not so important unless you are too keen :-) . As you might see, the class extends BasicDataSourceFactory from Commons/DBCP.
import java.io.UnsupportedEncodingException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class CipherEncrypter {
Cipher ecipher;
Cipher dcipher;
byte[] salt = {
(byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
(byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
};
int iterationCount = 19;
CipherEncrypter(String passPhrase) {
try {
// Create the key
PBEKeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray());
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (java.security.InvalidAlgorithmParameterException e) {
} catch (java.security.spec.InvalidKeySpecException e) {
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
}
}
public String encrypt(String str) {
try {
byte[] utf8 = str.getBytes("UTF8");
byte[] enc = ecipher.doFinal(utf8);
return new sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (Exception e) {
}
return null;
}
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (java.io.IOException e) {
}
return null;
}
public static void main(String[] args)
{
try {
DesEncrypter encrypter = new DesEncrypter("CompEncryptedDataSourceFactory"); // key for encryption
String encrypted = encrypter.encrypt("helloworld");
System.out.println("encrypted :"+encrypted);
String decrypted = encrypter.decrypt(encrypted);
System.out.println("decrypted :"+decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The above code includes a main method to get the encrypted text of the user name and password.
try{
InitialContext initialContext = new InitialContext();
DataSource dataSource = (DataSource)initialContext.lookup("java:comp/env/jdbc/test");
return dataSource.getConnection();
}catch(Exception e){
log.error("Exception in getInstance",e);
e.printStackTrace();
}
Hope this article makes a developer life easy in configuring a secured datasource.
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)