jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1466455 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtility.java test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilityTest.java
Date Wed, 10 Apr 2013 12:52:16 GMT
Author: thomasm
Date: Wed Apr 10 12:52:15 2013
New Revision: 1466455

URL: http://svn.apache.org/r1466455
Log:
OAK-697 Security: support for PBKDF2 password hashing

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtility.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilityTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtility.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtility.java?rev=1466455&r1=1466454&r2=1466455&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtility.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtility.java
Wed Apr 10 12:52:15 2013
@@ -17,10 +17,16 @@
 package org.apache.jackrabbit.oak.spi.security.user.util;
 
 import java.io.UnsupportedEncodingException;
+import java.security.Key;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
 import javax.annotation.Nullable;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
 
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
@@ -39,6 +45,8 @@ public final class PasswordUtility {
     private static final int NO_ITERATIONS = 1;
     private static final String ENCODING = "UTF-8";
 
+    private static final String PBKDF2_PREFIX = "PBKDF2";
+    
     public static final String DEFAULT_ALGORITHM = "SHA-256";
     public static final int DEFAULT_SALT_SIZE = 8;
     public static final int DEFAULT_ITERATIONS = 1000;
@@ -171,7 +179,7 @@ public final class PasswordUtility {
 
     //------------------------------------------------------------< private >---
 
-    private static String generateHash(String pwd, String algorithm, String salt, int iterations)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
+    public static String generateHash(String pwd, String algorithm, String salt, int iterations)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
         StringBuilder passwordHash = new StringBuilder();
         passwordHash.append('{').append(algorithm).append('}');
         if (salt != null && !salt.isEmpty()) {
@@ -182,7 +190,13 @@ public final class PasswordUtility {
             if (iterations > NO_ITERATIONS) {
                 passwordHash.append(iterations).append(DELIMITER);
             }
-            passwordHash.append(generateDigest(data.toString(), algorithm, iterations));
+            String digest;
+            if (algorithm.startsWith(PBKDF2_PREFIX)) {
+                digest = generatePBKDF2(pwd, salt, algorithm, iterations, 128);
+            } else {
+                digest = generateDigest(data.toString(), algorithm, iterations);
+            }
+            passwordHash.append(digest);
         } else {
             // backwards compatible to jr 2.0: no salt, no iterations
             passwordHash.append(Text.digest(algorithm, pwd.getBytes(ENCODING)));
@@ -195,13 +209,57 @@ public final class PasswordUtility {
         byte[] salt = new byte[saltSize];
         random.nextBytes(salt);
 
-        StringBuilder res = new StringBuilder(salt.length * 2);
-        for (byte b : salt) {
+        return convertBytesToHex(salt);
+    }
+    
+    /**
+     * Convert a byte array to a hex encoded string.
+     *
+     * @param bytes the byte array
+     * @return the hex encoded string
+     */
+    public static String convertBytesToHex(byte[] bytes) {
+        StringBuilder res = new StringBuilder(bytes.length * 2);
+        for (byte b : bytes) {
             res.append(Text.hexTable[(b >> 4) & 15]);
             res.append(Text.hexTable[b & 15]);
         }
         return res.toString();
     }
+    
+    /**
+     * Convert a hex encoded string to a byte array.
+     *
+     * @param s the hex encoded string
+     * @return the byte array
+     */
+    public static byte[] convertHexToBytes(String s) {
+        int len = s.length();
+        if (len % 2 != 0) {
+            throw new IllegalArgumentException("Not a hex encoded byte array: " + s);
+        }
+        byte[] bytes = new byte[len / 2];
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] = (byte) (
+                    (Character.digit(s.charAt(i + i), 16) << 4) + 
+                    Character.digit(s.charAt(i + i + 1), 16));
+        }
+        return bytes;
+    }
+    
+    private static String generatePBKDF2(String pwd, String salt, String algorithm, int iterations,
int keyLength) throws NoSuchAlgorithmException {
+        // for example PBKDF2WithHmacSHA1
+        SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
+        byte[] saltBytes = convertHexToBytes(salt);
+        KeySpec keyspec = new PBEKeySpec(pwd.toCharArray(), saltBytes, iterations, keyLength);
+        try {
+            Key key = factory.generateSecret(keyspec);
+            byte[] bytes = key.getEncoded();
+            return convertBytesToHex(bytes);
+        } catch (InvalidKeySpecException e) {
+            throw new NoSuchAlgorithmException(algorithm, e);
+        }  
+    }
 
     private static String generateDigest(String data, String algorithm, int iterations) throws
UnsupportedEncodingException, NoSuchAlgorithmException {
         byte[] bytes = data.getBytes(ENCODING);
@@ -212,12 +270,7 @@ public final class PasswordUtility {
             bytes = md.digest(bytes);
         }
 
-        StringBuilder res = new StringBuilder(bytes.length * 2);
-        for (byte b : bytes) {
-            res.append(Text.hexTable[(b >> 4) & 15]);
-            res.append(Text.hexTable[b & 15]);
-        }
-        return res.toString();
+        return convertBytesToHex(bytes);
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilityTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilityTest.java?rev=1466455&r1=1466454&r2=1466455&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilityTest.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilityTest.java
Wed Apr 10 12:52:15 2013
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -147,4 +148,15 @@ public class PasswordUtilityTest {
             previous = pw;
         }
     }
+    
+    @Test
+    public void testPBKDF2WithHmacSHA1() throws Exception {
+        String algo = "PBKDF2WithHmacSHA1";
+        // test vector from http://tools.ietf.org/html/rfc6070
+        String hash = PasswordUtility.generateHash(
+                "pass\0word", algo, 
+                PasswordUtility.convertBytesToHex("sa\0lt".getBytes()), 4096);
+        assertEquals("{PBKDF2WithHmacSHA1}7361006c74-4096-56fa6aa75548099dcc37d7f03425e0c3",
hash);
+    }
+    
 }
\ No newline at end of file



Mime
View raw message