diff --git a/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/PasswordHasher.java b/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/PasswordHasher.java index 8c2c7ce..72d3562 100644 --- a/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/PasswordHasher.java +++ b/helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/PasswordHasher.java @@ -1,17 +1,17 @@ package ntnu.sytemutvikling.team6.models; -import javax.crypto.SecretKeyFactory; -import javax.crypto.interfaces.PBEKey; -import javax.crypto.spec.PBEKeySpec; +import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Base64; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; /** - * A password hasher + * A utility for hashing and verifying passwords using PBKDF2. * - *
- * Description - *
+ *The generated hash contains both a random salt and the hashed password, + * encoded as Base64 string. + *
* * @author Robin Strand Prestmo */ @@ -19,13 +19,18 @@ public final class PasswordHasher { private static final SecureRandom RNG = new SecureRandom(); /** - * Get the hash of the password + * Hashes a password using PBKDF2 and a random salt. * - * @param password a string password - * @return a hash secured password + * @param password the password to hash. + * @return a Base64 string containing the salt and the hashed password. + * @throws IllegalArgumentException if the password is null or blank. */ public String getHashPassword(String password) { - String hasPass = ""; + if (password == null || password.isBlank()) { + throw new IllegalArgumentException("Password cannot be null or blank."); + } + + String hashPass = ""; try { // 1. Create salt @@ -33,7 +38,7 @@ public String getHashPassword(String password) { RNG.nextBytes(salt); // 2. Create PBKDF2 Hash value - PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100000, 32*8); + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100000, 32 * 8); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); byte[] hash = factory.generateSecret(spec).getEncoded(); @@ -42,11 +47,47 @@ public String getHashPassword(String password) { System.arraycopy(salt, 0, hashBytes, 0, 16); System.arraycopy(hash, 0, hashBytes, 16, 32); - //4. Turn the combined salt+hash into a string. - hasPass = Base64.getEncoder().encodeToString(hashBytes); + // 4. Turn the combined salt+hash into a string. + hashPass = Base64.getEncoder().encodeToString(hashBytes); + } catch (Exception e) { + throw new RuntimeException("Error while hashing password.", e); + } + return hashPass; + } + + /** + * Checks if the password matches a perviously stored hash. + * + * @param password The password the user types. + * @param hashPass Is the stored hashed password + * @return True if password is valid, otherwise false. + */ + public boolean isValidPassword(String password, String hashPass) { + if (password == null || password.isBlank()) { + return false; + } + + try { + // Extract the bytes + byte[] hashBytes = Base64.getDecoder().decode(hashPass); + + // Get salt + byte[] salt = new byte[16]; + System.arraycopy(hashBytes, 0, salt, 0, 16); + + // Compute the hash on the password the user entered + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100000, 32 * 8); + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + byte[] hash = factory.generateSecret(spec).getEncoded(); + + // Compare results + byte[] storedHash = new byte[32]; + System.arraycopy(hashBytes, 16, storedHash, 0, 32); + + return MessageDigest.isEqual(storedHash, hash); + } catch (Exception e) { - throw new RuntimeException("Error while hasing password.", e); + throw new RuntimeException("Error while validating password.", e); } - return hasPass; } }