mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-29 18:34:42 +00:00
Check / Set passwords for password tags
This commit is contained in:
246
libsrc/ffdec_lib/src/com/jpexs/helpers/MD5Crypt.java
Normal file
246
libsrc/ffdec_lib/src/com/jpexs/helpers/MD5Crypt.java
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.helpers;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* MD5 crypt - based on passlib.hash.md5_crypt
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class MD5Crypt {
|
||||
|
||||
private static final String SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
private static final String HASH64_CHARS = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
public static final String MAGIC = "$1$";
|
||||
public static final String MAGIC_APACHE = "$apr1$";
|
||||
|
||||
public static boolean checkPassword(String password, String hash) {
|
||||
String magic;
|
||||
|
||||
if (hash.startsWith(MAGIC)) {
|
||||
magic = MAGIC;
|
||||
} else if (hash.startsWith(MAGIC_APACHE)) {
|
||||
magic = MAGIC_APACHE;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
String checksum = hash.substring(magic.length());
|
||||
String salt = "";
|
||||
if (checksum.contains("$")) {
|
||||
salt = checksum.substring(0, checksum.indexOf("$"));
|
||||
}
|
||||
return hash.equals(crypt(password, salt, magic));
|
||||
}
|
||||
|
||||
public static String generateSalt(int length) {
|
||||
Random rnd = new Random();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(SALT_CHARS.charAt(rnd.nextInt(SALT_CHARS.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String crypt(String password, int saltLength, String magic) {
|
||||
return crypt(password, generateSalt(saltLength), magic);
|
||||
}
|
||||
|
||||
public static String crypt(String password, int saltLength) {
|
||||
return crypt(password, generateSalt(saltLength), MAGIC);
|
||||
}
|
||||
|
||||
public static String cryptApache(String password, int saltLength) {
|
||||
return crypt(password, generateSalt(saltLength), MAGIC_APACHE);
|
||||
}
|
||||
|
||||
public static String crypt(String password, String salt) {
|
||||
return crypt(password, salt, MAGIC);
|
||||
}
|
||||
|
||||
public static String cryptApache(String password, String salt) {
|
||||
return crypt(password, salt, MAGIC_APACHE);
|
||||
}
|
||||
|
||||
private static String crypt(String password, String salt, String magic) {
|
||||
|
||||
if (salt.startsWith(magic)) {
|
||||
salt = salt.substring(magic.length());
|
||||
}
|
||||
if (salt.length() > 8) {
|
||||
salt = salt.substring(0, 8);
|
||||
}
|
||||
|
||||
byte[] passwordBytes = new byte[0];
|
||||
byte[] constBytes = new byte[0];
|
||||
byte[] saltBytes = new byte[0];
|
||||
try {
|
||||
passwordBytes = password.getBytes("UTF-8");
|
||||
saltBytes = salt.getBytes("UTF-8");
|
||||
constBytes = magic.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
//ignore
|
||||
}
|
||||
|
||||
MessageDigest b;
|
||||
try {
|
||||
b = MessageDigest.getInstance("MD5"); //Start MD5 digest B
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
return null;
|
||||
}
|
||||
b.update(passwordBytes); //Add the password to digest B
|
||||
b.update(saltBytes);//Add the salt to digest B
|
||||
b.update(passwordBytes); //Add the password to digest B
|
||||
byte[] digest_b = b.digest(); //Finish MD5 digest B
|
||||
|
||||
MessageDigest a;
|
||||
try {
|
||||
a = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
return null;
|
||||
}
|
||||
a.update(passwordBytes); //Add the password to digest A
|
||||
a.update(constBytes); //Add the constant string $1$ to digest A
|
||||
a.update(saltBytes); //Add the salt to digest A
|
||||
|
||||
//For each block of 16 bytes in the password string, add digest B to digest A
|
||||
for (int i = passwordBytes.length; i > 0; i -= 16) {
|
||||
if (i >= 16) {
|
||||
a.update(digest_b);
|
||||
} else { //For the remaining N bytes of the password string, add the first N bytes of digest B to digest A
|
||||
a.update(digest_b, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
For each bit in the binary representation of the length of the password string; starting with the lowest value bit, up to and including the largest-valued bit that is set to 1:
|
||||
|
||||
If the current bit is set 0 (!! not 1), add the first character of the password to digest A.
|
||||
Otherwise, add a NULL character to digest A.
|
||||
|
||||
(If the password is the empty string, step 14 is omitted entirely).
|
||||
*/
|
||||
for (int i = passwordBytes.length; i > 0; i = i >>> 1) {
|
||||
if ((i & 1) == 0) {
|
||||
a.update(passwordBytes, 0, 1);
|
||||
} else {
|
||||
a.update((byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] digest_a = a.digest(); //Finish MD5 digest A
|
||||
byte[] round_result = null;
|
||||
|
||||
//For 1000 rounds (round values 0..999 inclusive)
|
||||
for (int round = 0; round < 1000; round++) {
|
||||
MessageDigest c;
|
||||
try {
|
||||
c = MessageDigest.getInstance("MD5"); //Start MD5 digest C
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//If the round is odd, add the password to digest C
|
||||
if (round % 2 == 1) {
|
||||
c.update(passwordBytes);
|
||||
}
|
||||
|
||||
//If the round is even, add the previous round’s result to digest C (for round 0, add digest A instead).
|
||||
if (round % 2 == 0) {
|
||||
if (round == 0) {
|
||||
c.update(digest_a);
|
||||
} else {
|
||||
c.update(round_result);
|
||||
}
|
||||
}
|
||||
//If the round is not a multiple of 3, add the salt to digest C.
|
||||
if (round % 3 != 0) {
|
||||
c.update(saltBytes);
|
||||
}
|
||||
|
||||
//If the round is not a multiple of 7, add the password to digest C.
|
||||
if (round % 7 != 0) {
|
||||
c.update(passwordBytes);
|
||||
}
|
||||
|
||||
//If the round is even, add the password to digest C.
|
||||
if (round % 2 == 0) {
|
||||
c.update(passwordBytes);
|
||||
}
|
||||
|
||||
//If the round is odd, add the previous round’s result to digest C (for round 0, add digest A instead).
|
||||
if (round % 2 == 1) {
|
||||
if (round == 0) {
|
||||
c.update(digest_a);
|
||||
} else {
|
||||
c.update(round_result);
|
||||
}
|
||||
}
|
||||
|
||||
//Use the final value of MD5 digest C as the result for this round.
|
||||
round_result = c.digest();
|
||||
}
|
||||
|
||||
//Transpose the 16 bytes of the final round’s result in the following order:
|
||||
//12,6,0,13,7,1,14,8,2,15,9,3,5,10,4,11
|
||||
byte[] transposed = new byte[]{
|
||||
round_result[12],
|
||||
round_result[6],
|
||||
round_result[0],
|
||||
round_result[13],
|
||||
round_result[7],
|
||||
round_result[1],
|
||||
round_result[14],
|
||||
round_result[8],
|
||||
round_result[2],
|
||||
round_result[15],
|
||||
round_result[9],
|
||||
round_result[3],
|
||||
round_result[5],
|
||||
round_result[10],
|
||||
round_result[4],
|
||||
round_result[11]};
|
||||
|
||||
//Encode the resulting 16 byte string into a 22 character hash64-encoded string
|
||||
//(the 2 msb bits encoded by the last hash64 character are used as 0 padding).
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int dstCharRem = 22;
|
||||
for (int srcBytePos = 0; srcBytePos < transposed.length; srcBytePos += 3, dstCharRem -= 4) {
|
||||
long v = (transposed[srcBytePos] & 0xff);
|
||||
if (srcBytePos + 1 < transposed.length) {
|
||||
v |= ((transposed[srcBytePos + 1] & 0xff) << 8);
|
||||
}
|
||||
if (srcBytePos + 2 < transposed.length) {
|
||||
v |= ((transposed[srcBytePos + 2] & 0xff) << 16);
|
||||
}
|
||||
for (int j = 0; j < (dstCharRem >= 4 ? 4 : dstCharRem); j++) {
|
||||
result.append(HASH64_CHARS.charAt((int) (v & 0x3f)));
|
||||
v >>>= 6;
|
||||
}
|
||||
}
|
||||
return magic + salt + "$" + result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user