如何在Android中对字符串进行哈希处理?

56

我正在开发一个Android应用程序,并有一些字符串需要在发送到数据库之前进行加密。我想要一个安全、易于实现的方案,每次传递相同数据时都会生成相同的结果,并且最好生成的字符串长度不受其输入数据长度影响。也许我需要使用哈希算法。


1
哈希是单向的,如果您想要解密数据,您不能使用恒定长度存储,以我个人的看法。 - Alois Cochard
2
我知道。这是为了验证,我只需要能够比较一个值和另一个值,不需要“撤消”它。我知道我没有说我是否打算解密,所以谢谢你的回答。 - Jorsher
1
不要误会,但是你的问题标题有误导性。 - Ton Snoei
12个回答

97

这段代码可以计算任何给定字符串的 MD5 值。

public String md5(String s) {
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
        digest.update(s.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        for (int i=0; i<messageDigest.length; i++)
            hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

来源: http://www.androidsnippets.com/snippets/52/index.html

希望这对你有用


2
MD5,据我所知,不被认为是可逆的。通常您会使用它来散列一些内容,通常是密码或类似的内容,然后要验证密码,您将运行相同的加密并将结果与存储的内容进行比较。 - Jorsher
31
这段代码无法正常工作,生成的字符串中有一些“0”字符会丢失。我不知道为什么会这样,但事实确实如此。 - Sertalp B. Cay
8
当两位十六进制数字的第一位是0时,这段代码会出现特殊情况。这段代码更好:https://dev59.com/73RC5IYBdhLWcg3wFM_X#6565597 - Jaec
填充问题非常容易解决。在迭代循环中附加每个字节的十六进制“String”之前,在其前面添加“0”,直到长度为2,然后将其附加到“hexString”上。现在对我完美运行。 - Steven Byle
2
同意@SertalpBilal的观点,正确的答案在这里https://dev59.com/G2445IYBdhLWcg3wdqIL#4846511。 - Alexandr
显示剩余4条评论

65

上述函数来自 (http://www.androidsnippets.org/snippets/52/index.html),但存在缺陷。如果消息摘要中的数字之一不是两个字符的十六进制值(即0x09),它将无法正常工作,因为它不会使用0进行填充。如果您搜索一下,就会发现有关该函数不起作用的投诉。这里是一个更好的在此页面的注释部分中找到的,我稍微修改了一下:

public static String md5(String s) 
{
    MessageDigest digest;
    try
    {
        digest = MessageDigest.getInstance("MD5");
        digest.update(s.getBytes(Charset.forName("US-ASCII")),0,s.length());
        byte[] magnitude = digest.digest();
        BigInteger bi = new BigInteger(1, magnitude);
        String hash = String.format("%0" + (magnitude.length << 1) + "x", bi);
        return hash;
    }
    catch (NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    return "";
}

6
这个也有问题。尝试编码 q4m'x68n6_YDB4ty8VC4&}wqBtn^68W,它给出的结果是 c70bb931f03b75af1591f261eb77d0b,而正确的应该是 0c70bb931f03b75af1591f261eb77d0b。第一个 0 消失了。 - Sertalp B. Cay
2
不,这会导致问题,它会丢弃0。我已经在.NET和Java中尝试过这个函数,它会丢弃0。 - CoronaPintu
7
我已经编辑过了,以便处理掉开头的零被丢弃的问题...现在它可以正常运行了 :) - Yash Sampat

21

不起作用的方法:

public static String md5(String s) {
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest
                .getInstance("MD5");
        digest.update(s.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < messageDigest.length; i++)
            hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

结果:1865e62e7129927f6e4cd9bff104f0(长度为30)

工作方法:

public static final String md5(final String toEncrypt) {
    try {
        final MessageDigest digest = MessageDigest.getInstance("md5");
        digest.update(toEncrypt.getBytes());
        final byte[] bytes = digest.digest();
        final StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(String.format("%02X", bytes[i]));
        }
        return sb.toString().toLowerCase();
    } catch (Exception exc) {
        return ""; // Impossibru!
    }
}

结果:1865e62e7129927f6e4c0d9bff1004f0(长度为32)


7
private static char[] hextable = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

public static String byteArrayToHex(byte[] array) {
    String s = "";
    for (int i = 0; i < array.length; ++i) {
        int di = (array[i] + 256) & 0xFF; // Make it unsigned
        s = s + hextable[(di >> 4) & 0xF] + hextable[di & 0xF];
    }
    return s;
}

public static String digest(String s, String algorithm) {
    MessageDigest m = null;
    try {
        m = MessageDigest.getInstance(algorithm);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return s;
    }

    m.update(s.getBytes(), 0, s.length());
    return byteArrayToHex(m.digest());
}

public static String md5(String s) {
    return digest(s, "MD5");
}

2
是的,100%的工作。我曾尝试上述函数,但所有情况都会有0丢失的问题,但这三个函数的解决方案给了我确切的解决方案,我已经将其与.NET MD5加密进行匹配。 - CoronaPintu
这是支持UTF-8的解决方案:https://dev59.com/gm865IYBdhLWcg3wM7u0#19589939 - Climbatize

6

使用@Donut的解决方案,对于UTF-8编码的字符(例如:é),您需要使用getBytes("UTF-8")。以下是我的修正摘要方法:

private static char[] hextable = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};


public static String byteArrayToHex(byte[] array) {
    String s = "";
    for (int i = 0; i < array.length; ++i) {
        int di = (array[i] + 256) & 0xFF; // Make it unsigned
        s = s + hextable[(di >> 4) & 0xF] + hextable[di & 0xF];
    }
    return s;
}

public static String digest(String s, String algorithm) {
    MessageDigest m = null;
    try {
        m = MessageDigest.getInstance(algorithm);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return s;
    }

    try {
        m.update(s.getBytes("UTF-8"));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
        m.update(s.getBytes());
    }
    return byteArrayToHex(m.digest());
}

public static String md5(String s) {
    return digest(s, "MD5");
}

5
上面的回答几乎100%正确。它在处理Unicode时会失败。
    MessageDigest digest;
    try {
        digest = MessageDigest.getInstance("MD5");
        byte utf8_bytes[] = tag_xml.getBytes();
        digest.update(utf8_bytes,0,utf8_bytes.length);
        hash = new BigInteger(1, digest.digest()).toString(16);
    } 
    catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

需要从字节数组中获取长度而不是字符串。

这里的 tag_xml 是什么意思?尝试使用上面的代码 q4m'x68n6_YDB4ty8VC4&}wqBtn^68W,结果是 c70bb931f03b75af1591f261eb77d0b 而不是 @Sertap Bilal 在这个答案之上的评论中提到的 0c70bb931f03b75af1591f261eb77d0b - Gautam Mandsorwale

3
甜甜圈的解决方案在一个函数中:
private static char[] hextable = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

private static String md5(String s)
{
    MessageDigest digest;
    try
    {
        digest = MessageDigest.getInstance("MD5");
        digest.update(s.getBytes(), 0, s.length());
        byte[] bytes = digest.digest();

        String hash = "";
        for (int i = 0; i < bytes.length; ++i)
        {
            int di = (bytes[i] + 256) & 0xFF;
            hash = hash + hextable[(di >> 4) & 0xF] + hextable[di & 0xF];
        }

        return hash;
    }
    catch (NoSuchAlgorithmException e)
    {
    }

    return "";
}

3

如果您没有安全限制,只想将字符串转换为唯一的整数。我写这篇文章是因为我正在寻找这个功能并来到了这里。

String my_key
int my_key.hashCode()

如果您有多达10个字符,甚至会是独一无二的。另请参见https://dev59.com/6GMm5IYBdhLWcg3wfu42#17583653


2
这里缺少了一个零。
  public static String md5(String string) {
        if (TextUtils.isEmpty(string)) {
            return "";
        }
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest(string.getBytes());
            String result = "";
            for (byte b : bytes) {
                String temp = Integer.toHexString(b & 0xff);
                if (temp.length() == 1) {
                    temp = "0" + temp;
                }
                result += temp;
            }
            return result;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

2
以下内容对我在Android上的操作有效,不会截断任何前面的0:
MessageDigest md = null;
String digest = null;
    try {
        md = MessageDigest.getInstance("MD5");

        byte[] hash = md.digest(myStringToEncode.getBytes("UTF-8")); //converting byte array to Hexadecimal String
        StringBuilder sb = new StringBuilder(2*hash.length);

        for(byte b : hash){
            sb.append(String.format("%02x", b&0xff));
        }

        digest = sb.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

return digest;

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