import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
import java.security.Key;
import java.security.Security;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.argeo.ArgeoException;
import org.argeo.StreamUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/** Simple password based encryption / decryption */
public class PasswordBasedEncryption {
+ private final static Log log = LogFactory
+ .getLog(PasswordBasedEncryption.class);
+
static {
Security.addProvider(new BouncyCastleProvider());
}
public final static Integer DEFAULT_ITERATION_COUNT = 1024;
- public final static Integer DEFAULT_KEY_LENGTH = 256;
+ /** Stronger with 256, but causes problem with Oracle JVM */
+ public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
+ public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
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_CIPHER_NAME = "AES/CBC/PKCS5Padding";
public final static String DEFAULT_CHARSET = "UTF-8";
+ private Integer iterationCount = DEFAULT_ITERATION_COUNT;
+ private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+ private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+ private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+ private String cipherName = DEFAULT_CIPHER_NAME;
+
private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
(byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
(byte) 0x03 };
(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;
+ private Key key;
+ private Cipher ecipher;
+ private Cipher dcipher;
/**
* Default provider is bouncy castle, in order to have consistent behaviour
public PasswordBasedEncryption(char[] password, byte[] passwordSalt,
byte[] initializationVector) {
try {
- byte[] salt = new byte[8];
- System.arraycopy(passwordSalt, 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(initializationVector, 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);
+ initKeyAndCiphers(password, passwordSalt, initializationVector);
+ } catch (InvalidKeyException e) {
+ Integer previousSecreteKeyLength = secreteKeyLength;
+ secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
+ log.warn("'" + e.getMessage() + "', will use " + secreteKeyLength
+ + " secrete key length instead of "
+ + previousSecreteKeyLength);
+ try {
+ initKeyAndCiphers(password, passwordSalt, initializationVector);
+ } catch (Exception e1) {
+ throw new ArgeoException(
+ "Cannot get secret key (with restricted length)", e1);
}
- ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
- 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);
}
}
+ protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
+ byte[] initializationVector) throws GeneralSecurityException {
+ byte[] salt = new byte[8];
+ System.arraycopy(passwordSalt, 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(initializationVector, 0, iv, 0, iv.length);
+
+ 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(), securityProviderName);
+ ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+ dcipher = Cipher.getInstance(getCipherName());
+ dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+ }
+
public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
throws IOException {
try {
}
protected Integer getIterationCount() {
- return DEFAULT_ITERATION_COUNT;
+ return iterationCount;
}
protected Integer getKeyLength() {
- return DEFAULT_KEY_LENGTH;
+ return secreteKeyLength;
}
protected String getSecretKeyFactoryName() {
- return DEFAULT_SECRETE_KEY_FACTORY;
+ return secreteKeyFactoryName;
}
protected String getSecretKeyEncryption() {
- return DEFAULT_SECRETE_KEY_ENCRYPTION;
+ return secreteKeyEncryption;
}
protected String getCipherName() {
- return DEFAULT_CIPHER;
+ return cipherName;
+ }
+
+ public void setIterationCount(Integer iterationCount) {
+ this.iterationCount = iterationCount;
+ }
+
+ public void setSecreteKeyLength(Integer keyLength) {
+ this.secreteKeyLength = keyLength;
+ }
+
+ public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+ this.secreteKeyFactoryName = secreteKeyFactoryName;
+ }
+
+ public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+ this.secreteKeyEncryption = secreteKeyEncryption;
+ }
+
+ public void setCipherName(String cipherName) {
+ this.cipherName = cipherName;
}
public void setSecurityProviderName(String securityProviderName) {
/** JCR based implementation of a keyring */
public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
+ /**
+ * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
+ * case
+ */
+ public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
+ public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+ public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+ public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
+
+ private Integer iterationCountFactor = 200;
+ private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+ private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+ private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+ private String cipherName = DEFAULT_CIPHER_NAME;
+
private Session session;
/**
binary = session.getValueFactory().createBinary(in);
keyring.setProperty(ARGEO_SALT, binary);
- Integer iterationCount = username.length() * 200;
+ Integer iterationCount = username.length() * iterationCountFactor;
keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
// default algo
// TODO check if algo and key length are available, use DES if not
- keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, "PBKDF2WithHmacSHA1");
- keyring.setProperty(ARGEO_KEY_LENGTH, 256l);
- keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, "AES");
- keyring.setProperty(ARGEO_CIPHER, "AES/CBC/PKCS5Padding");
+ keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
+ keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
+ keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION,
+ secreteKeyEncryption);
+ keyring.setProperty(ARGEO_CIPHER, cipherName);
// encrypted password hash
// IOUtils.closeQuietly(in);
public synchronized void setSession(Session session) {
this.session = session;
}
+
+ public void setIterationCountFactor(Integer iterationCountFactor) {
+ this.iterationCountFactor = iterationCountFactor;
+ }
+
+ public void setSecreteKeyLength(Long keyLength) {
+ this.secreteKeyLength = keyLength;
+ }
+
+ public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+ this.secreteKeyFactoryName = secreteKeyFactoryName;
+ }
+
+ public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+ this.secreteKeyEncryption = secreteKeyEncryption;
+ }
+
+ public void setCipherName(String cipherName) {
+ this.cipherName = cipherName;
+ }
+
}
\ No newline at end of file