1. 程式人生 > >"MD5"加密演算法全解析

"MD5"加密演算法全解析

大家好,我們現在來講解關於加密方面的知識,說到加密我認為不得不提MD5,因為這是一種特殊的加密方式,它到底特殊在哪,現在我們就開始學習它

全稱:message-digest algorithm 5
翻譯過來就是:資訊 摘要 演算法 5

1.特點

  • 1.長度固定:

    不管多長的字串,加密後長度都是一樣長
    作用:方便平時資訊的統計和管理

  • 2.易計算:

    字串和檔案加密的過程是容易的.
    作用: 開發者很容易理解和做出加密工具

  • 3.細微性

    一個檔案,不管多大,小到幾k,大到幾G,你只要改變裡面某個字元,那麼都會導致MD5值改變.
    作用:很多軟體和應用在網站提供下載資源,其中包含了對檔案的MD5碼

    ,使用者下載後只需要用工具測一下下載好的檔案,通過對比就知道該檔案是否有過更改變動.

  • 4.不可逆性

    你明明知道密文和加密方式,你卻無法反向計算出原密碼.
    作用:基於這個特點,很多安全的加密方式都是用到.大大提高了資料的安全性

2.後續講解

  • 關於撞庫破解:

    這是概率極低的破解方法,原理就是:

    1.建立一個大型的資料庫,把日常的各個語句,通過MD5加密成為密文,不斷的積累大量的句子,放在一個龐大的資料庫裡.

    2.比如一個人拿到了別人的密文,想去查詢真實的密碼,就需要那這個密文去到提供這個資料庫的公司網站去查詢.

    這就是撞庫的概念.

3.關於MD5加鹽:

比如我的銀行密碼是”12345”

1.得到的MD5是:827ccb0eea8a706c4c34a16891f84e7b

2.一個人擷取到這個密文,那麼通過撞庫肯定容易撞出12345.

3.我們要做的就是加鹽,銀行密碼還是”12345”,然後我把銀行密碼加上我特定的字串才計算MD5
所以密碼還是那個密碼,但是變成求”12345密碼加密987”的MD5值,然後再得到MD5,那麼這個MD5起碼可以確認那個資料庫不會有.

說了那麼多我們開始我們的MD5工具的製作

我們一般加密都是加密字串或者檔案,所以我們的工具就有加密字串和檔案的兩種方法,兩個方法同名,通過過載完成

1.加密字串

邏輯思維:

  • 1.獲取資訊摘要物件:md5

    通過資訊摘要單例的建構函式獲取:

    MessageDigest md5 = MessageDigest.getInstance("MD5");
    
  • 2.資訊摘要物件是對位元組陣列進行摘要的,所以先獲取字串的位元組陣列.

    byte[] bytes = str.getBytes();
    
  • 3.資訊摘要物件對位元組陣列進行摘要,得到摘要位元組陣列:

    byte[] digest = md5.digest(bytes);
    
  • 4.把摘要陣列中的每一個位元組轉換成16進位制,並拼在一起就得到了MD5值.
    (PS,有些轉換過來得到的是前面有6個f的情況,如:ffffff82,這是因為前面有6組4個1,所以提前把這6組1111先變成0就好了,然後再轉16進位制就沒有f了)
    (其實也可以在後面續把f去掉)

2.加密檔案

方法傳入的是檔案物件 : file

  • 1.因為是檔案不是方法,所以不是像剛才那樣通過摘要獲取字串.

  • 2.使用到另一個方法即可:就是資訊摘要物件更新:md5.update(byte[] input)方法,用法是通過讀取流,不斷的更新從流中讀到的”資訊陣列”.

  • 3.然後通過”資訊摘要物件”獲取摘要,不用引數:md5.digest(),此時返回的陣列就已經是包含內容的摘要陣列

以下是詳細程式碼:

public class MD5Tool {
    public static void main(String[] args) throws Exception {
        /*--------------字串--------------*/
        String str = "12345";
        String md1 = getMD5(str);
        System.out.println(md1);//827ccb0eea8a706c4c34a16891f84e7b

        /*--------------檔案--------------*/
        File file = new File("D:\\1.mp3");
        String md2 = getMD5(file);
        System.out.println(md2);//9068aaead9a5b75e6a54395d8183ec9
    }
    /**
     * 邏輯:
     *
     * 1.獲取md5物件,通過"資訊摘要"獲取例項構造("MD5").
     * 2.md5物件對("字串的"位元組形式"-得到的陣列)進行摘要",那麼會返回一個"摘要的位元組陣列"
     * 3.摘要位元組陣列中的"每個二進位制值"位元組形式,"轉成十六進位制形式",然後再把這些值給拼接起來,就是MD5值了
     *      (PS:為了便於閱讀,把多餘的fff去掉,並且單個字元前加個0)
     *
     */
    public static String getMD5(String str) throws Exception {

        String MD5 = "";

        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] bytes = str.getBytes();
        byte[] digest = md5.digest(bytes);

        for (int i = 0; i < digest.length; i++) {
            //摘要位元組陣列中各個位元組的"十六進位制"形式.
            int j = digest[i];
             j = j & 0x000000ff;
            String s1 = Integer.toHexString(j);

            if (s1.length() == 1) {
                s1 = "0" + s1;
            }
            MD5 += s1;
        }
        return MD5;
    }
    //過載,所以使用者傳入"字串"或者"檔案"都可以解決.

    /**
     * 處理邏輯:
     * 1.現在傳入的是"檔案",不是字串
     * 2.所以資訊摘要物件.進行摘要得到陣列不能像上面獲得:md5.digest(bytes),因為不是str.getBytes得到bytes
     * 3.其實還是通過mdt.digest();獲取到位元組陣列,但是前期必須要有一個方法必須是md5.update(),即"資訊摘要物件"要先更新
     * 4."資訊摘要更新"裡面有(byte[] input),說明是讀取流獲取到的陣列,所以我們就用這個方法.
     * 5.所以最終的邏輯就是:
     *
     *      1.獲取檔案的讀取流
     *      2.不停的讀取流中的"內容"放入字串,放一部分就"更新"一部分.直到全部完畢
     *      3.然後呼叫md5.digest();就會得到有內容的位元組陣列,剩下的就和上邊一樣了.
     */
    public static String getMD5(File file) throws Exception {
        String MD5 = "";

        MessageDigest md5 = MessageDigest.getInstance("MD5");
        FileInputStream fis = new FileInputStream(file);

        byte[] bytes = new byte[1024 * 5];

        int len = -1;
        while ((len=fis.read(bytes))!=-1) {
            //一部分一部分更新
            md5.update(bytes, 0, len);
        }
        byte[] digest = md5.digest();
        for (int i = 0; i <digest.length; i++) {
            int n = digest[i] & 0x000000ff;
            String s = Integer.toHexString(n);

            MD5 += s;
        }
        return MD5;
    }
}

拓展

0xfffffff代表的含義:

  • 0x:代表16進位制;

  • 一個f代表:4個1,即(1111);

  • 所以0xffffffff代表:8組4個1

    1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 1111
    
  • 所以剛才的0xffffff82就是前面6組都是1,後面兩組是

    1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 0111 - 0010
    
  • 所以先與上0x000000ff,即

    0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 1111 - 1111
    
  • 就得到了82了

上面的方法也可以寫寫成:

        for (int i = 0; i < digest.length; i++) {
            //摘要位元組陣列中各個位元組的"十六進位制"形式.
            String s1 = Integer.toHexString( digest[i]);

            //如果是8個長度的,把前面的6個f去掉,只獲取後面的
            if (s1.length() == 8) {
                s1 = s1.substring(6);
            }
            if (s1.length() == 1) {
                s1 = "0" + s1;
            }
            MD5 += s1;
        }