I am playing around with a login/register system for a desktop application. I have a register form and sign in form and the credential information is saved to a PostgreSQL database. I started out without encrypting information and everything is working but I now want to encrypt the password to save to the database. I am using Jasypt and got the password to encrypt and save to the database:
public void actionPerformed(ActionEvent e){
if(e.getSource() == registerButton){
try{
//connects to the database
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/cc","pi","server");
//inserts values into table
PreparedStatement statement = conn.prepareStatement("insert into users values(?,?,?,?,?)");
statement.setString(1, firstName.getText());
statement.setString(2, lastName.getText());
statement.setString(3, email.getText());
statement.setString(4, username.getText());
//checks password to ensure confirmation matches
//encrypts password for storing in database
if(password.getText().equals(confPass.getText())){
StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
String encryptPass = passwordEncryptor.encryptPassword(password.toString());
statement.setString(5, encryptPass);
statement.executeUpdate();
JOptionPane.showMessageDialog(null, "Registered Successfully");
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
}
else{
JOptionPane.showMessageDialog(null, "Password did not match");
}
} catch(SQLException f){
f.printStackTrace();
}
}
This works well and my password is encrypted. I am stumped on where to begin with getting the password from the database to compare the encrypted password to the user input. My sign in form to compare passwords (prior to encrypting the password):
public void actionPerformed(ActionEvent e){
//verify users credentials and signs in
if(e.getSource() == login){
try{
//connects to the database
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/cc","pi","server");
//inserts values into table
PreparedStatement statement = conn.prepareStatement("SELECT username, user_pass FROM users WHERE username = ? and user_pass = ?");
statement.setString(1, username.getText());
statement.setString(2, password.getText());
ResultSet result = statement.executeQuery();
if (result.next()) {
frame.dispose();
new CommunityCooks();
}
else {
JOptionPane.showMessageDialog(null, "Username or Password did not match.");
}
}
catch(SQLException f){
f.printStackTrace();
}
}
I made a test app to compare encrypted passwords and have a general idea of how it works:
import org.jasypt.util.password.StrongPasswordEncryptor;
public class EncryptTest {
public static void main(String[] args) {
String userPass = "test";
StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
String encryptPass = passwordEncryptor.encryptPassword(userPass);
System.out.println(encryptPass);
if(passwordEncryptor.checkPassword("test", encryptPass)){
System.out.println("correct");
}
else{
System.out.println("wrong");
}
}
}
The part that I am stumped on is trying to get the encrypted password and compare it to the user input password to validate. I believe I need to encrypt the input password and then can use the passworEncryptor.checkPassword(); but I don't know exactly where to put that in the sign in code and how to implement it. Any help would be great.
Thank you.
The name of the Java library Jasypt is shortened for "Java Simplified Encryption" and so this library is trying to avoid that
users are making implementations of crypto-routines that become insecure because of implementation errors.
The validation of a user password is as simple as your test app - you do not need to implement an encryption part, just pass the
password the user typed in your login form and pass it to the validation part
In details: There are two parts - in part 1 the user registers with his userPassword and passes it to the encryptPassword method:
String userPassword = "myPassword";
String encryptedPassword = passwordEncryptor.encryptPassword(userPassword);
encryptedPassword example: rCAgedhPnGoDZ1PF7hgspDIhLnLAHo536PSCKUfpYu8Yv0JHEcIZ3ZVHIHojBn1D
This encrypted password get stored in the database and you have to keep in mind that you cannot restore the original password from this string!
In part 2 the user tries to login with his loginPassword and passes it to the checkPassword method. The program loads the encryptedPassword from the database and passes the loginPassword together with the encryptedPassword. The result (boolean) is checked and the next steps in your program depend on the result, "true" means that the loginPassword is correct and "false" means that the loginPassword is not correct.
String loginPassword = "myPassword";
if (passwordEncryptor.checkPassword(loginPassword, encryptedPassword)) {
// correct!
System.out.println("correct passsword");
} else {
// bad login!
System.out.println("password not correct");
}
For more information you can check the "Easy usage" webpage: http://www.jasypt.org/easy-usage.html
Related
I have built a function to hash the users password when they register using my java application. I am not sure how I should go about trying to verify their login since the password stored is going to be hashed.
I have tried making a function, that takes the entered password and hashes it and then compares the hash to the one in the database but, this didn't work since each time the hashing function is run on a password (String) the result is never the same. I am not sure how to verify the users password on login. Below I have provided:
Login button code
Generating secure password using salt function
Verification function
Login button code:
try {
// Get password from database and verify login
String slt = passwordUtils.getSalt(100);
Connection con = connectDB.getConnectionUser();
Statement stmt = null;
String sql = "SELECT * FROM USER where userEmail=?";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, userEmail.getText());
ResultSet rs = pst.executeQuery();
String fromDatabase = rs.getString(3); //get the password stored in database (hashed version)
String enteredPassword = String.valueOf(userPassword.getPassword()); //get entered password
if(rs.next()){
if(passwordUtils.verifyUserPassword(enteredPassword, fromDatabase, slt)){
JOptionPane.showMessageDialog(null, "Login details are correct. Welcome!");
con.close();
} else {
JOptionPane.showMessageDialog(null, "Invalid Log in details, try again.");
con.close();
}
}
} catch (SQLException ex) {
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
Generate secure password function
public static String generateSecurePassword(String password, String salt){
String returnValue = null;
System.out.println("password supplied is: " + password + "salt added: " + salt);
System.out.println("");
byte[] securePassword = hash(password.toCharArray(), salt.getBytes());
System.out.println("");
System.out.println("secure pwd byte[]: " + securePassword);
System.out.println("");
returnValue = Base64.getEncoder().encodeToString(securePassword);
System.out.println("");
System.out.println("secure pwd converted " + returnValue);
return returnValue;
}
Verify login function
public static boolean verifyUserPassword(String providedPassword, String securedPassword, String salt){
boolean returnValue = false;
// generate new secure password with same salt
String newSecurePassword = generateSecurePassword(providedPassword, salt); //generate secure password using salt
//check to see if both passwords are the same
returnValue = newSecurePassword.equalsIgnoreCase(securedPassword);
System.out.println("Hashed password: " + securedPassword);
System.out.println("Verify hash: "+ newSecurePassword);
return returnValue;
}
Salting function
public static String getSalt(int length){;
StringBuilder returnValue = new StringBuilder(length);
for(int i = 0; i < length; i++){
returnValue.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
System.out.println("final salt generated: " + returnValue);
return new String(returnValue);
}
Hashing function:
public static byte[] hash(char[] password, byte[] salt){
PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
Arrays.fill(password, Character.MIN_VALUE);
try{
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
System.out.println("skf return to secure password call: " + skf.generateSecret(spec).getEncoded());
return skf.generateSecret(spec).getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e){
throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
} finally {
spec.clearPassword();
System.gc();
}
}
After reading taking on board the feedback received under this post. I managed to solve the issue.
I created another column in the database which was storing Users and named the column "saltPassword". This would store the salt used on the password, which can later be retrieved to check password validation.
After storing the hashed password and the salt used in the database when a user registers. It became quite simple to perform password verification.
public boolean verifyPassword(String providedPassword, String fromdatabase, String passwordSalt){
boolean isValid = false;
String generate = passwordUtils.generateSecurePassword(providedPassword, passwordSalt);
isValid = generate.equals(fromdatabase);
return isValid;
}
This function was used for password verification.
It takes the password entered by the user in the password field, the password currently stored in the database (hashed) and the salt used for the password.
It then generates a salted password using the entered password along with the same salt used for the original password in the database. This should produce the same hashed password in the database. We then check that this is the case by applying .equals(fromdatabase) and checking if it's the same as the one in the database. If this is true, then the password is correct and the user can be logged in.
I working on a log in system in Java and I am using PostgreSQL. I have a registration form that saves the user information to the database. It also hashes the password before storing. I am now trying to verify the password that the user enters against the hashed password in the database.
try {
//connects to the database
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/cc", "pi", "server");
//prepares SELECT statement
PreparedStatement statement = conn.prepareStatement("SELECT username, user_pass FROM users WHERE username = '" + username +"';");
ResultSet result = statement.executeQuery();
String upass = new String(password.getPassword());
String user = "";
String pass = "";
while(result.next()){
user = result.getString("username");
pass = result.getString("user_pass");
}
if(username.equals(user) && BCrypt.checkpw(upass, pass)){
frame.dispose();
new CommunityCooks();
}
else{
JOptionPane.showMessageDialog(null, "incorrect credentials");
}
}
catch(SQLException f){
f.printStackTrace();
}
This is the section that I am trying to verify the password matches. The "password" variable is a JPasswordField and the "username" variable is a JTextField. The stored credentials are 'tester' for the username and password and I am able to verify them as local variables and fields. I think the issue I am having is with the translation of the JTextField and the JPasswordField. What I am looking for help on is getting the username and password that the user enters to verify against the stored credentials after the password is hashed from the registration form. The issue I am having is that I can run the application and the login frame opens. I enter the test credentials and it is not matching. I’m getting my incorrect credentials message. I am unclear about the syntax for comparing the stored username (not hashed) and the stored password (hashed) against the user input credentials in the JTextField and the JPasswordField. I thought what I had was correct but it appears to not be right. The testing I have done for it works to read the database and verify but it is not working when I implement it in my program. The only difference in the two is the use of the fields where the test did not use them. My test is below:
public class Main {
public static void main(String[] args) {
String firstName = "tester";
String lastName = "tester";
String email = "tester";
String username = "tester";
String password = "tester";
try {
//connects to the database
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/cc", "pi", "server");
//inserts values into table
PreparedStatement statement = conn.prepareStatement("insert into users values(?,?,?,?,?)");
statement.setString(1, firstName);
statement.setString(2, lastName);
statement.setString(3, email);
statement.setString(4, username);
statement.setString(5, BCrypt.hashpw(password, BCrypt.gensalt()));
PreparedStatement st = conn.prepareStatement("SELECT username, user_pass FROM users WHERE username = '" + username + "';");
ResultSet result = st.executeQuery();
String u = null;
String p = null;
while (result.next()) {
u = result.getString("username");
p = result.getString("user_pass");
}
System.out.println(u);
System.out.println(p);
System.out.println(BCrypt.checkpw(password, p));
} catch(SQLException f){
f.printStackTrace();
}
}
}
The test output works and shows the username and the hashed password and stating that it is "true" to being verified.
Any guidance will be greatly appreciated. This is my first time using a hash system.
Currently I am working on a program that required to add multiple user and each user will have their own password, so all the username and password will save on a same text file. My question is, is there any solution to just comparing the username and password and that it.
For example, User A login, after the user type in the Username and Password it will go through the text file and take out User A username and password without touching User B or C Username and password of user B or C
The easiest way to do this is using Properties. A Java-Properties-File look like this:
# this is a comment
name=value
user=password
I would recommend to not store the password as plain-text in your file. Saving it's hash will be enough to compare passwords:
public class YourPasswordStorage
{
Properties users = new Properties();
public YourPasswordStorage (File file)
{
user.load(file);
}
void insertUser (String user, String password)
{
users.put(user,
Base64.getEncoder().encodeToString(
MessageDigest.getInstance("sha-512").digest(password)));
}
boolean checkUser (String user, String password)
{
String hash = Base64.getEncoder().encodeToString(
MessageDigest.getInstance("sha-512").digest(password));
return (hash.equals(users.get(user)); // note that users.get(user) could be null
}
}
Note that the Code above won't compile because of some uncaught Exceptions, but I hope you understand how it works.
Storing raw passwords in a text file isn't very safe for your users. It's a lot better to salt your passwords first - systematically convert them into hash that you write to file. Then instead of comparing password to password, you take salt the password the user gives you and compare that to what's written to file. Take a look here (and many other places) for how to do that.
Have you heard NEVER GIVE A MONKEY TO DO MAN'S WORK
Well AFAICS you will have the fields username password and user so why dont use a database instead of a file.
Take advantage of H2 database allows you to store data as file.
Just include the driver jar and write this simple codes
Connection con = null;
try{
Class.forName("org.h2.Driver");
con = DriverManager.getConnection("jdbc:h2:./mydb;MODE=MySQL;", "", "");
PreparedStatement stmt= con.prepareStatement
("INSERT INTO yourTable(user,username,password) VALUES(?,?,?)");
stmt.setString(1, "someUser");
stmt.setString(2, "someUsername");
stmt.setString(3, "somePassword");
stmt.executeUpdate();
}
catch (SQLException e) {
e.printStackTrace();
}
finally{
//close statements
}
I am trying to ensure that when a user enters username & password, authentication is done by checking if input matches some row in the user table. Here is the code so far: It doesn't respond when the login button is click. Please suggest how I can set it right. Thanks
private void dbConnection()
{
try
{
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/maths_tutor", "root", "jesus");
Statement stmt = conn.createStatement();
String CHECK_USER = "SELECT * FROM records WHERE username = '"+this.txtUser+"' AND password = '"+this.txtPass+"'";
ResultSet rs = stmt.executeQuery(CHECK_USER);
while(rs.next())
{
String user = txtUser.getText();
String pass = txtPass.getText();
if(user.equals(rs.getString("username")))
{
if(pass.equals(rs.getString("password")))
{
this.dispose();
new AboutTo().setVisible(true);
}
else JOptionPane.showMessageDialog(null, "Invalid Password");
}
else JOptionPane.showMessageDialog(null, "Invalid Username or Password");
}
stmt.close();
rs.close();
conn.close();
}
catch(SQLException | ClassNotFoundException er)
{
JOptionPane.showMessageDialog(null, "Exception:\n" + er.toString());
}
}
String CHECK_USER = "SELECT * FROM records WHERE username = '"+this.txtUser+"' AND password = '"+this.txtPass+"'";
you have passed username & password in sql query so it go in while block only if username And password will match ...
you supposed to make sql querylike this
String CHECK_USER = "SELECT * FROM records";
or you can use if block like this
if(rs.next()
{
//login successfull code
}
else
{
//login fail
}
Basically, the logic is wrong.
What you are doing is approximately this.
Get a username and a password from the user.
Ask the database for all records for which the user name is matches the supplied username and the password matches the supplied password.
For each such record:
Test if the user name matches, and open a dialog if it doesn't match. That won't happen ... because you only selected records with that user name.
Test if the password matches, and open a dialog if it doesn't match. That won't happen ... because you only selected records with that password.
What you really ought to be doing is:
Get a username and a password from the user.
Select the records that match the user name and password.
Print a message if the number of records that you matched is zero.
I should also point out some other things:
Popping up a dialog box to tell the user his user name / password are wrong is beside the point. What you really need to do is tell something else in your server that the login failed.
When the user gets just the username or just the password incorrect, you should not offer him any clues that one was correct. Doing that makes it easier for "the bad guy" to work out the correct combination.
Storing passwords in clear in a database is Bad Practice. Best practice is to store seeded hashes of the passwords ... and use a cryptographically strong hashing function.
You forgot to call getText() on txtUser and txtPass.
This is how you could fix your query:
String CHECK_USER = "SELECT * FROM records WHERE username = '" + this.txtUser.getText() + "' AND password = '" + this.txtPass.getText() + "'";
You should note that concatenation of raw input text to queries will open vulnerability to SQL injection. You should use PreparedStatement instead so that the input text is properly escaped.
The following is a way to implement this properly, however lacks the following things that should be of concern to you:
You are storing passwords in clear text. You should use a hashing function such as SHA-1.
Every authentication will result in a new connection to the database. You should probably use a proper connection pool.
.
private boolean authenticate() throws SQLException {
String dbUrl = "jdbc:mysql://localhost:3306/maths_tutor";
// This query will simply count the matching rows, instead of actually selecting
// them. This will result in less bandwidth between your application and the server
String query = "SELECT count(*) AS num_records FROM records WHERE username = ? AND password = ?";
// Obtaining the username and password beforehand could perhaps make it more clear
// and prevent errors instead of pulling the data every time you need it
String username = txtUser.getText();
String password = txtPass.getText();
// The try-with-resources block will make sure the resources are closed once we are done with
// them. More information available at
// http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
try (
// Open database connection
Connection conn = DriverManager.getConnection(dbUrl, "root", "jesus");
// Prepare the statement
PreparedStatement stmt = conn.prepareStatement(query)
) {
// Set the username and password for the SQL statement
stmt.setString(1, username);
stmt.setString(2, password);
// Execute the query in a try block, to ensure that the resources
// will be released
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
// If we got 1 or more matches, this means we successfully
// authenticated. Otherwise, we failed authentication.
return rs.getInt("num_records") > 0;
}
}
}
// Failed authentication.
return false;
}
// Rename this to something meaningful according to your application
private void doAuthentication() {
try {
if (authenticate()) {
// Do successful authentication handling
this.dispose();
new AboutTo().setVisible(true);
} else {
// Do failed authentication handling
JOptionPane.showMessageDialog(null, "Invalid Username or Password");
}
} catch(SQLException er) {
// Do error handling
JOptionPane.showMessageDialog(null, "Exception:\n" + er.toString());
}
}
The possible error would be near this line
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/maths_tutor", "root", "jesus");
first make sure you have set the classpath and added the mysql driver to the project
second I would the following instead of the above, in fact why do you make things to much complex?!
java.sql.Driver _dr=new com.mysql.jdbc.Driver();
java.util.Properties _pr=new java.util.Properties();
_pr.setProperty("user","root");
_pr.setProperty("password","jesus");
Connection conn = _dr.connect("jdbc:mysql://localhost:3306/maths_tutor", _pr);
and the last thing is beware about using this like of code
String CHECK_USER = "SELECT * FROM records WHERE username = '"+this.txtUser+"' AND password = '"+this.txtPass+"'";
so here the system is ready for injection.
so the good way would be like this, using parameters.
String CHECK_USER = "SELECT * FROM records WHERE username = ? AND password = ?";//this will avoid sql injection
java.sql.PreparedStatement _st=conn.prepareStatement(CHECK_USER);
_st.setString(1, this.txtUser);
_st.setString(1, this.txtPass);
EDIT :by the way, there is no need to iterate over result set! simple just call the next() method, if it returns true, so it means user has entered correct user/pass, else otherwise.
ResultSet rs = stmt.executeQuery(CHECK_USER);
if(rs.next()){/*user exist*/
this.dispose();
new AboutTo().setVisible(true); }
else{
JOptionPane.showMessageDialog(null, "Invalid Username or Password");
}
string query = "SELECT count(*) FROM [dbo].[login1] WHERE username='" + username.Text + "' and password='" + password.Text + "'";
SqlDataAdapter sda = new SqlDataAdapter(query, con);
DataTable dt = new DataTable();
sda.Fill(dt);
if (dt.Rows[0][0].ToString() == "1")
{MessageBox.Show("YEAH");}
I am having trouble with getting my program to run properly. I was able to clear any syntax errors, but now I am having issued with my output.
I have the following statement and the ELSE IF doesn't seem to be working? it always ignores the else if codes. i can't understand the problem with the code.
Can anybody help me?
private void login_btnActionPerformed(java.awt.event.ActionEvent evt) {
Connection con;
PreparedStatement stmt;
ResultSet rs;
try {
//connect to the database
String host = "jdbc:derby://localhost:1537/LoginSystem";
String uName = "root";
String uPass = "root";
con = DriverManager.getConnection(host, uName, uPass);
String user = username_txt.getText();
String pass = passwordField_txt.getText();
String sql = "Select USERNAME,PASSWORD from LOGINSYSTEM where USERNAME = '"+user+"' AND PASSWORD = '"+pass+"'";
stmt = con.prepareStatement(sql);
rs = stmt.executeQuery();
while(rs.next()) {
String s1 = rs.getString(1);
String s2 = rs.getString(2);
if(user.equals(s1) && pass.equals(s2)) {
JOptionPane.showMessageDialog(null, "Login Successfull!");
Account obj = new Account();
obj.setVisible(true);
} else if(!user.equals(s1) && !pass.equals(s2)) {
JOptionPane.showMessageDialog(null, "Login Failed!");
} else if(!pass.equals(s2)) {
JOptionPane.showMessageDialog(null, "Please Enter A Valid Password.");
passwordField_txt.setText(null);
}
}
} catch(SQLException e) {
JOptionPane.showMessageDialog(null, e);
}
}
The query returns matching user and pass values so there is no condition where !user.equals(s1) && !pass.equals(s2) is satisified. Always use PreparedStatement placeholders rather than String concatenation to protect against SQL Injection.
Differentiating the error between an invalid usernames and passwords allows any would-be hacker an insight what information can be used to gain access to the system.
If anything the code should look like
if (rs.next()) {
String s1 = rs.getString(1);
String s2 = rs.getString(2);
// user and pass already match from SQL - no if logic necessary
} else {
// display GENERIC login failure message
}
But storing passwords in a database is a major security flaw so this whole approach is not safe. The typical approach is to store the password using a salt to guard against the use of rainbow tables. To verify the password the same hashing algorithm and salt can be used to compare against what is stored in the database,
First this is subject to a SQL injection attack. Learn about prepared statements, and use the parameter passing feature of prepared statements, to prevent user names like: Little Bobby Tables
Second, your where statement will only return back rows where the user name and password are exact matches to the inputted values. Therefore, comparing a non-null result will be guaranteed to always match, barring some incredibly bizarre database bug.
If you ask me to give you a fruit where the fruit is a red apple, then assuming that I am trusted, when I give you a red apple, checking to see if the apple is not an apple, or that red is not red will result in checking a condition that is never met.
If usernames are forced unique, you can take that matching condition for the password out of the query. Then just query for the username matching condition and get the username and password for that row. This way you can check for incorrect password OR incorrect username more easily. The way it is now the query will only return results when the username and password are correct and you will never enter your else if's.