package com.openedit.util; import java.io.File; import java.io.FileInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.spec.KeySpec; import java.util.Properties; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import org.apache.commons.codec.binary.Base64; import com.openedit.OpenEditException; import com.openedit.OpenEditRuntimeException; public class StringEncryption { // public static final String DESEDE_ENCRYPTION_SCHEME = "DESede"; public static final String DES_ENCRYPTION_SCHEME = "DES"; //public static final String DEFAULT_ENCRYPTION_KEY = "This is a fairly long phrase used to encrypt"; public static final String DEFAULT_ENCRYPTION_KEY = "7939805759879766427939805759879766"; protected String fieldEncryptionKey; protected KeySpec fieldKeySpec; protected SecretKeyFactory fieldKeyFactory; protected Cipher fieldCipher; protected SecretKey fieldSecretKey; protected File fieldRootDirectory; protected String fieldSecretKeyName = "userpassword"; private static final String UNICODE_FORMAT = "UTF8"; public StringEncryption() { } public StringEncryption( String encryptionKey ) { try { setEncryptionKey(encryptionKey); } catch (Exception e) { throw new OpenEditRuntimeException(e); } } public void setEncryptionKey(String inEncryptionKey) { if ( inEncryptionKey == null ) throw new IllegalArgumentException( "encryption key was null" ); if ( inEncryptionKey.trim().length() < 24 ) throw new IllegalArgumentException( "encryption key was less than 24 characters" ); fieldEncryptionKey = inEncryptionKey; setKeySpec(null); setCipher(null); } public String getEncryptionKey() throws Exception { if( fieldEncryptionKey == null) { File prop = new File( getRootDirectory(), "WEB-INF/encrypt.properties"); if( prop.exists()) { Properties props = new Properties(); FileInputStream reader = null; try { reader = new FileInputStream(prop); props.load( reader); } finally { FileUtils.safeClose(reader); } fieldEncryptionKey = props.getProperty(getSecretKeyName()); } if( fieldEncryptionKey == null) { fieldEncryptionKey = DEFAULT_ENCRYPTION_KEY; } } return fieldEncryptionKey; } public synchronized String encrypt( String unencryptedString ) throws OpenEditException { if ( unencryptedString == null || unencryptedString.trim().length() == 0 ) { throw new IllegalArgumentException( "unencrypted string was null or empty" ); } try { byte[] cleartext = unencryptedString.getBytes( UNICODE_FORMAT ); Cipher cipher = getCipher(); //Not thread safe cipher.init( Cipher.ENCRYPT_MODE, getSecretKey() ); byte[] ciphertext = cipher.doFinal( cleartext ); Base64 base64encoder = new Base64(); return "DES:" + new String( base64encoder.encode( ciphertext ), UNICODE_FORMAT ); } catch (Exception e) { throw new OpenEditException( e ); } } public SecretKey getSecretKey() throws Exception { if( fieldSecretKey == null) { fieldSecretKey = getKeyFactory().generateSecret( getKeySpec() ); } return fieldSecretKey; } public synchronized String decrypt( String encryptedString ) throws OpenEditException { if ( encryptedString == null || encryptedString.trim().length() <= 0 ) throw new IllegalArgumentException( "encrypted string was null or empty" ); try { if( encryptedString.startsWith("DES:")) { encryptedString = encryptedString.substring(4); } if( encryptedString.startsWith("DES")) { encryptedString = encryptedString.substring(3); } Cipher cipher = getCipher(); cipher.init( Cipher.DECRYPT_MODE, getSecretKey() ); Base64 base64decoder = new Base64(); byte[] cleartext = base64decoder.decode( encryptedString.getBytes( UNICODE_FORMAT ) ); byte[] ciphertext = cipher.doFinal( cleartext ); return bytes2String( ciphertext ); } catch (Exception e) { throw new OpenEditException( e ); } } //This seems wrong. Only works for ASCII. Why not just use new String().getBytes()? public static String bytes2String( byte[] bytes ) { StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { stringBuffer.append( (char) bytes[i] ); } return stringBuffer.toString(); } public Cipher getCipher() throws Exception { if( fieldCipher == null) { fieldCipher = Cipher.getInstance( DES_ENCRYPTION_SCHEME ); } return fieldCipher; } public void setCipher(Cipher inCipher) { fieldCipher = inCipher; } public SecretKeyFactory getKeyFactory() throws Exception { if( fieldKeyFactory == null) { fieldKeyFactory = SecretKeyFactory.getInstance( DES_ENCRYPTION_SCHEME ); } return fieldKeyFactory; } public void setKeyFactory(SecretKeyFactory inKeyFactory) { fieldKeyFactory = inKeyFactory; } public KeySpec getKeySpec() throws Exception { if( fieldKeySpec == null) { byte[] keyAsBytes = getEncryptionKey().getBytes( UNICODE_FORMAT ); fieldKeySpec = new DESKeySpec( keyAsBytes ); } return fieldKeySpec; } public void setKeySpec(KeySpec inKeySpec) { fieldKeySpec = inKeySpec; } public File getRootDirectory() { return fieldRootDirectory; } public void setRootDirectory(File inRootDirectory) { fieldRootDirectory = inRootDirectory; } public String getSecretKeyName() { return fieldSecretKeyName; } public void setSecretKeyName(String inSecretKeyName) { fieldSecretKeyName = inSecretKeyName; } public String decryptIfNeeded(String inPassword) { if( inPassword != null) { if( inPassword.startsWith("DES")) { return decrypt(inPassword); } } return inPassword; } public String asMd5(String key) throws NoSuchAlgorithmException { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); digest.update(key.getBytes()); byte[] md5 = digest.digest(); StringBuffer hexString = new StringBuffer(); for (int i=0;i<md5.length;i++) { if( md5[i] <= 16) { hexString.append("0"); } hexString.append( Integer.toHexString(0xFF & md5[i])); } return hexString.toString(); } public String getPasswordMd5(String inPassword) { String password = decryptIfNeeded(inPassword); try { String secret = getEncryptionKey() + password; String hash = asMd5(secret); return hash; } catch( Exception ex) { throw new OpenEditException(ex); } } }