I'm trying to get a MySQL Password Hash in Java from a string, so I googled a bit and found out how PASSWORD() in MySQL works:
SELECT SHA1(UNHEX(SHA1('test')));
gives the same result as
SELECT PASSWORD('test');
with this I went on.
I wrote a method to convert a string to SHA1 Hash which works perfectly (tested a few different strings, getting the same result as with SHA1(str) in MySQL)
The next thing to do was the UNHEX() method. There im stuck now.
My current method:
public static String toMySQLPasswordHash(String str)
{
String hash1 = toSHA1Hash(str);
String unhexedHash1 = new String(DatatypeConverter.parseHexBinary(hash1));
String hash2 = toSHA1Hash(unhexedHash1);
String passwordHash = "*" + hash2.toUpperCase();
return passwordHash;
}
My "toSHA1Hash" method:
public static String toSHA1Hash(String str)
{
MessageDigest md = null;
try
{
md = MessageDigest.getInstance("SHA-1");
}
catch (NoSuchAlgorithmException e)
{
Logger.WriteLog(e.toString());
}
if (md == null)
return null;
md.reset();
md.update(str.getBytes());
byte[] byteData = md.digest();
StringBuilder sb = new StringBuilder();
for (byte currByte : byteData)
sb.append(Integer.toString((currByte & 0xff) + 0x100, 16).substring(1));
return sb.toString();
}
I don't like to use any external packages, so please help me doing this with the JDK 1.8.0_40 only.
So, I'm editing my own answer to this question 5 years later, since back then I didn't really know what I was really doing. The answer worked, but wasn't optimal.
The answer to this question is pretty simple. It takes a string of hexadecimal characters and converts it to a binary-object.
Keep in mind: A hexadecimal string is simply one of many ways to represent bytes as text. And as easy as this sentence is, is the answer to this question - we simply have to convert this string to a byte[]
.
And with that knowledge, we have our answer, which has been right here on SO years ago: Convert a string representation of a hex dump to a byte array using Java?
In short:
// this is the Java equivalent to the UNHEX() function in MySQL
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
Or simply:
import javax.xml.bind.DatatypeConverter;
// this is the Java equivalent to the UNHEX() function in MySQL
public static byte[] hexStringToByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
(Java > 8 removed the Java EE Classes, you can add the DataTypeConverter by adding the following dependency):
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
The MySQL Password() Function is the sha1-Hash of the sha1-Hash of the input, in hexadecimal representation, prefixxed with the literal *
.
To build the SHA1-Hash of any input, we can use the MessageDigest
class in Java.
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
return MessageDigest.getInstance(algorithm).digest(data);
}
public static byte[] sha1(byte[] data) throws NoSuchAlgorithmException {
return digest("SHA-1", data);
}
public static byte[] mysqlPasswordHash(byte[] data) throws NoSuchAlgorithmException {
// using the method explained in 2.2.1 twice
return sha1(sha1(data));
}
byte[]
This is the reverse of the UNHEX() Function I described before and already had many answers when I asked this question. See: How to convert a byte array to a hex string in Java?
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.UTF_8);
public static String bytesToHex(byte[] bytes) {
final byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
Or, if you have the DataTypeConverter
available:
public static String bytesToHex(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes);
}
If you added the methods described in the previous steps, the MySQL-Password implementation in Java works like this:
public static String mysqlPasswordHashString(String password, Charset charset) throws NoSuchAlgorithmException {
return "*" + bytesToHex(mysqlPasswordHash(password.getBytes(charset)));
}
To call the method, you have to provide a java.nio.charset.Charset
. To get the same results as you'd run this on your MySQL DB, you have to figure out what the default Charset of your MySQL is.
Let's say your MySQL uses UTF-8:
public static void main(String[] args) throws Exception {
final String mysqlPasswordHash = mysqlPasswordHashString("Hello world", StandardCharsets.UTF_8);
System.out.println(mysqlPasswordHash);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With