Working keyring
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 2 Oct 2011 17:52:10 +0000 (17:52 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 2 Oct 2011 17:52:10 +0000 (17:52 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@4772 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/AbstractKeyring.java
basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PBEKeySpecCallback.java
basic/runtime/org.argeo.basic.nodeps/src/test/java/org/argeo/util/crypto/PasswordBasedEncryptionTest.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrKeyring.java

index ee82fd944c072a1cebe5a17dd7376b73046d1801..98422bdbb3f74c27f8a9892fd1731a09c767cb59 100644 (file)
@@ -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)
index a0fe3e0ff8f48a12f8995e57c44da5e3916f21b9..f534c6ae7b981ccd4a3901ee7990166d9580b87f 100644 (file)
@@ -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;
+       }
+
 }
index d0687baa92e7c6bc1279740ed0d0ef14beee7b16..95d2db2305ea2c7dff4fc9a29f27618cc605b849 100644 (file)
@@ -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));
 
index 7d9baad956ed9b9481230619ed80b4cafbc6a6cc..7383b39ad301e20f2ab5488747228c67d6b9ccf1 100644 (file)
@@ -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);
                }
        }