使用 MessageDigest 对字符串加密

  今天在阅读代码的时候看到一段很常见的对用户密码进行加密的代码。然鹅,知其然不知其因此然。肯定对其解读一番。java

 

public static final String ALGORITHM = "SHA-256";

public static String encrypt(String orignal, String salt) {
    orignal = orignal + salt;
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance(ALGORITHM);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    if (null != md) {
        byte[] origBytes = orignal.getBytes();
        md.update(origBytes);
        byte[] digestRes = md.digest();
        String digestStr = getDigestStr(digestRes);
        return digestStr;
    }

    return null;
}

private static String getDigestStr(byte[] origBytes) {
    String tempStr = null;
    StringBuilder stb = new StringBuilder();
    for (int i = 0; i < origBytes.length; i++) {
        tempStr = Integer.toHexString(origBytes[i] & 0xff);
        //tempStr = String.valueOf(origBytes[i] & 0xff);
        if (tempStr.length() == 1) {
            stb.append("0");
        }
        stb.append(tempStr);

    }
    return stb.toString();
}

public static void main(String[] args) {
    String str = "123456";
    String rs = encrypt(str, "");
    System.out.println(rs);
}

这是很简单的一段加密代码,百度上处处都是。稍微解读一下,encrypt 方法接收2个参数,orignal 是须要加密的原始字符串,salt 是盐(为了增强密码的强度传入的随机字符串,这里能够忽略)。调用 MessageDigest 类 getInstance 方法获取算法(这里是SHA-256),而后调用相关的 update digest 方法 getDigestStr 对 digest 的返回结果进行一番操做,最终生成加密以后的字符串。算法

看到 getDigestStr 方法内有一段代码数组

Integer.toHexString(origBytes[i] & 0xff);

不明觉厉,脑子里充满疑问。app

1.0xff 什么意思ui

基本功不扎实,一顿百度。如下是结果。加密

0x 开头表示16进制,即16进制下面的 ff。f 对应10进制下 15, 对应2进制下 1111。code

因此 ff 10进制下是 255, 2进制下是 1111 1111。字符串

 

2.origBytes[i] & 0xff 是什么结果get

origBytes[i] 的类型为 byte, 结果就是 byte 转换成2进制和 1111 1111 相与。it

按位与(&)操做的规则是2个bit都为1则结果为1,不然为0。 如 1010 & 1111 = 1010

那 5 & 0xff 是什么结果?

5 在二进制下为 0000 0101, 00000101 和 11111111 相与, 因此是 0000 0101 & 1111 1111 = 0000 0101。结果仍是5。

那 25 & 0xff 是什么结果?

25 在二进制下为 11001, 11001 和 11111111 相与,因此是 0001 1001 & 1111 1111 = 0001 1001。结果仍是 25。

能够发现这2个数和 0xff 相与以后结果都是本身,那这么作的意义何在?

5 & 0xff 是 5, 那 -5 & 0xff 呢?

5 在二进制下是 101,那 -5 在二进制下是什么呢?-101?

这里就要穿插一点计算机知识。

正数在计算机中是已原码的形式存在,负数在计算机中是已补码的形式存在。补码=反码+1。

举个例子:

5 的原码是 0101, 反码是原码逐位取反即 1010, 补码为反码加一 1010 + 1 = 1011。因此 5 的补码为 1011,即 -5 在计算机内部是以 1011 的形式存在。

了解完补码以后就能够知道 -5 & 0xff 的结果,假设-5是一个int类型的数,int类型占用4个byte(32bit)

因此先得到5的原码

0000 0000 0000 0000 0000 0000 0000 0101

再得到5的反码

1111 1111 1111 1111 1111 1111 1111 1010

再得到反码的补码

1111 1111 1111 1111 1111 1111 1111 1011

再和 0xff 相与

0000 0000 0000 0000 0000 0000 1111 1111 (这是 0xff 的32位表示)

结果是

0000 0000 0000 0000 0000 0000 1111 1011

转换为10进制是 251。

因此 -5 & 0xff 的结果转换成10进制是 251,并非-5。

其实观察上面的计算过程能够发现和 0xff 相与至关于取了低8位的bit, 把前面的24位都至0。

 

3.为何origBytes[i]要 & 0xff? 为何是 & 0xff 而不是 & 0xffff 或者是其余的

origBytes[i] 的类型是一个 byte 它占8个bit

origBytes[i] & 0xff 的时候 origBytes[i] 被转成 int 类型,int类型占4个byte(32个bit)

假设如今 origBytes[i] = -5;

origBytes[i] 的值是 1111 1011

origBytes[i] & 0xff 的时候转型成 int 类型,变成了一个32bit的数,

origBytes[i] 的值变成 1111 1111 1111 1111 1111 1111 1111 1011 (扩充到32位)

原来是 0xfb,如今变成了 0xfffffffb

因此和 0xff 相与 把前面的24位都至0 保留低8位,防止了由于符号补位形成的错误。这也是为何是 & 0xff 而不是 & 0xffff,和其余相与都是不正确的。

 

扩展符号补位:

窄的整型转换成较宽的整型时,若是最初的数值类型是有符号的,那么就执行符号扩展。即若是符号位为1,则扩展1,若是为0,则扩展0。若是是 char,那么无论被提高成什么类型,都执行零扩展。byte 是有符号的。

 

扩展2:

byte[] digestRes = md.digest();

算法为 MD5 的时候 digestRes 是一个 16 位的数组,最终的结果是 32 位的长度。

算法为 SHA-1 的时候 digestRes 是一个 20 位的数组,最终的结果是 40 位的长度。

算法为 SHA-256 的时候 digestRes 是一个 32 位的数组,最终的结果是 64 位的长度。

算法为 SHA-384 的时候 digestRes 是一个 48 位的数组,最终的结果是 96 位的长度。

算法为 SHA-512 的时候 digestRes 是一个 64 位的数组,最终的结果是 128 位的长度。

相关文章
相关标签/搜索