Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I perform MySQL UNHEX() Function in Java

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.

like image 757
Felix Avatar asked Oct 30 '22 20:10

Felix


1 Answers

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.

1. The actual question: "How do I perform MySQL UNHEX() Function in Java"

1.1 What does the UNHEX() Function in MySQL actually do

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>

2. The answer to the problem I actually wanted to solve when I asked this question: "How do I perform MySQL PASSWORD() Function in Java"

2.1 What does the PASSWORD() Function in MySQL actually do

The MySQL Password() Function is the sha1-Hash of the sha1-Hash of the input, in hexadecimal representation, prefixxed with the literal *.

2.2 Break it down

2.2.1 The SHA1-Hash

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);
    }

2.2.2 The SHA1-Hash of the SHA1-Hash

    public static byte[] mysqlPasswordHash(byte[] data) throws NoSuchAlgorithmException {
        // using the method explained in 2.2.1 twice
        return sha1(sha1(data));
    }

2.2.3 Building hexadecimal representation of a 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);
    }

2.2.4 The equivalent of the MySQL Password Function

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)));
    }

2.3 Calling the method

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);
    }
like image 72
Felix Avatar answered Nov 08 '22 06:11

Felix