From bc4739d215e53ce7415699c5923ebc5391a2a6c7 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 11 Oct 2012 19:48:06 +0000 Subject: [PATCH] Fix issue with Oracle JVM git-svn-id: https://svn.argeo.org/commons/trunk@5600 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../crypto/PasswordBasedEncryption.java | 120 ++++++++++++------ .../org/argeo/security/jcr/JcrKeyring.java | 47 ++++++- .../security/crypto/ListBCCapabilities.java | 43 ------- .../crypto/PasswordBasedEncryptionTest.java | 13 +- .../META-INF/spring/jcr.xml | 9 ++ .../jcrexplorer.properties | 1 + 6 files changed, 144 insertions(+), 89 deletions(-) delete mode 100644 security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/ListBCCapabilities.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/jcrexplorer.properties diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java index 63cdc6c0c..8b1d55d4a 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java @@ -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) { diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java index 04974bdd7..f346d3b44 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java @@ -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 index a95fba274..000000000 --- a/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/ListBCCapabilities.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.argeo.security.crypto; - -import java.security.Provider; -import java.security.Security; -import java.util.Iterator; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -/** - * List the available capabilities for ciphers, key agreement, macs, message - * digests, signatures and other objects in the BC provider. - */ -public class ListBCCapabilities { - public static void main(String[] args) { - Security.addProvider(new BouncyCastleProvider()); - - Provider[] providers = Security.getProviders(); - for (Provider provider : providers) { - System.out.println(provider.getName()); - System.out.println(" " + provider.getVersion()); - System.out.println(" " + provider.getInfo()); - } - Provider provider = Security.getProvider("BC"); - // Provider provider = Security.getProvider(null); - - Iterator it = provider.keySet().iterator(); - - while (it.hasNext()) { - String entry = (String) it.next(); - - // this indicates the entry refers to another entry - - if (entry.startsWith("Alg.Alias.")) { - entry = entry.substring("Alg.Alias.".length()); - } - - String factoryClass = entry.substring(0, entry.indexOf('.')); - String name = entry.substring(factoryClass.length() + 1); - - System.out.println(factoryClass + ": " + name); - } - } -} diff --git a/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java b/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java index bea93188f..ebcbdffc3 100644 --- a/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java +++ b/security/runtime/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java @@ -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); } diff --git a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml index 243ed5025..9eb9fc9ad 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/jcr.xml @@ -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"> + + + + osgibundle:jcrexplorer.properties + + + @@ -12,6 +20,7 @@ + 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 index 000000000..0228d47ee --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/jcrexplorer.properties @@ -0,0 +1 @@ +argeo.keyring.secreteKeyLength=256 \ No newline at end of file -- 2.30.2