From 70538e1286a2b47ecd58cb1cfb7ede8dddff5859 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sun, 2 Oct 2011 17:52:10 +0000 Subject: [PATCH] Working keyring git-svn-id: https://svn.argeo.org/commons/trunk@4772 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../argeo/util/crypto/AbstractKeyring.java | 35 +++++++++- .../argeo/util/crypto/PBEKeySpecCallback.java | 13 ++++ .../crypto/PasswordBasedEncryptionTest.java | 17 ++++- .../org/argeo/jcr/security/JcrKeyring.java | 65 ++++++++++++++++--- 4 files changed, 114 insertions(+), 16 deletions(-) diff --git a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/AbstractKeyring.java b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/AbstractKeyring.java index ee82fd944..98422bdbb 100644 --- a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/AbstractKeyring.java +++ b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/AbstractKeyring.java @@ -10,6 +10,7 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.security.AccessController; +import java.security.MessageDigest; import java.util.Arrays; import java.util.Iterator; @@ -45,7 +46,7 @@ public abstract class AbstractKeyring implements Keyring { * Setup the keyring persistently, {@link #isSetup()} must return true * afterwards */ - protected abstract void setup(); + protected abstract void setup(char[] password); /** Populates the key spec callback */ protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback); @@ -116,6 +117,7 @@ public abstract class AbstractKeyring implements Keyring { try { writer = new OutputStreamWriter(out, charset); writer.write(arr); + writer.flush(); in = new ByteArrayInputStream(out.toByteArray()); set(path, in); } catch (IOException e) { @@ -139,6 +141,32 @@ public abstract class AbstractKeyring implements Keyring { this.charset = charset; } + protected static byte[] hash(char[] password, byte[] salt, + Integer iterationCount) { + ByteArrayOutputStream out = null; + OutputStreamWriter writer = null; + try { + out = new ByteArrayOutputStream(); + writer = new OutputStreamWriter(out, "UTF-8"); + writer.write(password); + MessageDigest pwDigest = MessageDigest.getInstance("SHA-256"); + pwDigest.reset(); + pwDigest.update(salt); + byte[] btPass = pwDigest.digest(out.toByteArray()); + for (int i = 0; i < iterationCount; i++) { + pwDigest.reset(); + btPass = pwDigest.digest(btPass); + } + return btPass; + } catch (Exception e) { + throw new ArgeoException("Cannot hash", e); + } finally { + StreamUtils.closeQuietly(out); + StreamUtils.closeQuietly(writer); + } + + } + class KeyringCallbackHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { @@ -186,8 +214,9 @@ public abstract class AbstractKeyring implements Keyring { defaultCallbackHandler.handle(dialogCbs); } - if (passwordCb.getPassword() != null)// not cancelled - setup(); + if (passwordCb.getPassword() != null) {// not cancelled + setup(passwordCb.getPassword()); + } } if (passwordCb.getPassword() != null) diff --git a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PBEKeySpecCallback.java b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PBEKeySpecCallback.java index a0fe3e0ff..f534c6ae7 100644 --- a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PBEKeySpecCallback.java +++ b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PBEKeySpecCallback.java @@ -17,6 +17,9 @@ public class PBEKeySpecCallback implements Callback { /** Can be null, will trigger secret key encryption if not */ private String secretKeyEncryption; + private String encryptedPasswordHashCipher; + private byte[] encryptedPasswordHash; + public void set(String secretKeyFactory, byte[] salt, Integer iterationCount, Integer keyLength, String secretKeyEncryption) { @@ -25,6 +28,8 @@ public class PBEKeySpecCallback implements Callback { this.iterationCount = iterationCount; this.keyLength = keyLength; this.secretKeyEncryption = secretKeyEncryption; +// this.encryptedPasswordHashCipher = encryptedPasswordHashCipher; +// this.encryptedPasswordHash = encryptedPasswordHash; } public String getSecretKeyFactory() { @@ -47,4 +52,12 @@ public class PBEKeySpecCallback implements Callback { return secretKeyEncryption; } + public String getEncryptedPasswordHashCipher() { + return encryptedPasswordHashCipher; + } + + public byte[] getEncryptedPasswordHash() { + return encryptedPasswordHash; + } + } diff --git a/basic/runtime/org.argeo.basic.nodeps/src/test/java/org/argeo/util/crypto/PasswordBasedEncryptionTest.java b/basic/runtime/org.argeo.basic.nodeps/src/test/java/org/argeo/util/crypto/PasswordBasedEncryptionTest.java index d0687baa9..95d2db230 100644 --- a/basic/runtime/org.argeo.basic.nodeps/src/test/java/org/argeo/util/crypto/PasswordBasedEncryptionTest.java +++ b/basic/runtime/org.argeo.basic.nodeps/src/test/java/org/argeo/util/crypto/PasswordBasedEncryptionTest.java @@ -71,6 +71,10 @@ public class PasswordBasedEncryptionTest extends TestCase { byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c, (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 }; + byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c, + (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99, + (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c, + (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 }; int count = 1024; // int keyLength = 256; @@ -85,9 +89,16 @@ public class PasswordBasedEncryptionTest extends TestCase { 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(); + ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv)); + + // decrypt + keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm); + pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count, + keyLength); + tmp = keyFac.generateSecret(pbeKeySpec); + secret = new SecretKeySpec(tmp.getEncoded(), "AES"); + // 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)); diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrKeyring.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrKeyring.java index 7d9baad95..7383b39ad 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrKeyring.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrKeyring.java @@ -1,10 +1,15 @@ package org.argeo.jcr.security; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.io.OutputStream; +import java.security.AlgorithmParameters; +import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.jcr.Binary; @@ -54,7 +59,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { } @Override - protected void setup() { + protected void setup(char[] password) { Binary binary = null; InputStream in = null; try { @@ -78,7 +83,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { binary = session.getValueFactory().createBinary(in); keyring.setProperty(ARGEO_SALT, binary); - Long iterationCount = username.length() * 200l; + Integer iterationCount = username.length() * 200; keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount); // default algo @@ -88,8 +93,16 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION, "AES"); keyring.setProperty(ARGEO_CIPHER, "AES/CBC/PKCS5Padding"); + // encrypted password hash + // IOUtils.closeQuietly(in); + // JcrUtils.closeQuietly(binary); + // byte[] btPass = hash(password, salt, iterationCount); + // in = new ByteArrayInputStream(btPass); + // binary = session.getValueFactory().createBinary(in); + // keyring.setProperty(ARGEO_PASSWORD, binary); + notYetSavedKeyring.set(keyring); - } catch (RepositoryException e) { + } catch (Exception e) { throw new ArgeoException("Cannot setup keyring", e); } finally { JcrUtils.closeQuietly(binary); @@ -109,6 +122,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { keyring = notYetSavedKeyring.get(); else throw new ArgeoException("Keyring not setup"); + pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY) .getString(), JcrUtils.getBinaryAsBytes(keyring .getProperty(ARGEO_SALT)), @@ -132,6 +146,8 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { Binary binary = null; InputStream in = null; + // ByteArrayOutputStream out = null; + // OutputStream encrypted = null; try { Cipher cipher = createCipher(); @@ -139,19 +155,41 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { throw new ArgeoException("No node at " + path); Node node = session.getNode(path); node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] iv = cipher.getIV(); - if (iv != null) { - JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv); - } + SecureRandom random = new SecureRandom(); + byte[] iv = new byte[16]; + random.nextBytes(iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); + // AlgorithmParameters params = cipher.getParameters(); + // byte[] iv = + // params.getParameterSpec(IvParameterSpec.class).getIV(); + // if (iv != null) + JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv); + + // out = new ByteArrayOutputStream(); + // // encrypted = new CipherOutputStream(out, cipher); + // IOUtils.copy(unencrypted, out); + // byte[] unenc = out.toByteArray(); + // byte[] crypted = cipher.doFinal(unenc); + + // Cipher decipher = createCipher(); + // decipher.init(Cipher.DECRYPT_MODE, secretKey, new + // IvParameterSpec( + // iv)); + // byte[] decrypted = decipher.doFinal(crypted); + // System.out.println("Password :'" + new String(decrypted) + "'"); + + // JcrUtils.setBinaryAsBytes(node, Property.JCR_DATA, crypted); + in = new CipherInputStream(unencrypted, cipher); binary = session.getValueFactory().createBinary(in); node.setProperty(Property.JCR_DATA, binary); } catch (Exception e) { throw new ArgeoException("Cannot encrypt", e); } finally { - IOUtils.closeQuietly(in); + // IOUtils.closeQuietly(out); + // IOUtils.closeQuietly(encrypted); IOUtils.closeQuietly(unencrypted); + IOUtils.closeQuietly(in); JcrUtils.closeQuietly(binary); } } @@ -177,13 +215,20 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { } else { cipher.init(Cipher.DECRYPT_MODE, secretKey); } + + // byte[] arr = JcrUtils.getBinaryAsBytes(node + // .getProperty(Property.JCR_DATA)); + // byte[] arr2 = cipher.doFinal(arr); + // + // return new ByteArrayInputStream(arr2); + binary = node.getProperty(Property.JCR_DATA).getBinary(); encrypted = binary.getStream(); return new CipherInputStream(encrypted, cipher); } catch (Exception e) { throw new ArgeoException("Cannot decrypt", e); } finally { - // IOUtils.closeQuietly(encrypted); + IOUtils.closeQuietly(encrypted); JcrUtils.closeQuietly(binary); } } -- 2.39.2