From 469ea017cafd416d1ea6b7df33db73dca063670d Mon Sep 17 00:00:00 2001
From: Sinisa Veseli <sveseli@aps.anl.gov>
Date: Thu, 9 Apr 2015 15:13:39 +0000
Subject: [PATCH] added java utility to verify crypted passwords from the local
 db; modified login controller to use it

---
 .../portal/controllers/LoginController.java   |  3 +-
 .../aps/dm/portal/utilities/CryptUtility.java | 76 +++++++++++++++++++
 2 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/utilities/CryptUtility.java

diff --git a/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/controllers/LoginController.java b/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/controllers/LoginController.java
index 2376ba0c..c1639afe 100644
--- a/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/controllers/LoginController.java
+++ b/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/controllers/LoginController.java
@@ -9,6 +9,7 @@ import gov.anl.aps.dm.portal.constants.RoleTypeName;
 import gov.anl.aps.dm.portal.model.beans.RoleTypeFacade;
 import gov.anl.aps.dm.portal.model.beans.UserInfoFacade;
 import gov.anl.aps.dm.portal.model.entities.UserInfo;
+import gov.anl.aps.dm.portal.utilities.CryptUtility;
 import gov.anl.aps.dm.portal.utilities.LdapUtility;
 import gov.anl.aps.dm.portal.utilities.SessionUtility;
 import java.io.Serializable;
@@ -112,7 +113,7 @@ public class LoginController implements Serializable
         }
 
         boolean validCredentials = false;
-        if (user.getPassword() != null && user.getPassword().equals(password)) {
+        if (user.getPassword() != null && CryptUtility.verifyPasswordWithPbkdf2(password, user.getPassword())) {
             logger.debug("User " + username + " is authorized by DM DB");
             validCredentials = true;
         }
diff --git a/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/utilities/CryptUtility.java b/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/utilities/CryptUtility.java
new file mode 100644
index 00000000..a6600e8b
--- /dev/null
+++ b/src/java/DmWebPortal/src/java/gov/anl/aps/dm/portal/utilities/CryptUtility.java
@@ -0,0 +1,76 @@
+package gov.anl.aps.dm.portal.utilities;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Random;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import org.apache.log4j.Logger;
+import org.primefaces.util.Base64;
+
+public class CryptUtility {
+
+    private static final Logger logger = Logger.getLogger(CryptUtility.class.getName());
+
+    private static final String SECRET_KEY_FACTORY_TYPE = "PBKDF2WithHmacSHA1";
+    private static final int PBDKF2_ITERATIONS = 1003;
+    private static final int PBDKF2_KEY_LENGTH_IN_BITS = 192;
+    private static final int SALT_LENGTH_IN_BYTES = 4;
+    private static final char[] SALT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
+    private static final String SALT_DELIMITER = "$";
+    
+    public static String randomString(char[] characterSet, int length) {
+        Random random = new SecureRandom();
+        char[] result = new char[length];
+        for (int i = 0; i < result.length; i++) {
+            // picks a random index out of character set > random character
+            int randomCharIndex = random.nextInt(characterSet.length);
+            result[i] = characterSet[randomCharIndex];
+        }
+        return new String(result);
+    }
+
+    public static String cryptPasswordWithPbkdf2(String password) {
+        String salt = randomString(SALT_CHARSET, SALT_LENGTH_IN_BYTES);
+        return saltAndCryptPasswordWithPbkdf2(password, salt);
+    }
+
+    public static String saltAndCryptPasswordWithPbkdf2(String password, String salt) {
+        char[] passwordChars = password.toCharArray();
+        byte[] saltBytes = salt.getBytes();
+
+        PBEKeySpec spec = new PBEKeySpec(
+                passwordChars,
+                saltBytes,
+                PBDKF2_ITERATIONS,
+                PBDKF2_KEY_LENGTH_IN_BITS
+        );
+        SecretKeyFactory key;
+        try {
+            key = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_TYPE);
+            byte[] hashedPassword = key.generateSecret(spec).getEncoded();
+            String encodedPassword = Base64.encodeToString(hashedPassword, true);
+            return salt + SALT_DELIMITER + encodedPassword;
+        } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
+            // Should not happen
+            logger.error("Password cannot be crypted: " + ex);
+        }
+        return null;
+    }
+
+    public static boolean verifyPasswordWithPbkdf2(String password, String cryptedPassword) {
+        int saltEnd = cryptedPassword.indexOf(SALT_DELIMITER);
+        String salt = cryptedPassword.substring(0,saltEnd);
+        return cryptedPassword.equals(saltAndCryptPasswordWithPbkdf2(password, salt));
+    }
+    
+    
+    public static void main(String[] args) {
+        String password = "cdb";
+        System.out.println("Original password: " + password);
+        String cryptedPassword = cryptPasswordWithPbkdf2(password);
+        System.out.println("Crypted password: " + cryptedPassword);
+        System.out.println("Verified: " + verifyPasswordWithPbkdf2(password, cryptedPassword));
+    }
+}
-- 
GitLab