DBPasswordHash.java
/*
* Copyright 2018 Gregory Graham.
*
* Commercial licenses are available, please contact info@gregs.co.nz for details.
*
* This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/
* or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
*
* You are free to:
* Share - copy and redistribute the material in any medium or format
* Adapt - remix, transform, and build upon the material
*
* The licensor cannot revoke these freedoms as long as you follow the license terms.
* Under the following terms:
*
* Attribution -
* You must give appropriate credit, provide a link to the license, and indicate if changes were made.
* You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
* NonCommercial -
* You may not use the material for commercial purposes.
* ShareAlike -
* If you remix, transform, or build upon the material,
* you must distribute your contributions under the same license as the original.
* No additional restrictions -
* You may not apply legal terms or technological measures that legally restrict others from doing anything the
* license permits.
*
* Check the Creative Commons website for any details, legalese, and updates.
*/
package nz.co.gregs.dbvolution.datatypes;
import nz.co.gregs.dbvolution.exceptions.IncorrectPasswordException;
import nz.co.gregs.dbvolution.databases.DBDatabase;
import nz.co.gregs.dbvolution.utility.encryption.UpdatingBCrypt;
/**
* Creates a password column using the secure hashing.
*
* <p>
* Does not contain passwords, only hashes. This is the correct way to protect
* and "store" your passwords.</p>
*
* <p>
* Use {@link #checkPasswordWithException(java.lang.String)
* } and {@link #checkPassword(java.lang.String) } to check a password
* attempt with the hash. Use {@link #checkPasswordAndUpdateHash(java.lang.String)
* } to check the password against the hash and update the local (not database)
* hash with a more appropriate hash.</p>
*
* <p>
* Passwords are automatically hashed with BCrypt as the value is set and the
* actual password is never stored in the object or database.</p>
*
* <p>
* Please note that DBPasswordHash supports generating hashes appropriate to the
* CPU on which it is running. Use the {@link #checkPasswordAndUpdateHash(java.lang.String)
* } method to both check the password is correct and generate a new hash that
* is more appropriate to the CPU. The system assumes that you will call the
* method occasionally, updating the hash if it has changed. You will need to
* update the row on the database using {@link DBDatabase#update(nz.co.gregs.dbvolution.DBRow...)
* } or similar.</p>
*
* @author gregorygraham
*/
public class DBPasswordHash extends DBString {
static final long serialVersionUID = 1l;
transient UpdatingBCrypt crypt = new UpdatingBCrypt();
public DBPasswordHash() {
this(UpdatingBCrypt.DEFAULT_ROUNDS);
}
/**
* logRounds should be an number between 4 and 30, probably around 10.
*
* <p>
* Use {@link #checkPasswordAndUpdateHash(java.lang.String) } to generate a
* hash closer to the optimum for your system.</p>
*
* @param logRounds the number of encryption loops to do
*/
public DBPasswordHash(int logRounds) {
super();
crypt = new UpdatingBCrypt(logRounds);
}
public DBPasswordHash(String password) {
this(password, UpdatingBCrypt.DEFAULT_ROUNDS);
}
/**
* logRounds should be an number between 4 and 30, probably around 10.
*
* <p>
* Use {@link #checkPasswordAndUpdateHash(java.lang.String) } to generate a
* hash closer to the optimum for your system.</p>
*
* @param password the password to encrypt
* @param logRounds the number of hashing loops
*/
public DBPasswordHash(String password, int logRounds) {
super();
crypt = new UpdatingBCrypt(logRounds);
this.setValue(password);
}
@Override
public final void setValue(String password) {
super.setValue(password == null ? null : crypt.hashPassword(password));
}
public final boolean checkPassword(String password) {
return UpdatingBCrypt.checkPassword(password, getValue());
}
public final boolean checkPasswordWithException(String password) throws IncorrectPasswordException {
if (UpdatingBCrypt.checkPassword(password, getValue())) {
return true;
} else {
throw new IncorrectPasswordException(getValue());
}
}
/**
* DBPasswordHash supports generating hashes appropriate to the CPU on which
* it is running.
*
* <p>
* Also handles changing plain text passwords to bcrypt hashes.
*
* <p>
* Use the {@link #checkPasswordAndUpdateHash(java.lang.String)
* } method to both check the password is correct and generate a new hash that
* is more appropriate to the CPU. The system assumes that you will call the
* method occasionally, updating the hash if it has changed. You will need to
* update the row on the database using {@link DBDatabase#update(nz.co.gregs.dbvolution.DBRow...)
* } or similar.</p>
*
* @param password the secure text
* @return TRUE when the password is correct
* @throws IncorrectPasswordException thrown if the password does not match the field value
*/
public boolean checkPasswordAndUpdateHash(String password) throws IncorrectPasswordException {
String newHash = crypt.checkPasswordAndCreateSecureHash(password, getValue());
if (!newHash.equals(getValue())) {
super.setValue(newHash);
}
return true;
}
}