使用 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 位的長度。

相關文章
相關標籤/搜索