-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34 from cathrkri/feature/password-hashing
Feature/password hashing
- Loading branch information
Showing
39 changed files
with
524 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <module version="4"> | ||
| <component name="CheckStyle-IDEA-Module" serialisationVersion="2"> | ||
| <option name="activeLocationsIds" /> | ||
| </component> | ||
| </module> |
2 changes: 2 additions & 0 deletions
2
helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/Donation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/Feedback.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 0 additions & 92 deletions
92
helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/User.java
This file was deleted.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
...nu/sytemutvikling/team6/models/Inbox.java → ...temutvikling/team6/models/user/Inbox.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package ntnu.sytemutvikling.team6.models; | ||
| package ntnu.sytemutvikling.team6.models.user; | ||
|
|
||
| import java.util.*; | ||
|
|
||
|
|
||
2 changes: 1 addition & 1 deletion
2
...sytemutvikling/team6/models/Language.java → ...utvikling/team6/models/user/Language.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
.../sytemutvikling/team6/models/Message.java → ...mutvikling/team6/models/user/Message.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...tnu/sytemutvikling/team6/models/Role.java → ...ytemutvikling/team6/models/user/Role.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package ntnu.sytemutvikling.team6.models; | ||
| package ntnu.sytemutvikling.team6.models.user; | ||
|
|
||
| /** | ||
| * Available users | ||
|
|
||
2 changes: 1 addition & 1 deletion
2
...sytemutvikling/team6/models/Settings.java → ...utvikling/team6/models/user/Settings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/models/user/User.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| package ntnu.sytemutvikling.team6.models.user; | ||
|
|
||
| import ntnu.sytemutvikling.team6.security.PasswordHasher; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| // TODO: Enhetstesting mangler | ||
|
|
||
| /** | ||
| * Represents a user. | ||
| * | ||
| * @author Robin Strand Prestmo | ||
| */ | ||
| public class User { | ||
| private static final PasswordHasher passwordHasher = new PasswordHasher(); | ||
|
|
||
| private final UUID id; | ||
| private String name; | ||
| private String email; | ||
| private String passwordHash; | ||
| private final Role role; | ||
| private final Settings settings; | ||
| private final Inbox inbox; | ||
|
|
||
| /** | ||
| * Creates a new user | ||
| * | ||
| * @param id gives the user a unique identifier with UUID | ||
| * @param name the name of the user | ||
| * @param email the email of the user | ||
| * @param password the password for the user | ||
| * @param role users role | ||
| * @param settings the user´s settings | ||
| * @param inbox the user´s inbox | ||
| * | ||
| */ | ||
| public User(UUID id, | ||
| String name, | ||
| String email, | ||
| String password, | ||
| Role role, | ||
| Settings settings, | ||
| Inbox inbox) { | ||
| if (id == null) { | ||
| throw new IllegalArgumentException("ID cannot be null."); | ||
| } | ||
|
|
||
| if (name == null || name.isBlank()) { | ||
| throw new IllegalArgumentException("Name cannot be null or blank."); | ||
| } | ||
|
|
||
| if (email == null || email.isBlank() || !email.contains("@") || !email.contains(".")) { | ||
| throw new IllegalArgumentException("Email cannot be null or blank, and must contain '@' and '.'"); | ||
| } | ||
|
|
||
| if (role == null) { | ||
| throw new IllegalArgumentException("Role cannot be null"); | ||
| } | ||
|
|
||
| if (settings == null) { | ||
| throw new IllegalArgumentException("Settings cannot be null"); | ||
| } | ||
|
|
||
| if (inbox == null) { | ||
| throw new IllegalArgumentException("Inbox cannot be null"); | ||
| } | ||
|
|
||
| this.id = id; | ||
| this.name = name; | ||
| this.email = email; | ||
| this.passwordHash = passwordHasher.getHashPassword(password); | ||
| this.role = role; | ||
| this.settings = settings; | ||
| this.inbox = inbox; | ||
| } | ||
|
|
||
| // Add Getters | ||
|
|
||
| public UUID getId() { | ||
| return id; | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public String getEmail() { | ||
| return email; | ||
| } | ||
|
|
||
| public Role getRole() { | ||
| return role; | ||
| } | ||
|
|
||
| public Settings getSettings() { | ||
| return settings; | ||
| } | ||
|
|
||
| public Inbox getInbox() { | ||
| return inbox; | ||
| } | ||
|
|
||
| // Add Setters | ||
|
|
||
| public void setName(String name) { | ||
| if (name == null || name.isBlank()) { | ||
| throw new IllegalArgumentException("Name cannot be null or blank."); | ||
| } | ||
| this.name = name; | ||
| } | ||
|
|
||
| public void setPassword(String password) { | ||
| this.passwordHash = passwordHasher.getHashPassword(password); | ||
| } | ||
|
|
||
| public void setEmail(String email) { | ||
| if (email == null || email.isBlank() || !email.contains("@") || !email.contains(".")) { | ||
| throw new IllegalArgumentException("Email cannot be null or blank, and must contains '@' and '.'"); | ||
| } | ||
| this.email = email; | ||
| } | ||
|
|
||
| // Other methods | ||
|
|
||
| public boolean checkPassword(String password) { | ||
| return passwordHasher.isValidPassword(password, passwordHash); | ||
| } | ||
| } |
93 changes: 93 additions & 0 deletions
93
helpmehelpapplication/src/main/java/ntnu/sytemutvikling/team6/security/PasswordHasher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| package ntnu.sytemutvikling.team6.security; | ||
|
|
||
| import java.security.MessageDigest; | ||
| import java.security.SecureRandom; | ||
| import java.util.Base64; | ||
| import javax.crypto.SecretKeyFactory; | ||
| import javax.crypto.spec.PBEKeySpec; | ||
|
|
||
| /** | ||
| * A utility for hashing and verifying passwords using PBKDF2. | ||
| * | ||
| * <p>The generated hash contains both a random salt and the hashed password, | ||
| * encoded as Base64 string. | ||
| * </p> | ||
| * | ||
| * @author Robin Strand Prestmo | ||
| */ | ||
| public final class PasswordHasher { | ||
| private static final SecureRandom RNG = new SecureRandom(); | ||
|
|
||
| /** | ||
| * Hashes a password using PBKDF2 and a random salt. | ||
| * | ||
| * @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) { | ||
| if (password == null || password.isBlank()) { | ||
| throw new IllegalArgumentException("Password cannot be null or blank."); | ||
| } | ||
|
|
||
| String hashPass = ""; | ||
|
|
||
| try { | ||
| // 1. Create salt | ||
| byte[] salt = new byte[16]; | ||
| RNG.nextBytes(salt); | ||
|
|
||
| // 2. Create PBKDF2 Hash value | ||
| PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100000, 32 * 8); | ||
| SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); | ||
| byte[] hash = factory.generateSecret(spec).getEncoded(); | ||
|
|
||
| // 3. Combine salt and password bytes | ||
| byte[] hashBytes = new byte[48]; | ||
| System.arraycopy(salt, 0, hashBytes, 0, 16); | ||
| System.arraycopy(hash, 0, hashBytes, 16, 32); | ||
|
|
||
| // 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 validating password.", e); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.