package org.cellocad.authenticate;
/**
*
* @author evanappleton
*/
import org.apache.commons.codec.binary.Base64;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The Authenticator class represents the interface between the
* AuthenticationServlet and the persisted objects.
*
* @author Ernst Oberortner
*
*/
public class Authenticator {
private EntityManager entityManager;
/**
* Authenticator Constructor
*
* @param db ... the name of the DB which contains username and password
* information
*/
public Authenticator(String db) {
try {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory(db);
this.entityManager = emf.createEntityManager();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The login/2 method evaluates if a given user and password exists.
*
* @param user ... username
* @param password ... password
* @return true ... the user exists false ... the user does not exist
* @throws AuthenticationException
*/
public String getUsername(String basic_auth) {
String authString = basic_auth.split(" ")[1];
byte[] bytesEncoded = authString.getBytes();
byte[] valueDecoded = Base64.decodeBase64(bytesEncoded);
String authPlain = new String(valueDecoded);
String username = authPlain.split(":")[0];
return username;
}
public boolean login(String basic_auth) {
String authString = basic_auth.split(" ")[1];
byte[] bytesEncoded = authString.getBytes();
byte[] valueDecoded = Base64.decodeBase64(bytesEncoded);
String authPlain = new String(valueDecoded);
String username = authPlain.split(":")[0];
String password = authPlain.split(":")[1];
try {
return login(username, password);
} catch (AuthenticationException e) {
return false;
}
}
// public boolean isAuthorized(String basic_auth) throws AuthenticationException {
//
// String authString = basic_auth.split(" ")[1];
// byte[] bytesEncoded = authString.getBytes();
// byte[] valueDecoded = Base64.decodeBase64(bytesEncoded);
// String authPlain = new String(valueDecoded);
// String username = authPlain.split(":")[0];
// String password = authPlain.split(":")[1];
//
// return login(username, password);
// }
public boolean login(String user, String password)
throws AuthenticationException {
UserInformation ui = this.entityManager.find(UserInformation.class, user);
if (null == ui) {
// if the user does not exist, we throw an exception
// throw new AuthenticationException("Invalid Login!");
return false;
}
/*
* hash & salt the received password
*/
byte[] received_password = getEncryptedPassword(
password, ui.getSalt());
/*
* now, we compare the given password and
* the user's password stored in the DB
*/
return Arrays.equals(
received_password,
ui.getEncryptedPassword());
}
/**
* The register/2 method stores the user including its password
*
* @param user ... username
* @param password ... password
*
* @throws AuthenticationException
*/
public void register(String user, String password, Boolean initialize)
throws AuthenticationException {
UserInformation ui = this.entityManager.find(UserInformation.class, user);
if (null != ui) {
if (initialize == false) {
// if the user does exist already, then we throw an exception
throw new AuthenticationException("The user exists already!");
} else {
return;
}
}
/*
* hash & salt the password
*/
byte[] salt = generateSalt();
byte[] encrypted_password = getEncryptedPassword(password, salt);
//BCryptPasswordEncoder bc = new BCryptPasswordEncoder(10);
//bc.encode(password);
/*
* then, we store username and password into
* out database
*/
UserInformation userInfo = new UserInformation(user, salt, encrypted_password);
this.persist(userInfo);
}
/**
* transaction-based persistence of a UserInformation object
*
* @param ui ... the UserInformation object
*/
private void persist(UserInformation ui) {
this.entityManager.getTransaction().begin();
this.entityManager.persist(ui);
this.entityManager.getTransaction().commit();
}
/**
* Generates a hash from the supplied password and salt (see
* {@link PasswordEncryptionService#generateSalt()}) using the PBKDF2 with
* SHA-1 algorithm
*
*
* @param password - Password to hash
* @param salt - Salt to use for the hashing
* @return - Password hash
*/
private byte[] getEncryptedPassword(String password, byte[] salt) {
try {
// PBKDF2 with SHA-1 as the hashing algorithm. Note that the NIST
// specifically names SHA-1 as an acceptable hashing algorithm for
// PBKDF2
String algorithm = "PBKDF2WithHmacSHA1";
// SHA-1 generates 160 bit hashes, so that's what makes sense here
int derivedKeyLength = 160;
// Pick an iteration count that works for you. The NIST recommends
// at
// least 1,000 iterations:
// http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
// iOS 4.x reportedly uses 10,000:
// http://blog.crackpassword.com/2010/09/smartphone-forensics-cracking-blackberry-backup-passwords/
int iterations = 20000;
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt,
iterations, derivedKeyLength);
SecretKeyFactory f = SecretKeyFactory.getInstance(algorithm);
return f.generateSecret(spec).getEncoded();
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(Authenticator.class.getName()).log(Level.SEVERE, null, ex);
ex.printStackTrace();
} catch (InvalidKeySpecException ex) {
Logger.getLogger(Authenticator.class.getName()).log(Level.SEVERE, null, ex);
ex.printStackTrace();
}
// catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
// /*
// * This should only happen when running this code on a new
// * environment. It will never happen unpredictably and therefore is
// * caught here so that utilizing code doesn't require a try/catch
// * block.
// */
// e.printStackTrace();
// }
return null;
}
/**
* SALT generator
*/
private byte[] generateSalt() {
try {
// we're using SecureRandom instead of just Random
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
// Generate a 8 byte (64 bit) salt as recommended by RSA PKCS5
byte[] salt = new byte[8];
random.nextBytes(salt);
return salt;
} catch (NoSuchAlgorithmException e) {
/*
* This should only happen when running this code on a new
* environment. It will never happen unpredictably and therefore is
* caught here so that utilizing code doesn't require a try/catch
* block.
*/
e.printStackTrace();
}
return null;
}
}