Improved: LoginWorker HashCrypt the type of hash for one-way encryption
(OFBIZ-8537)

Committed wangjunyuan's new patch.

Thanks: wangjunyuan for the new patch to make the PBKDF2 hashed password comply with RFC 6070, the feedbacks of Jacques and Nicolas on the previous patch.

git-svn-id: https://svn.apache.org/repos/asf/ofbiz/trunk@1773066 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java b/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java
index 1e0d29d..8c66937 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java
@@ -46,15 +46,11 @@
     public static final String module = HashCrypt.class.getName();
     public static final String CRYPT_CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
 
-    private static final String PBKDF2_SHA1 ="pbkdf2_sha1";
-    
-    private static final String PBKDF2_SHA256 ="pbkdf2_sha256"; 
-    
-    private static final String PBKDF2_SHA384 ="pbkdf2_sha384";
-    
-    private static final String PBKDF2_SHA512 ="pbkdf2_sha512";
-
-    private static final int PBKDF2_ITERATIONS = UtilProperties.getPropertyAsInteger("security.properties", "password.encrypt.pbkdf2.iterations", 1000);
+    private static final String PBKDF2_SHA1 ="PBKDF2-SHA1";
+    private static final String PBKDF2_SHA256 ="PBKDF2-SHA256"; 
+    private static final String PBKDF2_SHA384 ="PBKDF2-SHA384";
+    private static final String PBKDF2_SHA512 ="PBKDF2-SHA512";
+    private static final int PBKDF2_ITERATIONS = UtilProperties.getPropertyAsInteger("security.properties", "password.encrypt.pbkdf2.iterations", 10000);
     
     public static MessageDigest getMessageDigest(String type) {
         try {
@@ -65,13 +61,13 @@
     }
 
     public static boolean comparePassword(String crypted, String defaultCrypt, String password) {
-        if (crypted.startsWith("{")) {
+    	if (crypted.startsWith("{PBKDF2")) {
+            return doComparePbkdf2(crypted, password);
+    	} else if (crypted.startsWith("{")) {
             // FIXME: should have been getBytes("UTF-8") originally
             return doCompareTypePrefix(crypted, defaultCrypt, password.getBytes());
         } else if (crypted.startsWith("$")) {
             return doComparePosix(crypted, defaultCrypt, password.getBytes(UtilIO.getUtf8()));
-        } else if (crypted.startsWith("pbkdf2")) {
-            return doComparePbkdf2(crypted, password);
         } else {
             // FIXME: should have been getBytes("UTF-8") originally
             return doCompareBare(crypted, defaultCrypt, password.getBytes());
@@ -194,7 +190,12 @@
                 default: 
                     pbkdf2Type = PBKDF2_SHA1; 
             }
-            return pbkdf2Type + "$" + PBKDF2_ITERATIONS + "$" + salt + "$" + new String(hash);
+            StringBuilder sb = new StringBuilder();
+            sb.append("{").append(pbkdf2Type).append("}");
+            sb.append(PBKDF2_ITERATIONS).append("$");
+            sb.append(org.apache.ofbiz.base.util.Base64.base64Encode(salt)).append("$");
+            sb.append(new String(hash)).toString();
+            return sb.toString();
         } catch (InvalidKeySpecException e) {
             throw new GeneralRuntimeException("Error while creating SecretKey", e);
         } catch (NoSuchAlgorithmException e) {
@@ -202,24 +203,24 @@
         }
     }
     
-    public static boolean doComparePbkdf2(String storedPassword, String originalPassword){
+    public static boolean doComparePbkdf2(String crypted, String password){
         try {
-            String[] parts = storedPassword.split("\\$");
-            String hashHead = parts[0];
-            int iterations = Integer.parseInt(parts[1]);
-            byte[] salt = parts[2].getBytes();
-            byte[] hash = Base64.decodeBase64(parts[3].getBytes());
+        	int typeEnd = crypted.indexOf("}");
+            String hashType = crypted.substring(1, typeEnd);
+            String[] parts = crypted.split("\\$");
+            int iterations = Integer.parseInt(parts[0].substring(typeEnd+1));
+            byte[] salt = org.apache.ofbiz.base.util.Base64.base64Decode(parts[1]).getBytes();
+            byte[] hash = Base64.decodeBase64(parts[2].getBytes());
             
-            PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, iterations, hash.length * 8);
-            String hashType = null;
-            switch (hashHead.substring(hashHead.indexOf("_")+4)) {
-                case "256":
+            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, hash.length * 8);
+            switch (hashType.substring(hashType.indexOf("-")+1)) {
+                case "SHA256":
                     hashType = "PBKDF2WithHmacSHA256";
                     break;
-                case "384":
+                case "SHA384":
                     hashType = "PBKDF2WithHmacSHA384";
                     break;
-                case "512":
+                case "SHA512":
                     hashType = "PBKDF2WithHmacSHA512";
                     break;
                 default:  
diff --git a/framework/security/config/security.properties b/framework/security/config/security.properties
index 1fe9452..d7cdac4 100644
--- a/framework/security/config/security.properties
+++ b/framework/security/config/security.properties
@@ -87,7 +87,7 @@
 
 # -- if the type of hash to use for one-way encryption is PBKDF2WithHmacSHA1 or PBKDF2WithHmacSHA256 or PBKDF2WithHmacSHA384 or PBKDF2WithHmacSHA512
 # -- the type of hash to use for one-way encryption needs iteration
-password.encrypt.pbkdf2.iterations=1000
+password.encrypt.pbkdf2.iterations=10000
 
 # -- this is helpful to recover old accounts or to be able to login at all sometimes --
 # -- SHOULD GENERALLY NOT BE TRUE FOR PRODUCTION SITES, but is useful for interim periods when going to password encryption --
diff --git a/framework/security/data/PasswordSecurityDemoData.xml b/framework/security/data/PasswordSecurityDemoData.xml
index f7b6621..a575c8b 100644
--- a/framework/security/data/PasswordSecurityDemoData.xml
+++ b/framework/security/data/PasswordSecurityDemoData.xml
@@ -21,6 +21,15 @@
 <entity-engine-xml>
     <!-- from the securityext component: SecurityExtData.xml -->
     <UserLogin userLoginId="admin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/>
+    <!-- PBKDF2-SHA1
+    	<UserLogin userLoginId="flexadmin" currentPassword="{PBKDF2-SHA1}10000$W0JAMTY4ZTNlZjA=$HkbaHQBTdUbfDuL3tJ1eeyqouQ+66qqIj8Axx9vbHPQ=" passwordHint=""/>
+    	 PBKDF2-SHA256
+    	<UserLogin userLoginId="flexadmin" currentPassword="{PBKDF2-SHA256}10000$W0JAMTRjMzI4YmE=$vWXmPPjyVlvPjN/7v7heWXRPW5ZBWEQTeYv/vxDT5L8=" passwordHint=""/>
+    	 PBKDF2-SHA384
+    	<UserLogin userLoginId="flexadmin" currentPassword="{PBKDF2-SHA384}10000$W0JANWVhMDNjYzE=$H+ouAKild4PCvzFaBxZDbsH2kU1yGJFoCofklWa7YZE=" passwordHint=""/>
+    	 PBKDF2-SHA512
+    	<UserLogin userLoginId="flexadmin" currentPassword="{PBKDF2-SHA512}10000$W0JANTAwZGIwZDY=$M1ZJMdOEjE8bo5E7s5qfza6SZ3dVKwIiwZ8LyKL+2ns=" passwordHint=""/> 
+    -->
     <UserLogin userLoginId="flexadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/>
     <UserLogin userLoginId="demoadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/>
     <UserLogin userLoginId="ltdadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/>