PasswordEncode.java
package com.tradecloud.authentication;
import org.apache.log4j.Logger;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class PasswordEncode implements PasswordEncoder {
public static final String MD5 = "MD5";
public static final String SHA_1 = "SHA-1";
public static final String UTF_16 = "UTF-16";
private static final Logger log = Logger.getLogger(PasswordEncode.class);
public static final String BASE64_ENCODING = "BASE64";
public static final String BASE16_ENCODING = "HEX";
/**
* Default encode method.
*/
public static String encode(String plaintext) {
return PasswordEncode.createPasswordHash(SHA_1, PasswordEncode.BASE16_ENCODING, UTF_16, "", plaintext);
}
/**
* Calculate a password hash using a MessageDigest.
*
* @param hashAlgorithm the MessageDigest algorithm name
* @param hashEncoding either base64 or hex to specify the type of encoding the
* MessageDigest as a string.
* @param hashCharset the charset used to create the byte[] passed to the
* MessageDigestfrom the password String. If null the platform
* default is used.
* @param username ignored in default version
* @param password the password string to be hashed
* @return the hashed string if successful, null if there is a digest
* exception
*/
public static String createPasswordHash(String hashAlgorithm, String hashEncoding, String hashCharset, String username, String password) {
byte[] passBytes;
String passwordHash = null;
// convert password to byte data
try {
if (hashCharset == null)
passBytes = password.getBytes();
else
passBytes = password.getBytes(hashCharset);
} catch (UnsupportedEncodingException uee) {
log.error("charset " + hashCharset + " not found. Using platform default.", uee);
passBytes = password.getBytes();
}
// calculate the hash and apply the encoding.
try {
MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
md.update(passBytes);
byte[] hash = md.digest();
if (hashEncoding.equalsIgnoreCase(BASE64_ENCODING)) {
passwordHash = PasswordEncode.encodeBase64(hash);
} else if (hashEncoding.equalsIgnoreCase(BASE16_ENCODING)) {
passwordHash = PasswordEncode.encodeBase16(hash);
} else {
log.error("Unsupported hash encoding format " + hashEncoding);
}
} catch (Exception e) {
log.error("Password hash calculation failed ", e);
}
return passwordHash;
}
/**
* BASE64 encoder implementation. Provides encoding methods, using the
* BASE64 encoding rules, as defined in the MIME specification, <a
* href="http://ietf.org/rfc/rfc1521.txt">rfc1521</a>.
*/
public static String encodeBase64(byte[] bytes) {
String base64 = null;
try {
base64 = new String(Base64.encode(bytes));
} catch (Exception e) {
}
return base64;
}
/**
* Hex encoding of hashes, as used by Catalina. Each byte is converted to
* the corresponding two hex characters.
*/
public static String encodeBase16(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
// top 4 bits
char c = (char) ((b >> 4) & 0xf);
if (c > 9)
c = (char) ((c - 10) + 'a');
else
c = (char) (c + '0');
sb.append(c);
// bottom 4 bits
c = (char) (b & 0xf);
if (c > 9)
c = (char) ((c - 10) + 'a');
else
c = (char) (c + '0');
sb.append(c);
}
return sb.toString();
}
public String encodePassword(String rawPass, Object salt) {
return PasswordEncode.createPasswordHash(SHA_1, PasswordEncode.BASE16_ENCODING, UTF_16, "", rawPass);
}
/**
* This is the method invoked by the Spring security framework that compares
* the users password with the stored one.
*/
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
String newPassword = PasswordEncode.createPasswordHash(SHA_1, PasswordEncode.BASE16_ENCODING, UTF_16, "", rawPass);
boolean result = encPass.equals(newPassword);
// retry with raw password to enable comparison if hashed passwords get
// used as input
// if (!result) {
// result = encPass.equals(rawPass);
// }
// Print error so that we know it was a password mis-match.
if (!result) {
log.error("\n\nPassword Comparison Failure: [" + encPass + "] != [" + newPassword + "].\n\n");
} else {
// log.debug("Passwords compared successfully. User login successful.");
}
return result;
}
/**
* Adding this method for legacy deploys where we need to has passwords.
* <p>
* Tradecloud was originally deployed with MD5 hashing but we switched this
* to fit with Redbox
* <p>
* To get an equivalent hash to what we would have got using Spring's
* default MD5 hashing algorithm use this method
*/
public static String encodePasswordWithMD5(String plaintext) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance(MD5);
messageDigest.update(plaintext.getBytes(), 0, plaintext.length());
String hashedPass = new BigInteger(1, messageDigest.digest()).toString(16);
if (hashedPass.length() < 32) {
hashedPass = "0" + hashedPass;
}
return hashedPass;
}
@Override
public String encode(CharSequence charSequence) {
try {
return encodePasswordWithMD5((String) charSequence);
} catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException("Password encoding " + e.getMessage());
}
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return isPasswordValid(s, (String) charSequence, null);
}
}