Fix issue with Oracle JVM
[lgpl/argeo-commons.git] / security / runtime / org.argeo.security.core / src / main / java / org / argeo / security / crypto / PasswordBasedEncryption.java
index 63cdc6c0c8c11e7e260e2db76212779c89670b0e..8b1d55d4a66eacc2f4362a9e57c286412ba3a9f8 100644 (file)
@@ -20,6 +20,8 @@ import java.io.ByteArrayOutputStream;
 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;
 
@@ -32,23 +34,36 @@ import javax.crypto.spec.IvParameterSpec;
 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 };
@@ -57,9 +72,9 @@ public class PasswordBasedEncryption {
                        (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
@@ -82,39 +97,50 @@ public class PasswordBasedEncryption {
        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 {
@@ -188,23 +214,43 @@ public class PasswordBasedEncryption {
        }
 
        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) {