Fix issue with Oracle JVM
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 11 Oct 2012 19:48:06 +0000 (19:48 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 11 Oct 2012 19:48:06 +0000 (19:48 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@5600 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java
security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/ListBCCapabilities.java [deleted file]
security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java
server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml
server/plugins/org.argeo.jcr.ui.explorer/jcrexplorer.properties [new file with mode: 0644]

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) {
index 04974bdd7bd9281ede437f283b9d0476f9e54852..f346d3b448f2b9e9c98a7c426b9c4ac518815472 100644 (file)
@@ -42,6 +42,21 @@ import org.argeo.security.crypto.PBEKeySpecCallback;
 
 /** 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;
 
        /**
@@ -97,15 +112,16 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        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);
@@ -254,4 +270,25 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
        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
diff --git a/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/ListBCCapabilities.java b/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/ListBCCapabilities.java
deleted file mode 100644 (file)
index a95fba2..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.argeo.security.crypto;\r
-\r
-import java.security.Provider;\r
-import java.security.Security;\r
-import java.util.Iterator;\r
-\r
-import org.bouncycastle.jce.provider.BouncyCastleProvider;\r
-\r
-/**\r
- * List the available capabilities for ciphers, key agreement, macs, message\r
- * digests, signatures and other objects in the BC provider.\r
- */\r
-public class ListBCCapabilities {\r
-       public static void main(String[] args) {\r
-               Security.addProvider(new BouncyCastleProvider());\r
-\r
-               Provider[] providers = Security.getProviders();\r
-               for (Provider provider : providers) {\r
-                       System.out.println(provider.getName());\r
-                       System.out.println(" " + provider.getVersion());\r
-                       System.out.println(" " + provider.getInfo());\r
-               }\r
-               Provider provider = Security.getProvider("BC");\r
-               // Provider provider = Security.getProvider(null);\r
-\r
-               Iterator it = provider.keySet().iterator();\r
-\r
-               while (it.hasNext()) {\r
-                       String entry = (String) it.next();\r
-\r
-                       // this indicates the entry refers to another entry\r
-\r
-                       if (entry.startsWith("Alg.Alias.")) {\r
-                               entry = entry.substring("Alg.Alias.".length());\r
-                       }\r
-\r
-                       String factoryClass = entry.substring(0, entry.indexOf('.'));\r
-                       String name = entry.substring(factoryClass.length() + 1);\r
-\r
-                       System.out.println(factoryClass + ": " + name);\r
-               }\r
-       }\r
-}\r
index bea93188fd2df2810512b34c5754de26289565c7..ebcbdffc3071f5998b359857af61a7f500838f48 100644 (file)
@@ -32,26 +32,31 @@ import javax.xml.bind.DatatypeConverter;
 
 import junit.framework.TestCase;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.argeo.StreamUtils;
 import org.argeo.security.crypto.PasswordBasedEncryption;
 
 public class PasswordBasedEncryptionTest extends TestCase {
+       private final static Log log = LogFactory
+                       .getLog(PasswordBasedEncryptionTest.class);
+
        public void testEncryptDecrypt() {
                final String password = "test long password since they are safer";
                PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
                                password.toCharArray());
                String message = "Hello World!";
-               System.out.println("Password:\t'" + password + "'");
-               System.out.println("Message:\t'" + message + "'");
+               log.info("Password:\t'" + password + "'");
+               log.info("Message:\t'" + message + "'");
                byte[] encrypted = pbeEnc.encryptString(message);
-               System.out.println("Encrypted:\t'"
+               log.info("Encrypted:\t'"
                                + DatatypeConverter.printBase64Binary(encrypted) + "'");
                PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
                                password.toCharArray());
                InputStream in = null;
                in = new ByteArrayInputStream(encrypted);
                String decrypted = pbeDec.decryptAsString(in);
-               System.out.println("Decrypted:\t'" + decrypted + "'");
+               log.info("Decrypted:\t'" + decrypted + "'");
                StreamUtils.closeQuietly(in);
                assertEquals(message, decrypted);
        }
index 243ed50255f0b528fb12952f384be7009a689fa6..9eb9fc9ad4efe7b3f99e74900b9103b9362f7346 100644 (file)
@@ -5,6 +5,14 @@
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
 
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:jcrexplorer.properties</value>
+               </property>
+       </bean>
+
        <bean id="nodeSession" class="org.argeo.jcr.spring.ThreadBoundSession">
                <property name="repository" ref="nodeRepository" />
        </bean>
@@ -12,6 +20,7 @@
        <bean id="keyring" class="org.argeo.security.jcr.JcrKeyring">
                <property name="session" ref="nodeSession" />
                <property name="defaultCallbackHandler" ref="defaultCallbackHandler" />
+               <property name="secreteKeyLength" value="${argeo.keyring.secreteKeyLength}" />
        </bean>
 
        <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" />
diff --git a/server/plugins/org.argeo.jcr.ui.explorer/jcrexplorer.properties b/server/plugins/org.argeo.jcr.ui.explorer/jcrexplorer.properties
new file mode 100644 (file)
index 0000000..0228d47
--- /dev/null
@@ -0,0 +1 @@
+argeo.keyring.secreteKeyLength=256
\ No newline at end of file