User Login The two methods give you a sense of how to implement a secure login (
ID: 3808557 • Letter: U
Question
User Login
The two methods give you a sense of how to implement a secure login (e.g. for a website). You begin by “hashing” a password using a “salt.” A hash function1 converts an arbitrary amount of input into an output of fixed size (in this case, take a string and represent it as an integer) in a way that is hard to find the original input given the output. It is common security practice to store password hashes – this way if someone breaks in, they have user hashes, not their original passwords. A salt2 is a random number, one per user, that is combined with a user’s password when hashing to make sure that even if two users have the same password, their hashes are different (thus, even if a hacker reverse-engineers one hash, it doesn’t save them work for even those hashes that correspond to the same password).
The methods
The hashPassword method has you try this yourself. In general, there are many hash functions, and they can get quite mathematically complex. So, we will simply use Java’s String.hashCode() method. You are to take a supplied password (e.g. "foo" ), prefix it with a supplied salt (separated by a pipe; so if the salt was 1 you would get "1|foo" ), and then get a string representation of the String.hashCode() output (in this case the resulting hash would be "49048187" ).
Now, implement the attemptLogin method. You will be supplied a map of User objects (user name ->User object; the User class has been written for you), a user name with which to login, and an attempted password. Login should only succeed if the user name exists, and the password, once hashed with the corresponding user’s salt, matches the hash previously stored for the user.
User Class
public class User {
final private String userName;
final private int salt;
final private String pwHash;
/**
* Initialize the user
*
* @param userName user name
* @param salt password salt
* @param pwHash salted hash
*/
public User(String userName, int salt, String pwHash) {
this.userName = userName;
this.salt = salt;
this.pwHash = pwHash;
}
/**
* Get the user name
*
* @return user name
*/
public String getUserName() {
return userName;
}
/**
* Get the password salt
*
* @return password salt
*/
public int getSalt() {
return salt;
}
/**
* Checks a supplied password hash
*
* @param pwHash comparison hash
* @return true if input hash matches user's hash
*/
public boolean hashMatches(String pwHash) {
return this.pwHash.equals(pwHash);
}
@Override
public String toString() {
return String.format("%s (%d, %s)", userName, salt, pwHash);
}
}
Code I need help ( the attemptLogin method is giving me trouble, hash password passes):
/**
* Simple salted password hash using Java's String.hashCode():
* 1. Creates a new prefixed string: "salt|password"
* 2. Get the hashCode() value of this string
* 3. Convert this value to a string
*
* Example: hashPassword("foo", 1)
* 1. "1|foo"
* 2. "1|foo".hashCode() => 49048187
* 3. return "49048187"
*
* @param password password to hash
* @param salt salt to use to hash
* @return salted hash of supplied password
*/
public static String hashPassword(String password, int salt) {
return (salt+"|"+password).hashCode()+"";
}
/**
* Validates a login to a system given
* a map of users (userName -> user),
* a userName to attempt, and a password
*
* @param users valid users
* @param userName user's login
* @param password attempted password
* @return reference to user if successful, null otherwise
*/
public static User attemptLogin(Map<String, User> users, String userName, String password) {
for (String key : users.keySet()) {
if(key.equals(userName)){ // if user exist in map
User user=users.get(userName); // taking out the user object out
int salt=user.getSalt();
/* retriving salt value from use object and appending with user password and calculating
hashcode on top of that then finally adding empty string to covert the calculated hashcode into string*/
String hashedPasswd=(salt+"|"+password).hashCode()+"";
if(hashedPasswd.equals(password)){
return user; //if user provided password matches to the password which user object contains then returning user object otherwise null
}
}
}
return null;
}
I am using these test cases to test the code, but attemptLogin fails.
// users
final User alice = new User("alice", 5, "1859628144"); // password123
final User bob = new User("bob", 98, "-1938366791"); // fuzzybunny
final Map<String, User> users1 = new HashMap<>();
for (User u : new User[] {alice, bob}) {
users1.put(u.getUserName(), u);
}
final User wendy = new User("wendy", -3, "1141373183"); // computerscienceisfun!
final User walter = new User("walter", 13, "873346171"); // computerscienceisfun!
final Map<String, User> users2 = new HashMap<>();
for (User u : new User[] {wendy, walter}) {
users2.put(u.getUserName(), u);
}
// tester
private void _testLogin(Map<String, User> users, String userName, String password, User uE) {
User result = null;
try {
result = UserLogin.attemptLogin(users, userName, password);
} catch (ExitException e) {}
assertEquals(uE, result);
}
Where is fails
_testLogin(users1, "alice", "password123", alice); - attemptLogin keeps return null, expected succesfull login
_testLogin(users2, "alice", "password123", null);
_testLogin(users1, "alice", "fuzzybunny", null);
_testLogin(users1, "alice", "goodPassword", null);
_testLogin(users1, "alice", "computerscienceisfun!", null);
BONUS TESTS TO PASS
_testLogin(users1, "bob", "password123", null);
_testLogin(users1, "bob", "fuzzybunny", bob);
_testLogin(users2, "bob", "fuzzybunny", null);
_testLogin(users1, "bob", "goodPassword", null);
_testLogin(users1, "bob", "computerscienceisfun!", null);
_testLogin(users1, "wendy", "password123", null);
_testLogin(users1, "wendy", "fuzzybunny", null);
_testLogin(users1, "wendy", "goodPassword", null);
_testLogin(users1, "wendy", "computerscienceisfun!", null);
_testLogin(users2, "wendy", "computerscienceisfun!", wendy);
_testLogin(users1, "walter", "correcthorsebatterystaple", null);
_testLogin(users1, "walter", "foobarbazqux", null);
_testLogin(users1, "walter", "computerscienceisfun!", null);
_testLogin(users2, "walter", "computerscienceisfun!", walter);
Explanation / Answer
Explanation:
In the attemptlogin method you are comparing hashedpassword stored in the user with the normal string password , so its returning no match.
if(hashedPasswd.equals(password)){
return user;
}
instead use the below:
if(user.hashMatches(hashedPasswd)){
return user;
}
Code:
package chegg;
import java.util.HashMap;
import java.util.Map;
class User {
final private String userName;
final private int salt;
final private String pwHash;
/**
* Initialize the user
*
* @param userName user name
* @param salt password salt
* @param pwHash salted hash
*/
public User(String userName, int salt, String pwHash) {
this.userName = userName;
this.salt = salt;
this.pwHash=pwHash;
}
/**
* Get the user name
*
* @return user name
*/
public String getUserName() {
return userName;
}
/**
* Get the password salt
*
* @return password salt
*/
public int getSalt() {
return salt;
}
/**
* Checks a supplied password hash
*
* @param pwHash comparison hash
* @return true if input hash matches user's hash
*/
public boolean hashMatches(String pwHash) {
return this.pwHash.equals(pwHash);
}
@Override
public String toString() {
return String.format("%s (%d, %s)", userName, salt, pwHash);
}
}
public class hashpqd {
public static String hashPassword(String password, int salt) {
return (salt+"|"+password).hashCode()+"";
}
/**
* Validates a login to a system given
* a map of users (userName -> user),
* a userName to attempt, and a password
*
* @param users valid users
* @param userName user's login
* @param password attempted password
* @return reference to user if successful, null otherwise
*/
public static User attemptLogin(Map<String, User> users, String userName, String password) {
for (String key : users.keySet()) {
if(key.equals(userName)){ // if user exist in map
User user=users.get(userName); // taking out the user object out
int salt=user.getSalt();
/* retriving salt value from use object and appending with user password and calculating
hashcode on top of that then finally adding empty string to covert the calculated hashcode into string*/
String hashedPasswd=(salt+"|"+password).hashCode()+"";
if(user.hashMatches(hashedPasswd)){
return user; //if user provided password matches to the password which user object contains then returning user object otherwise null
}
}
}
return null;
}
public static void main(String args[]){
// users
final User alice = new User("alice", 5, "1859628144"); // password123
final User bob = new User("bob", 98, "-1938366791"); // fuzzybunny
final Map<String, User> users1 = new HashMap<>();
for (User u : new User[] {alice, bob}) {
users1.put(u.getUserName(), u);
}
final User wendy = new User("wendy", -3, "1141373183"); // computerscienceisfun!
final User walter = new User("walter", 13, "873346171"); // computerscienceisfun!
final Map<String, User> users2 = new HashMap<>();
for (User u : new User[] {wendy, walter}) {
users2.put(u.getUserName(), u);
}
_testLogin(users1,"alice","password123");
}
private static void _testLogin(Map<String, User> users, String userName, String password) {
User result = null;
try {
result = hashpqd.attemptLogin(users, userName, password);
System.out.println(result);
} catch (Exception e) {}
// assertEquals(uE, result);
}
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.