PasswordSecurity.java
package sk.iway.iwcm.users;
import static sk.iway.iwcm.Tools.isAnyEmpty;
import org.apache.commons.lang3.RandomStringUtils;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.users.crypto.Bcrypt;
import sk.iway.iwcm.users.crypto.Sha512;
/**
* PasswordSecurity.java
*
* Contains hash functions helping in
* users' password managment and verification.
*
*@Title webjet7
*@Company Interway s.r.o. (www.interway.sk)
*@Copyright Interway s.r.o. (c) 2001-2011
*@author $Author: marosurbanec $
*@version $Revision: 1.3 $
*@created Date: 4.1.2011 10:38:12
*@modified $Date: 2004/08/16 06:26:11 $
*/
public final class PasswordSecurity {
private PasswordSecurity(){}
public static String generatePassword(int length) {
return generatePassword(length, true, true, true, true, true);
}
/**
* Generates a random string.
*
* @param length
* @param symbols ( e.g. @#$% )
* @param numbers ( e.g. 123456 )
* @param lowercase ( e.g. abcdefgh )
* @param uppercase ( e.g. ABCDEFGH )
* @param excludeAmbiguousCharacters ( { } [ ] ( ) / \ ' " ` ~ , ; : . < > )
* @return
*/
public static String generatePassword(int length, boolean symbols, boolean numbers, boolean lowercase, boolean uppercase, boolean excludeAmbiguousCharacters)
{
String symbolsChar = "@#$%&!";
String numbersChar = "1234567890";
String lowercaseChar = "abcdefghijklmnopqrstuvwxyz";
String uppercaseChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String ambiguousCharactersChar = "{}[]()/\\'\"`~,;:.<>";
StringBuilder sb = new StringBuilder();
if (symbols) {
sb.append(symbolsChar);
}
if (numbers) {
sb.append(numbersChar);
}
if (lowercase) {
sb.append(lowercaseChar);
}
if (uppercase) {
sb.append(uppercaseChar);
}
if (!excludeAmbiguousCharacters) {
sb.append(ambiguousCharactersChar);
}
return RandomStringUtils.secureStrong().next(length, sb.toString());
}
/**
* Generates a random HEX string
* chars = [0-9a-f]{16}, lowercase only
*
* @return {@link String} random hex string
*/
public static String generateSalt()
{
if(Constants.getString("passwordHashAlgorithm").equals("bcrypt")) {
Bcrypt bcrypt = new Bcrypt();
return bcrypt.generateSalt();
} else {
Sha512 sha512 = new Sha512();
return sha512.generateSalt();
}
}
/**
* Generates a hash combining password and a salt using concatenation.
*
* @param password any String
* @param salt Generated by generateSalt, must match ^[0-9a-f]{16}$, otherwise rejected
* @return [0-9a-f]{128} String
*/
public static String calculateHash(String password, String salt)
{
if(Constants.getString("passwordHashAlgorithm").equals("bcrypt") && salt.startsWith("bcrypt:")) {
Bcrypt bcrypt = new Bcrypt();
return bcrypt.calculateHash(password, salt); //calculateHash can handle salt prefix
} else {
Sha512 sha512 = new Sha512();
return sha512.calculateHash(password, salt);
}
}
public static boolean isPasswordCorrect(String password, String salt, String hash)
{
if(isAnyEmpty(salt, hash))
return false;
//CVE-2025-22228
//BCryptPasswordEncoder.matches(CharSequence,String) will incorrectly return true for passwords larger than 72 characters as long as the first 72 characters are the same.
if (Tools.isNotEmpty(password)) {
String test = password.trim();
//verify all characters are not same
if (test.length() > 64) {
char c = test.charAt(0);
int counter = 0;
for (int i = 1; i < test.length(); i++) {
if (c == test.charAt(i)) {
counter++;
}
}
if (counter == test.length() - 1) {
return false;
}
}
}
if(hash.startsWith("bcrypt:") && salt.startsWith("bcrypt:")) {
//!! salt is not required, because is build into hash
Bcrypt bcrypt = new Bcrypt();
return bcrypt.isPasswordCorrect(password, salt, hash); //isPasswordCorrect can handle hash prefix
} else {
Sha512 sha512 = new Sha512();
return sha512.isPasswordCorrect(password, salt, hash);
}
}
}