如何在Java中执行MySQL UNHEX()函数

7

我正在尝试使用Java从字符串中获取MySQL密码哈希值,因此我搜索了一下并了解了MySQL中PASSWORD()的工作原理:

SELECT SHA1(UNHEX(SHA1('test')));

产生与其相同的结果

SELECT PASSWORD('test');

我继续进行了下一个步骤。

我编写了一个将字符串转换为SHA1哈希的方法,它完美地工作(测试了几个不同的字符串,得到与MySQL中的SHA1(str)相同的结果)

下一步要做的是UNHEX()方法。但我现在被卡住了。

我的当前方法:

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

我的"toSHA1Hash"方法:

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

我不喜欢使用任何外部包,所以请只使用JDK 1.8.0_40帮我完成这个任务。


这个http://docs.oracle.com/javase/7/docs/api/java/security/MessageDigest.html对你没有帮助吗? - IgnazioC
我正在使用MessageDigest创建我的SHA1-散列,它运行良好。但我卡在了解析SHA1散列上。 - Felix
1
也许你想编辑你的帖子并询问如何实现UNHEX()函数... - IgnazioC
1
你不应该将二进制数据存储在字符串(unhexedHash1)中!应该使用字节数组。 - JimmyB
但是我的 toSHA1Hash 方法不接受字节数组。 - Felix
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - RealSkeptic
1个回答

5
所以,我五年后编辑了自己对这个问题的答案,因为当时我并不知道自己在做什么。答案虽然有效,但并不是最优解。

1. 实际问题:“如何在Java中执行MySQL UNHEX()函数”

1.1 MySQL中UNHEX()函数的作用是什么

这个问题的答案非常简单。它将一个十六进制字符的字符串转换为二进制对象。

请记住:十六进制字符串只是表示字节的文本方式之一。就像这句话一样简单,答案也很简单 - 我们只需要将这个字符串转换为byte[]

有了这个知识,我们就有了我们的答案,这个答案早在几年前就在SO上了:如何使用Java将十六进制转储的字符串表示转换为字节数组?

简而言之:

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

或者简单地说:

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移除了Java EE类,您可以通过添加以下依赖项来添加DataTypeConverter:

<dependency>
  <groupId>jakarta.xml.bind</groupId>
  <artifactId>jakarta.xml.bind-api</artifactId>
  <version>2.3.3</version>
</dependency>

2. 当我问这个问题时,我实际想要解决的问题的答案: "如何在Java中执行MySQL PASSWORD()函数"

2.1 MySQL中PASSWORD()函数实际上是做什么的

MySQL的Password()函数是输入的sha1哈希值的sha1哈希值,以十六进制表示前缀*构成的。

2.2 分解

2.2.1 SHA1哈希值

要构建任何输入的SHA1哈希值,我们可以使用Java中的MessageDigest类。

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 SHA1-Hash的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 构建byte[]十六进制表示

这是我之前描述的UNHEX()函数的相反操作,当我提出这个问题时已经有很多答案了。请参见:如何在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);
    }

或者,如果您有可用的DataTypeConverter

    public static String bytesToHex(byte[] bytes) {
        return DatatypeConverter.printHexBinary(bytes);
    }

2.2.4 MySQL密码函数的等效实现

如果您按照前面步骤所述添加了方法,则Java中的MySQL-Password实现如下:

    public static String mysqlPasswordHashString(String password, Charset charset) throws NoSuchAlgorithmException {
        return "*" + bytesToHex(mysqlPasswordHash(password.getBytes(charset)));
    }

2.3 调用方法

要调用该方法,您需要提供一个java.nio.charset.Charset。为了获得与在您的MySQL数据库上运行此操作相同的结果,您必须找出您的MySQL的默认字符集是什么。

假设您的MySQL使用UTF-8:

    public static void main(String[] args) throws Exception {
        final String mysqlPasswordHash = mysqlPasswordHashString("Hello world", StandardCharsets.UTF_8);
        System.out.println(mysqlPasswordHash);
    }

感谢您为我提供了简写的DatatypeConverter.parseHexBinary,我已经寻找了3个小时如何在Java中执行MariaDB的UNHEX()函数... - barfoos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接