Implement 389 DS's PBKDF2_SHA256 password scheme.
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / osgi / useradmin / DigestUtils.java
index f6a237bcc0e4e355dd464af70c180c49bfeb45ff..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;
@@ -42,7 +84,28 @@ class DigestUtils {
                return bytes;
        }
 
-       private DigestUtils() {
+       static String sha1str(String str) {
+               byte[] hash = sha1(str.getBytes(StandardCharsets.UTF_8));
+               return encodeHexString(hash);
        }
 
+       final private static char[] hexArray = "0123456789abcdef".toCharArray();
+
+       /**
+        * From
+        * http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to
+        * -a-hex-string-in-java
+        */
+       public static String encodeHexString(byte[] bytes) {
+               char[] hexChars = new char[bytes.length * 2];
+               for (int j = 0; j < bytes.length; j++) {
+                       int v = bytes[j] & 0xFF;
+                       hexChars[j * 2] = hexArray[v >>> 4];
+                       hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+               }
+               return new String(hexChars);
+       }
+
+       private DigestUtils() {
+       }
 }