--- /dev/null
+package org.argeo.util.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+
+/** Simple password based encryption / decryption */
+public class PasswordBasedEncryption {
+ public final static Integer DEFAULT_ITERATION_COUNT = 1024;
+ public final static Integer DEFAULT_KEY_LENGTH = 256;
+ public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+ public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+ public final static String DEFAULT_CIPHER = "AES/CBC/PKCS5Padding";
+ public final static String DEFAULT_CHARSET = "UTF-8";
+
+ private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
+ (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+ (byte) 0x03 };
+ private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
+ (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+ (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
+ (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
+
+ private final Key key;
+ private final Cipher ecipher;
+ private final Cipher dcipher;
+
+ public PasswordBasedEncryption(char[] password) {
+ try {
+ byte[] salt = new byte[8];
+ System.arraycopy(DEFAULT_SALT_8, 0, salt, 0, salt.length);
+ for (int i = 0; i < password.length && i < salt.length; i++)
+ salt[i] = (byte) password[i];
+ byte[] iv = new byte[16];
+ System.arraycopy(DEFAULT_IV_16, 0, iv, 0, iv.length);
+ for (int i = 0; i < password.length && i < iv.length; i++)
+ iv[i] = (byte) password[i];
+
+ SecretKeyFactory keyFac = SecretKeyFactory
+ .getInstance(getSecretKeyFactoryName());
+ PBEKeySpec keySpec = new PBEKeySpec(password, salt,
+ getIterationCount(), getKeyLength());
+ String secKeyEncryption = getSecretKeyEncryption();
+ if (secKeyEncryption != null) {
+ SecretKey tmp = keyFac.generateSecret(keySpec);
+ key = new SecretKeySpec(tmp.getEncoded(),
+ getSecretKeyEncryption());
+ } else {
+ key = keyFac.generateSecret(keySpec);
+ }
+ ecipher = Cipher.getInstance(getCipherName());
+ ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+ // AlgorithmParameters params = ecipher.getParameters();
+ // byte[] iv =
+ // params.getParameterSpec(IvParameterSpec.class).getIV();
+ dcipher = Cipher.getInstance(getCipherName());
+ dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot get secret key", e);
+ }
+ }
+
+ public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
+ throws IOException {
+ try {
+ CipherOutputStream out = new CipherOutputStream(encryptedOut,
+ ecipher);
+ StreamUtils.copy(decryptedIn, out);
+ StreamUtils.closeQuietly(out);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot encrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(decryptedIn);
+ }
+ }
+
+ public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
+ throws IOException {
+ try {
+ CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
+ dcipher);
+ StreamUtils.copy(decryptedIn, decryptedOut);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot decrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(encryptedIn);
+ }
+ }
+
+ public byte[] encryptString(String str) {
+ ByteArrayOutputStream out = null;
+ ByteArrayInputStream in = null;
+ try {
+ out = new ByteArrayOutputStream();
+ in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
+ encrypt(in, out);
+ return out.toByteArray();
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot encrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(out);
+ }
+ }
+
+ /** Closes the input stream */
+ public String decryptAsString(InputStream in) {
+ ByteArrayOutputStream out = null;
+ try {
+ out = new ByteArrayOutputStream();
+ decrypt(in, out);
+ return new String(out.toByteArray(), DEFAULT_CHARSET);
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot decrypt", e);
+ } finally {
+ StreamUtils.closeQuietly(out);
+ }
+ }
+
+ protected Key getKey() {
+ return key;
+ }
+
+ protected Cipher getEcipher() {
+ return ecipher;
+ }
+
+ protected Cipher getDcipher() {
+ return dcipher;
+ }
+
+ protected Integer getIterationCount() {
+ return DEFAULT_ITERATION_COUNT;
+ }
+
+ protected Integer getKeyLength() {
+ return DEFAULT_KEY_LENGTH;
+ }
+
+ protected String getSecretKeyFactoryName() {
+ return DEFAULT_SECRETE_KEY_FACTORY;
+ }
+
+ protected String getSecretKeyEncryption() {
+ return DEFAULT_SECRETE_KEY_ENCRYPTION;
+ }
+
+ protected String getCipherName() {
+ return DEFAULT_CIPHER;
+ }
+}
--- /dev/null
+package org.argeo.util.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.security.AlgorithmParameters;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import junit.framework.TestCase;
+
+import org.argeo.StreamUtils;
+import org.argeo.util.crypto.PasswordBasedEncryption;
+
+public class PasswordBasedEncryptionTest extends TestCase {
+ public void testEncryptDecrypt() {
+ final String password = "test long password since they are more powerful";
+ PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
+ password.toCharArray());
+ String message = "Hello World!";
+ byte[] encrypted = pbeEnc.encryptString(message);
+ // System.out.println("Encrypted: '" + new String(encrypted) + "'");
+ PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
+ password.toCharArray());
+ InputStream in = null;
+ in = new ByteArrayInputStream(encrypted);
+ String decrypted = pbeDec.decryptAsString(in);
+ // System.out.println("Decrypted: '" + decrypted + "'");
+ StreamUtils.closeQuietly(in);
+ assertEquals(message, decrypted);
+ }
+
+ public void testPBEWithMD5AndDES() throws Exception {
+ String password = "test";
+ String message = "Hello World!";
+
+ byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+ (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+ int count = 1024;
+
+ String cipherAlgorithm = "PBEWithMD5AndDES";
+ String secretKeyAlgorithm = "PBEWithMD5AndDES";
+ SecretKeyFactory keyFac = SecretKeyFactory
+ .getInstance(secretKeyAlgorithm);
+ PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
+ PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
+ SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
+ Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+ ecipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
+ Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+ dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
+
+ byte[] encrypted = ecipher.doFinal(message.getBytes());
+ byte[] decrypted = dcipher.doFinal(encrypted);
+ assertEquals(message, new String(decrypted));
+
+ }
+
+ public void testPBEWithSHA1AndAES() throws Exception {
+ String password = "test";
+ String message = "Hello World!";
+
+ byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+ (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+ int count = 1024;
+ // int keyLength = 256;
+ int keyLength = 128;
+
+ String cipherAlgorithm = "AES/CBC/PKCS5Padding";
+ String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
+ SecretKeyFactory keyFac = SecretKeyFactory
+ .getInstance(secretKeyAlgorithm);
+ PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
+ count, keyLength);
+ SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
+ SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+ Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+ ecipher.init(Cipher.ENCRYPT_MODE, secret);
+ AlgorithmParameters params = ecipher.getParameters();
+ byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
+ Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+ dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
+
+ byte[] encrypted = ecipher.doFinal(message.getBytes());
+ byte[] decrypted = dcipher.doFinal(encrypted);
+ assertEquals(message, new String(decrypted));
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
+ cipherOut.write(message.getBytes());
+ StreamUtils.closeQuietly(cipherOut);
+ byte[] enc = out.toByteArray();
+
+ ByteArrayInputStream in = new ByteArrayInputStream(enc);
+ CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
+ ByteArrayOutputStream dec = new ByteArrayOutputStream();
+ StreamUtils.copy(cipherIn, dec);
+ assertEquals(message, new String(dec.toByteArray()));
+ }
+}