#358 Make RandomString static & generate all rand. strings with it

- Remove dubious random String generator on HashUtils
- Make further hash classes use HashUtils
This commit is contained in:
ljacqu 2015-12-27 22:16:16 +01:00
parent 90a0325194
commit 513ff9a928
17 changed files with 125 additions and 123 deletions

View File

@ -41,7 +41,7 @@ public class CaptchaCommand extends PlayerCommand {
if (Settings.useCaptcha && !captcha.equals(plugin.cap.get(playerNameLowerCase))) {
plugin.cap.remove(playerNameLowerCase);
String randStr = new RandomString(Settings.captchaLength).nextString();
String randStr = RandomString.generate(Settings.captchaLength);
plugin.cap.put(playerNameLowerCase, randStr);
commandService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, plugin.cap.get(playerNameLowerCase));
return;

View File

@ -38,8 +38,7 @@ public class RecoverEmailCommand extends PlayerCommand {
return;
}
try {
RandomString rand = new RandomString(Settings.getRecoveryPassLength);
String thePass = rand.nextString();
String thePass = RandomString.generate(Settings.getRecoveryPassLength);
String hashNew = PasswordSecurity.getHash(Settings.getPasswordHash, thePass, playerName);
PlayerAuth auth;
if (PlayerCache.getInstance().isAuthenticated(playerName)) {

View File

@ -26,12 +26,11 @@ import java.util.List;
*/
public class AsynchronousLogin {
private static final RandomString rdm = new RandomString(Settings.captchaLength);
protected final Player player;
protected final String name;
protected final String realName;
protected final String password;
protected final boolean forceLogin;
private final Player player;
private final String name;
private final String realName;
private final String password;
private final boolean forceLogin;
private final AuthMe plugin;
private final DataSource database;
private final Messages m;
@ -70,7 +69,7 @@ public class AsynchronousLogin {
plugin.captcha.putIfAbsent(name, i);
}
if (plugin.captcha.containsKey(name) && plugin.captcha.get(name) > Settings.maxLoginTry) {
plugin.cap.putIfAbsent(name, rdm.nextString());
plugin.cap.putIfAbsent(name, RandomString.generate(Settings.captchaLength));
m.send(player, MessageKey.USAGE_CAPTCHA, plugin.cap.get(name));
return true;
}

View File

@ -3,40 +3,27 @@ package fr.xephi.authme.security;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public final class HashUtils {
private static final SecureRandom RANDOM = new SecureRandom();
private HashUtils() {
}
public static String hash(String message, MessageDigestAlgorithm algorithm) {
MessageDigest md = getDigest(algorithm);
md.reset();
md.update(message.getBytes());
byte[] digest = md.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
public static String sha1(String message) {
return hash(message, MessageDigestAlgorithm.SHA1);
}
public static String md5(String message) {
return hash(message, MessageDigestAlgorithm.MD5);
public static String sha256(String message) {
return hash(message, MessageDigestAlgorithm.SHA256);
}
// Only works for length up to 40!
public static String generateSalt(int length) {
byte[] msg = new byte[40];
RANDOM.nextBytes(msg);
MessageDigest sha1 = getDigest(MessageDigestAlgorithm.SHA1);
sha1.reset();
byte[] digest = sha1.digest(msg);
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)).substring(0, length);
public static String sha512(String message) {
return hash(message, MessageDigestAlgorithm.SHA512);
}
public static String md5(String message) {
return hash(message, MessageDigestAlgorithm.MD5);
}
public static MessageDigest getDigest(MessageDigestAlgorithm algorithm) {
@ -48,6 +35,12 @@ public final class HashUtils {
}
}
private static String hash(String message, MessageDigestAlgorithm algorithm) {
MessageDigest md = getDigest(algorithm);
md.reset();
md.update(message.getBytes());
byte[] digest = md.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
}

View File

@ -1,13 +1,13 @@
package fr.xephi.authme.security;
import java.security.SecureRandom;
import java.util.Calendar;
import java.util.Random;
public class RandomString {
public final class RandomString {
private static final char[] chars = new char[36];
private static final Random RANDOM = new SecureRandom();
private static final int HEX_MAX_INDEX = 15;
static {
for (int idx = 0; idx < 10; ++idx) {
@ -18,30 +18,24 @@ public class RandomString {
}
}
private final Random random = new Random();
private final char[] buf;
public RandomString(int length) {
if (length < 1)
throw new IllegalArgumentException("length < 1: " + length);
buf = new char[length];
random.setSeed(Calendar.getInstance().getTimeInMillis());
}
public String nextString() {
for (int idx = 0; idx < buf.length; ++idx)
buf[idx] = chars[random.nextInt(chars.length)];
return new String(buf);
private RandomString() {
}
public static String generate(int length) {
return generate(length, chars.length);
}
public static String generateHex(int length) {
return generate(length, HEX_MAX_INDEX);
}
private static String generate(int length, int maxIndex) {
if (length < 0) {
throw new IllegalArgumentException("Length must be positive but was " + length);
}
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
sb.append(chars[RANDOM.nextInt(chars.length)]);
sb.append(chars[RANDOM.nextInt(maxIndex)]);
}
return sb.toString();
}

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
@ -10,7 +10,7 @@ import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
import javax.xml.bind.DatatypeConverter;
@Recommendation(Usage.OK)
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 12)
public class CryptPBKDF2Django implements EncryptionMethod {
@ -38,7 +38,7 @@ public class CryptPBKDF2Django implements EncryptionMethod {
}
public String generateSalt() {
return HashUtils.generateSalt(12);
return RandomString.generateHex(12);
}
}

View File

@ -1,12 +1,13 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
@Recommendation(Usage.OK)
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 32)
public class JOOMLA implements EncryptionMethod {
@ -20,7 +21,7 @@ public class JOOMLA implements EncryptionMethod {
}
public String generateSalt() {
return HashUtils.generateSalt(32);
return RandomString.generateHex(32);
}
@Override

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
@ -8,7 +8,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
import static fr.xephi.authme.security.HashUtils.md5;
@Recommendation(Usage.OK)
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 16)
public class MD5VB implements EncryptionMethod {
@ -28,7 +28,7 @@ public class MD5VB implements EncryptionMethod {
}
public String generateSalt() {
return HashUtils.generateSalt(16);
return RandomString.generateHex(16);
}
}

View File

@ -1,33 +1,34 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
import static fr.xephi.authme.security.HashUtils.sha256;
@Recommendation(Usage.RECOMMENDED)
@HasSalt(value = SaltType.TEXT, length = 16)
public class SHA256 implements EncryptionMethod {
private static String getSHA256(String message)
throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.reset();
sha256.update(message.getBytes());
byte[] digest = sha256.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return "$SHA$" + salt + "$" + sha256(sha256(password) + salt);
}
public String computeHash(String password, String name) {
return computeHash(password, generateSalt(), name);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return "$SHA$" + salt + "$" + getSHA256(getSHA256(password) + salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String hash, String password, String playerName) {
String[] line = hash.split("\\$");
return hash.equals(computeHash(password, line[2], ""));
return line.length == 4 && hash.equals(computeHash(password, line[2], ""));
}
public String generateSalt() {
return RandomString.generateHex(16);
}
}

View File

@ -1,31 +1,30 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public class SHA512 implements EncryptionMethod {
private static String getSHA512(String message)
throws NoSuchAlgorithmException {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
sha512.reset();
sha512.update(message.getBytes());
byte[] digest = sha512.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, name);
}
public String computeHash(String password, String name) {
return HashUtils.sha512(password);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA512(password);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, "", ""));
}
public String generateSalt() {
return null;
}
}

View File

@ -1,31 +1,30 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public class SMF implements EncryptionMethod {
private static String getSHA1(String message)
throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset();
sha1.update(message.getBytes());
byte[] digest = sha1.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, name);
}
public String computeHash(String password, String name) {
return HashUtils.sha1(name.toLowerCase() + password);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA1(name.toLowerCase() + password);
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, playerName));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, null, playerName));
public String generateSalt() {
return null;
}
}

View File

@ -11,7 +11,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
@Recommendation(Usage.OK)
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 9)
public class WORDPRESS implements EncryptionMethod {

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
@ -38,7 +38,7 @@ public class XAUTH implements EncryptionMethod {
}
public String generateSalt() {
return HashUtils.generateSalt(12);
return RandomString.generateHex(12);
}
}

View File

@ -1,13 +1,22 @@
package fr.xephi.authme.security.crypts.description;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Describes the type of salt the encryption algorithm uses. This is purely for documentation
* purposes and is ignored by the code.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HasSalt {
/** The type of the salt. */
SaltType value();
/** For text salts, the length of the salt. */
int length() default 0;
}

View File

@ -1,10 +1,18 @@
package fr.xephi.authme.security.crypts.description;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to mark a hash algorithm with the usage recommendation, see {@link Usage}.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Recommendation {
/** The recommendation for using the hash algorithm. */
Usage value();
}

View File

@ -8,7 +8,7 @@ public enum SaltType {
/** Random, newly generated text. */
TEXT,
/** The username, including variations or repetitions. */
/** Salt is based on the username, including variations and repetitions. */
USERNAME,
/** No salt. */

View File

@ -9,7 +9,7 @@ public enum Usage {
RECOMMENDED,
/** There are safer algorithms that can be chosen but using the algorithm is generally OK. */
OK,
ACCEPTABLE,
/** Hash algorithm is not recommended to be used. Use only if required by another system. */
DO_NOT_USE,