Implement 389 DS's PBKDF2_SHA256 password scheme.
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / osgi / useradmin / DigestUtils.java
index 51d18349b2a662a762871e9464c5e24370a01c77..511c2fede5e747e8e7e3adef942a09063793c579 100644 (file)
@@ -1,13 +1,21 @@
 package org.argeo.osgi.useradmin;
 
+import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.security.spec.KeySpec;
 import java.util.Arrays;
 
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
 /** Utilities around digests, mostly those related to passwords. */
 class DigestUtils {
+       final static String PASSWORD_SCHEME_SHA = "SHA";
+       final static String PASSWORD_SCHEME_PBKDF2_SHA256 = "PBKDF2_SHA256";
+
        static byte[] sha1(byte[] bytes) {
                try {
                        MessageDigest digest = MessageDigest.getInstance("SHA1");
@@ -19,6 +27,40 @@ class DigestUtils {
                }
        }
 
+       static byte[] toPasswordScheme(String passwordScheme, char[] password, byte[] salt, Integer iterations,
+                       Integer keyLength) {
+               try {
+                       if (PASSWORD_SCHEME_SHA.equals(passwordScheme)) {
+                               MessageDigest digest = MessageDigest.getInstance("SHA1");
+                               byte[] bytes = charsToBytes(password);
+                               digest.update(bytes);
+                               return digest.digest();
+                       } else if (PASSWORD_SCHEME_PBKDF2_SHA256.equals(passwordScheme)) {
+                               KeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
+
+                               SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+                               final int ITERATION_LENGTH = 4;
+                               byte[] key = f.generateSecret(spec).getEncoded();
+                               byte[] result = new byte[ITERATION_LENGTH + salt.length + key.length];
+                               byte iterationsArr[] = new BigInteger(iterations.toString()).toByteArray();
+                               if (iterationsArr.length < ITERATION_LENGTH) {
+                                       Arrays.fill(result, 0, ITERATION_LENGTH - iterationsArr.length, (byte) 0);
+                                       System.arraycopy(iterationsArr, 0, result, ITERATION_LENGTH - iterationsArr.length,
+                                                       iterationsArr.length);
+                               } else {
+                                       System.arraycopy(iterationsArr, 0, result, 0, ITERATION_LENGTH);
+                               }
+                               System.arraycopy(salt, 0, result, ITERATION_LENGTH, salt.length);
+                               System.arraycopy(key, 0, result, ITERATION_LENGTH + salt.length, key.length);
+                               return result;
+                       } else {
+                               throw new UnsupportedOperationException("Unkown password scheme " + passwordScheme);
+                       }
+               } catch (Exception e) {
+                       throw new UserDirectoryException("Cannot digest", e);
+               }
+       }
+
        static char[] bytesToChars(Object obj) {
                if (obj instanceof char[])
                        return (char[]) obj;