#358 Add future interface methods, remove exception throwing
- Create Utils class for a common implementation of md5/sha1 - Create "foolproof" way of getting the MessageDigest for md5 etc. (MessageDigestAlgorithm enum) - Create description annotations to annotate algorithms with usage recommendation and salt type
This commit is contained in:
parent
2bb386c488
commit
90a0325194
@ -5,14 +5,10 @@ import org.bukkit.event.Event;
|
|||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
|
||||||
* This event is called when we need to compare or get an hash password, for set
|
* This event is called when we need to compare or get an hash password, for set
|
||||||
* a custom EncryptionMethod
|
* a custom EncryptionMethod
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author Xephi59
|
* @author Xephi59
|
||||||
* @version $Revision: 1.0 $
|
|
||||||
* @see fr.xephi.authme.security.crypts.EncryptionMethod
|
|
||||||
*/
|
*/
|
||||||
public class PasswordEncryptionEvent extends Event {
|
public class PasswordEncryptionEvent extends Event {
|
||||||
|
|
||||||
@ -20,60 +16,29 @@ public class PasswordEncryptionEvent extends Event {
|
|||||||
private EncryptionMethod method = null;
|
private EncryptionMethod method = null;
|
||||||
private String playerName = "";
|
private String playerName = "";
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for PasswordEncryptionEvent.
|
|
||||||
*
|
|
||||||
* @param method EncryptionMethod
|
|
||||||
* @param playerName String
|
|
||||||
*/
|
|
||||||
public PasswordEncryptionEvent(EncryptionMethod method, String playerName) {
|
public PasswordEncryptionEvent(EncryptionMethod method, String playerName) {
|
||||||
super(false);
|
super(false);
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.playerName = playerName;
|
this.playerName = playerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method getHandlerList.
|
|
||||||
*
|
|
||||||
* @return HandlerList
|
|
||||||
*/
|
|
||||||
public static HandlerList getHandlerList() {
|
public static HandlerList getHandlerList() {
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method getHandlers.
|
|
||||||
*
|
|
||||||
* @return HandlerList
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public HandlerList getHandlers() {
|
public HandlerList getHandlers() {
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method getMethod.
|
|
||||||
*
|
|
||||||
* @return EncryptionMethod
|
|
||||||
*/
|
|
||||||
public EncryptionMethod getMethod() {
|
public EncryptionMethod getMethod() {
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method setMethod.
|
|
||||||
*
|
|
||||||
* @param method EncryptionMethod
|
|
||||||
*/
|
|
||||||
public void setMethod(EncryptionMethod method) {
|
public void setMethod(EncryptionMethod method) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method getPlayerName.
|
|
||||||
*
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
public String getPlayerName() {
|
public String getPlayerName() {
|
||||||
return playerName;
|
return playerName;
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/main/java/fr/xephi/authme/security/HashUtils.java
Normal file
53
src/main/java/fr/xephi/authme/security/HashUtils.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 MessageDigest getDigest(MessageDigestAlgorithm algorithm) {
|
||||||
|
try {
|
||||||
|
return MessageDigest.getInstance(algorithm.getKey());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new UnsupportedOperationException("Your system seems not to support the hash algorithm '"
|
||||||
|
+ algorithm.getKey() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package fr.xephi.authme.security;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Java-supported names to get a {@link MessageDigest} instance with.
|
||||||
|
*
|
||||||
|
* @see <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA">
|
||||||
|
* Crypto Spec Appendix A: Standard Names</a>
|
||||||
|
*/
|
||||||
|
public enum MessageDigestAlgorithm {
|
||||||
|
|
||||||
|
MD5("MD5"),
|
||||||
|
|
||||||
|
SHA1("SHA-1"),
|
||||||
|
|
||||||
|
SHA256("SHA-256"),
|
||||||
|
|
||||||
|
SHA512("SHA-512");
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
MessageDigestAlgorithm(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,8 +13,12 @@
|
|||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
|
import fr.xephi.authme.security.crypts.description.Usage;
|
||||||
|
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||||
|
import fr.xephi.authme.security.crypts.description.SaltType;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,11 +63,13 @@ import java.security.SecureRandom;
|
|||||||
* @author Damien Miller
|
* @author Damien Miller
|
||||||
* @version 0.2
|
* @version 0.2
|
||||||
*/
|
*/
|
||||||
|
@Recommendation(Usage.RECOMMENDED)
|
||||||
|
@HasSalt(value = SaltType.TEXT, length = BCRYPT.BCRYPT_SALT_LEN)
|
||||||
public class BCRYPT implements EncryptionMethod {
|
public class BCRYPT implements EncryptionMethod {
|
||||||
|
|
||||||
// BCrypt parameters
|
// BCrypt parameters
|
||||||
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
||||||
private static final int BCRYPT_SALT_LEN = 16;
|
protected static final int BCRYPT_SALT_LEN = 16;
|
||||||
|
|
||||||
// Blowfish parameters
|
// Blowfish parameters
|
||||||
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
||||||
@ -508,14 +514,20 @@ public class BCRYPT implements EncryptionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
return hashpw(password, salt);
|
return hashpw(password, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String computeHash(String password, String name) {
|
||||||
|
return hashpw(password, generateSalt());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
return checkpw(password, hash);
|
return checkpw(password, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
return BCRYPT.gensalt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,22 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||||
|
import fr.xephi.authme.security.crypts.description.Usage;
|
||||||
|
|
||||||
/**
|
@Recommendation(Usage.DOES_NOT_WORK)
|
||||||
*/
|
|
||||||
public class BCRYPT2Y implements EncryptionMethod {
|
public class BCRYPT2Y implements EncryptionMethod {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
if (salt.length() == 22)
|
if (salt.length() == 22)
|
||||||
salt = "$2y$10$" + salt;
|
salt = "$2y$10$" + salt;
|
||||||
return (BCRYPT.hashpw(password, salt));
|
return (BCRYPT.hashpw(password, salt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
String ok = hash.substring(0, 29);
|
String ok = hash.substring(0, 29);
|
||||||
if (ok.length() != 29)
|
return ok.length() == 29 && hash.equals(computeHash(password, ok, playerName));
|
||||||
return false;
|
|
||||||
return hash.equals(computeHash(password, ok, playerName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
|
import fr.xephi.authme.security.HashUtils;
|
||||||
|
import fr.xephi.authme.security.MessageDigestAlgorithm;
|
||||||
|
import fr.xephi.authme.security.crypts.description.Usage;
|
||||||
|
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||||
|
import fr.xephi.authme.security.crypts.description.SaltType;
|
||||||
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
@Recommendation(Usage.DO_NOT_USE)
|
||||||
*/
|
@HasSalt(SaltType.USERNAME)
|
||||||
public class CRAZYCRYPT1 implements EncryptionMethod {
|
public class CRAZYCRYPT1 implements EncryptionMethod {
|
||||||
|
|
||||||
private static final char[] CRYPTCHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
private static final char[] CRYPTCHARS =
|
||||||
protected final Charset charset = Charset.forName("UTF-8");
|
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
private final Charset charset = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
private static String byteArrayToHexString(final byte... args) {
|
||||||
public static String byteArrayToHexString(final byte... args) {
|
|
||||||
final char[] chars = new char[args.length * 2];
|
final char[] chars = new char[args.length * 2];
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
chars[i * 2] = CRYPTCHARS[(args[i] >> 4) & 0xF];
|
chars[i * 2] = CRYPTCHARS[(args[i] >> 4) & 0xF];
|
||||||
@ -22,21 +28,23 @@ public class CRAZYCRYPT1 implements EncryptionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
return computeHash(password, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String computeHash(String password, String name) {
|
||||||
final String text = "ÜÄaeut//&/=I " + password + "7421€547" + name + "__+IÄIH§%NK " + password;
|
final String text = "ÜÄaeut//&/=I " + password + "7421€547" + name + "__+IÄIH§%NK " + password;
|
||||||
try {
|
final MessageDigest md = HashUtils.getDigest(MessageDigestAlgorithm.SHA512);
|
||||||
final MessageDigest md = MessageDigest.getInstance("SHA-512");
|
md.update(text.getBytes(charset), 0, text.length());
|
||||||
md.update(text.getBytes(charset), 0, text.length());
|
return byteArrayToHexString(md.digest());
|
||||||
return byteArrayToHexString(md.digest());
|
|
||||||
} catch (final NoSuchAlgorithmException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
return hash.equals(computeHash(password, playerName));
|
||||||
return hash.equals(computeHash(password, null, playerName));
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
|
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||||
|
import fr.xephi.authme.security.crypts.description.Usage;
|
||||||
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
|
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
|
||||||
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
|
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
@Recommendation(Usage.DOES_NOT_WORK)
|
||||||
*/
|
|
||||||
public class CryptPBKDF2 implements EncryptionMethod {
|
public class CryptPBKDF2 implements EncryptionMethod {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,18 +1,21 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
|
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;
|
||||||
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
|
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
|
||||||
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
|
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
|
||||||
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
@Recommendation(Usage.OK)
|
||||||
*/
|
@HasSalt(value = SaltType.TEXT, length = 12)
|
||||||
public class CryptPBKDF2Django implements EncryptionMethod {
|
public class CryptPBKDF2Django implements EncryptionMethod {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
String result = "pbkdf2_sha256$15000$" + salt + "$";
|
String result = "pbkdf2_sha256$15000$" + salt + "$";
|
||||||
PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 15000);
|
PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 15000);
|
||||||
PBKDF2Engine engine = new PBKDF2Engine(params);
|
PBKDF2Engine engine = new PBKDF2Engine(params);
|
||||||
@ -20,9 +23,12 @@ public class CryptPBKDF2Django implements EncryptionMethod {
|
|||||||
return result + String.valueOf(DatatypeConverter.printBase64Binary(engine.deriveKey(password, 32)));
|
return result + String.valueOf(DatatypeConverter.printBase64Binary(engine.deriveKey(password, 32)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String computeHash(String password, String name) {
|
||||||
|
return computeHash(password, generateSalt(), null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
String[] line = hash.split("\\$");
|
String[] line = hash.split("\\$");
|
||||||
String salt = line[2];
|
String salt = line[2];
|
||||||
byte[] derivedKey = DatatypeConverter.parseBase64Binary(line[3]);
|
byte[] derivedKey = DatatypeConverter.parseBase64Binary(line[3]);
|
||||||
@ -31,4 +37,8 @@ public class CryptPBKDF2Django implements EncryptionMethod {
|
|||||||
return engine.verifyKey(password);
|
return engine.verifyKey(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
return HashUtils.generateSalt(12);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,31 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import fr.xephi.authme.security.HashUtils;
|
||||||
import java.security.MessageDigest;
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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 DOUBLEMD5 implements EncryptionMethod {
|
public class DOUBLEMD5 implements EncryptionMethod {
|
||||||
|
|
||||||
private static String getMD5(String message)
|
@Override
|
||||||
throws NoSuchAlgorithmException {
|
public String computeHash(String password, String salt, String name) {
|
||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
return computeHash(password, null);
|
||||||
md5.reset();
|
}
|
||||||
md5.update(message.getBytes());
|
|
||||||
byte[] digest = md5.digest();
|
public String computeHash(String password, String name) {
|
||||||
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
|
return HashUtils.md5(HashUtils.md5(password));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
throws NoSuchAlgorithmException {
|
return hash.equals(computeHash(password, null, null));
|
||||||
return getMD5(getMD5(password));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean comparePassword(String hash, String password,
|
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
return hash.equals(computeHash(password, "", ""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,4 +32,8 @@ public interface EncryptionMethod {
|
|||||||
boolean comparePassword(String hash, String password, String playerName)
|
boolean comparePassword(String hash, String password, String playerName)
|
||||||
throws NoSuchAlgorithmException;
|
throws NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
// String generateSalt();
|
||||||
|
|
||||||
|
// String computeHash(String password, String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,35 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import fr.xephi.authme.security.HashUtils;
|
||||||
import java.security.MessageDigest;
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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)
|
||||||
*/
|
@HasSalt(value = SaltType.TEXT, length = 32)
|
||||||
public class JOOMLA implements EncryptionMethod {
|
public class JOOMLA implements EncryptionMethod {
|
||||||
|
|
||||||
private static String getMD5(String message)
|
@Override
|
||||||
throws NoSuchAlgorithmException {
|
public String computeHash(String password, String salt, String name) {
|
||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
return HashUtils.md5(password + salt) + ":" + salt;
|
||||||
md5.reset();
|
}
|
||||||
md5.update(message.getBytes());
|
|
||||||
byte[] digest = md5.digest();
|
public String computeHash(String password, String name) {
|
||||||
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
|
return computeHash(password, generateSalt(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
return HashUtils.generateSalt(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
throws NoSuchAlgorithmException {
|
String[] hashParts = hash.split(":");
|
||||||
return getMD5(password + salt) + ":" + salt;
|
if (hashParts.length != 2) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
@Override
|
String salt = hashParts[1];
|
||||||
public boolean comparePassword(String hash, String password,
|
return hash.equals(computeHash(password, salt, null));
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
String salt = hash.split(":")[1];
|
|
||||||
return hash.equals(getMD5(password + salt) + ":" + salt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,26 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import fr.xephi.authme.security.HashUtils;
|
||||||
import java.security.MessageDigest;
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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 MD5 implements EncryptionMethod {
|
public class MD5 implements EncryptionMethod {
|
||||||
|
|
||||||
private static String getMD5(String message)
|
@Override
|
||||||
throws NoSuchAlgorithmException {
|
public String computeHash(String password, String salt, String name) {
|
||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
return computeHash(password, null);
|
||||||
md5.reset();
|
}
|
||||||
md5.update(message.getBytes());
|
|
||||||
byte[] digest = md5.digest();
|
public String computeHash(String password, String name) {
|
||||||
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
|
return HashUtils.md5(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
throws NoSuchAlgorithmException {
|
return hash.equals(computeHash(password, null));
|
||||||
return getMD5(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean comparePassword(String hash, String password,
|
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
return hash.equals(computeHash(password, "", ""));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,34 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import fr.xephi.authme.security.HashUtils;
|
||||||
import java.security.MessageDigest;
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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.md5;
|
||||||
*/
|
|
||||||
|
@Recommendation(Usage.OK)
|
||||||
|
@HasSalt(value = SaltType.TEXT, length = 16)
|
||||||
public class MD5VB implements EncryptionMethod {
|
public class MD5VB implements EncryptionMethod {
|
||||||
|
|
||||||
private static String getMD5(String message)
|
@Override
|
||||||
throws NoSuchAlgorithmException {
|
public String computeHash(String password, String salt, String name) {
|
||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
return "$MD5vb$" + salt + "$" + md5(md5(password) + salt);
|
||||||
md5.reset();
|
}
|
||||||
md5.update(message.getBytes());
|
|
||||||
byte[] digest = md5.digest();
|
public String computeHash(String password, String name) {
|
||||||
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
|
return computeHash(password, generateSalt(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
return "$MD5vb$" + salt + "$" + getMD5(getMD5(password) + salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean comparePassword(String hash, String password,
|
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
String[] line = hash.split("\\$");
|
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 HashUtils.generateSalt(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,31 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import fr.xephi.authme.security.HashUtils;
|
||||||
import java.security.MessageDigest;
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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 SHA1 implements EncryptionMethod {
|
public class SHA1 implements EncryptionMethod {
|
||||||
|
|
||||||
private static String getSHA1(String message)
|
@Override
|
||||||
throws NoSuchAlgorithmException {
|
public String computeHash(String password, String salt, String name) {
|
||||||
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
|
return computeHash(password, null);
|
||||||
sha1.reset();
|
}
|
||||||
sha1.update(message.getBytes());
|
|
||||||
byte[] digest = sha1.digest();
|
public String computeHash(String password, String name) {
|
||||||
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
|
return HashUtils.sha1(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
throws NoSuchAlgorithmException {
|
return hash.equals(computeHash(password, null));
|
||||||
return getSHA1(password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String generateSalt() {
|
||||||
public boolean comparePassword(String hash, String password,
|
return null;
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
return hash.equals(computeHash(password, "", ""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,11 +59,15 @@ package fr.xephi.authme.security.crypts;
|
|||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
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 java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
@Recommendation(Usage.DO_NOT_USE)
|
||||||
*/
|
@HasSalt(SaltType.NONE)
|
||||||
public class WHIRLPOOL implements EncryptionMethod {
|
public class WHIRLPOOL implements EncryptionMethod {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -383,8 +387,11 @@ public class WHIRLPOOL implements EncryptionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
return computeHash(password, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String computeHash(String password, String name) {
|
||||||
byte[] digest = new byte[DIGESTBYTES];
|
byte[] digest = new byte[DIGESTBYTES];
|
||||||
NESSIEinit();
|
NESSIEinit();
|
||||||
NESSIEadd(password);
|
NESSIEadd(password);
|
||||||
@ -393,8 +400,8 @@ public class WHIRLPOOL implements EncryptionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
return hash.equals(computeHash(password, null, null));
|
||||||
return hash.equals(computeHash(password, "", ""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
|
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 java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
@Recommendation(Usage.OK)
|
||||||
*/
|
@HasSalt(value = SaltType.TEXT, length = 9)
|
||||||
public class WORDPRESS implements EncryptionMethod {
|
public class WORDPRESS implements EncryptionMethod {
|
||||||
|
|
||||||
private static final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
private static final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
@ -102,18 +107,25 @@ public class WORDPRESS implements EncryptionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
byte random[] = new byte[6];
|
byte random[] = new byte[6];
|
||||||
this.randomGen.nextBytes(random);
|
randomGen.nextBytes(random);
|
||||||
return crypt(password, gensaltPrivate(stringToUtf8(new String(random))));
|
return crypt(password, gensaltPrivate(stringToUtf8(new String(random))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String computeHash(String password, String name) {
|
||||||
|
return computeHash(password, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
String comparedHash = crypt(password, hash);
|
String comparedHash = crypt(password, hash);
|
||||||
return comparedHash.equals(hash);
|
return comparedHash.equals(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
// This hash uses a salt, but it is not exposed to the outside
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
package fr.xephi.authme.security.crypts;
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
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.RECOMMENDED)
|
||||||
*/
|
@HasSalt(value = SaltType.TEXT, length = 12)
|
||||||
public class XAUTH implements EncryptionMethod {
|
public class XAUTH implements EncryptionMethod {
|
||||||
|
|
||||||
public static String getWhirlpool(String message) {
|
public static String getWhirlpool(String message) {
|
||||||
@ -16,19 +20,25 @@ public class XAUTH implements EncryptionMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String computeHash(String password, String salt, String name)
|
public String computeHash(String password, String salt, String name) {
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
String hash = getWhirlpool(salt + password).toLowerCase();
|
String hash = getWhirlpool(salt + password).toLowerCase();
|
||||||
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
|
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
|
||||||
return hash.substring(0, saltPos) + salt + hash.substring(saltPos);
|
return hash.substring(0, saltPos) + salt + hash.substring(saltPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String computeHash(String password, String name) {
|
||||||
|
return computeHash(password, generateSalt(), null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean comparePassword(String hash, String password,
|
public boolean comparePassword(String hash, String password, String playerName) {
|
||||||
String playerName) throws NoSuchAlgorithmException {
|
|
||||||
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
|
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
|
||||||
String salt = hash.substring(saltPos, saltPos + 12);
|
String salt = hash.substring(saltPos, saltPos + 12);
|
||||||
return hash.equals(computeHash(password, salt, ""));
|
return hash.equals(computeHash(password, salt, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String generateSalt() {
|
||||||
|
return HashUtils.generateSalt(12);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
package fr.xephi.authme.security.crypts.description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the type of salt the encryption algorithm uses. This is purely for documentation
|
||||||
|
* purposes and is ignored by the code.
|
||||||
|
*/
|
||||||
|
public @interface HasSalt {
|
||||||
|
|
||||||
|
SaltType value();
|
||||||
|
|
||||||
|
int length() default 0;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package fr.xephi.authme.security.crypts.description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to mark a hash algorithm with the usage recommendation, see {@link Usage}.
|
||||||
|
*/
|
||||||
|
public @interface Recommendation {
|
||||||
|
|
||||||
|
Usage value();
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package fr.xephi.authme.security.crypts.description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of salt used by an encryption algorithms.
|
||||||
|
*/
|
||||||
|
public enum SaltType {
|
||||||
|
|
||||||
|
/** Random, newly generated text. */
|
||||||
|
TEXT,
|
||||||
|
|
||||||
|
/** The username, including variations or repetitions. */
|
||||||
|
USERNAME,
|
||||||
|
|
||||||
|
/** No salt. */
|
||||||
|
NONE
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package fr.xephi.authme.security.crypts.description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage recommendation that can be provided for a hash algorithm.
|
||||||
|
*/
|
||||||
|
public enum Usage {
|
||||||
|
|
||||||
|
/** The hash algorithm appears to be cryptographically secure and is one of the algorithms recommended by AuthMe. */
|
||||||
|
RECOMMENDED,
|
||||||
|
|
||||||
|
/** There are safer algorithms that can be chosen but using the algorithm is generally OK. */
|
||||||
|
OK,
|
||||||
|
|
||||||
|
/** Hash algorithm is not recommended to be used. Use only if required by another system. */
|
||||||
|
DO_NOT_USE,
|
||||||
|
|
||||||
|
/** The algorithm does not work properly; do not use. */
|
||||||
|
DOES_NOT_WORK
|
||||||
|
|
||||||
|
}
|
||||||
@ -134,7 +134,7 @@ public abstract class AbstractEncryptionMethodTest {
|
|||||||
return BCRYPT.gensalt();
|
return BCRYPT.gensalt();
|
||||||
} else if (method instanceof MD5 || method instanceof WORDPRESS || method instanceof SMF
|
} else if (method instanceof MD5 || method instanceof WORDPRESS || method instanceof SMF
|
||||||
|| method instanceof SHA512 || method instanceof SHA1 || method instanceof ROYALAUTH
|
|| method instanceof SHA512 || method instanceof SHA1 || method instanceof ROYALAUTH
|
||||||
|| method instanceof DOUBLEMD5) {
|
|| method instanceof DOUBLEMD5 || method instanceof CRAZYCRYPT1) {
|
||||||
return "";
|
return "";
|
||||||
} else if (method instanceof JOOMLA || method instanceof SALTEDSHA512) {
|
} else if (method instanceof JOOMLA || method instanceof SALTEDSHA512) {
|
||||||
return PasswordSecurity.createSalt(32);
|
return PasswordSecurity.createSalt(32);
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
package fr.xephi.authme.security.crypts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link CRAZYCRYPT1}.
|
||||||
|
*/
|
||||||
|
public class CRAZYCRYPT1Test extends AbstractEncryptionMethodTest {
|
||||||
|
|
||||||
|
public CRAZYCRYPT1Test() {
|
||||||
|
super(new CRAZYCRYPT1(),
|
||||||
|
"d5c76eb36417d4e97ec62609619e40a9e549a2598d0dab5a7194fd997a9305af78de2b93f958e150d19dd1e7f821043379ddf5f9c7f352bf27df91ae4913f3e8", // password
|
||||||
|
"49c63f827c88196871e344e589bd46cc4fa6db3c27801bbad5374c0d216381977627c1d76f2114667d5dd117e046f7493eb06e4f461f4f848aa08f6f40a3e934", // PassWord1
|
||||||
|
"6fefb0233bab6e6efb9c16f82cb0d8f569488905e2dae0e7c9dde700e7363da67213d37c44bc15f4a05854c9c21e5688389d416413c7309398aa96cb1f341d08", // &^%te$t?Pw@_
|
||||||
|
"46f51cde7657fdec9848bad0fd8e7fb97783cf5335f94dbb5260899ab0b04022a52d651b1c45345328850178e7165308c8c213040b0864de66018a0b769d37cb"); // âË_3(íù*
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user