package edu.harvard.iq.dataverse.authorization.providers.builtin;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang.RandomStringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.primefaces.util.Base64;
/**
* Password encryption, supporting multiple encryption algorithms to
* allow migrations between them.
*
* When adding a new password hashing algorithm, implement the {@link Algorithm}
* interface, and add an instance of the implementation as the last element
* of the {@link #algorithms} array. The rest should pretty much happen automatically
* (e.g system will detect outdated passwords for users and initiate the password reset breakout).
*
* @author Ellen Kraffmiller
* @author Michael Bar-Sinai
*/
public final class PasswordEncryption implements java.io.Serializable {
public interface Algorithm {
String encrypt( String plainText );
boolean check( String plainText, String hashed );
}
/**
* The SHA algorithm, now considered not secure enough.
*/
private static final Algorithm SHA = new Algorithm() {
@Override
public String encrypt(String plainText) {
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(plainText.getBytes("UTF-8"));
byte[] raw = md.digest();
String hash = Base64.encodeToString(raw, true);
return hash;
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean check(String plainText, String hashed) {
return hashed.equals( encrypt(plainText) );
}
};
/**
* BCrypt, using a complexity factor of 10 (considered safe by 2015 standards).
*/
private static final Algorithm BCRYPT_10 = new Algorithm() {
@Override
public String encrypt(String plainText) {
return BCrypt.hashpw(plainText, BCrypt.gensalt());
}
@Override
public boolean check(String plainText, String hashed) {
return BCrypt.checkpw(plainText, hashed);
}
};
private static final Algorithm[] algorithms;
static {
algorithms = new Algorithm[]{SHA, BCRYPT_10};
}
/**
* Prevent people instantiating this class.
*/
private PasswordEncryption() {}
public static Algorithm get() {
return getVersion( getLatestVersionNumber() );
}
public static int getLatestVersionNumber() {
return algorithms.length-1;
}
public static Algorithm getVersion(int i){
return algorithms[i];
}
public static String generateRandomPassword() {
return RandomStringUtils.randomAlphanumeric(8);
}
}