加密方式之MD5

                      最近項目中設計加密解密的技術應用場景比較多,因此總結一下JAVA中常見的加密技術。今天總結一下MD5加密。

一、MD5的必要性以及實際應用場景

MD5爲計算機安全領域廣泛使用的一種散列函數,用以提供消息的完整性保護。用於確保信息傳輸完整一致。是計算機廣泛使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言普遍已有MD5實現。將數據(如漢字)運算爲另一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD2、MD3和MD4。

1.MD5算法具有以下特點:

1、壓縮性:任意長度的數據,算出的MD5值長度都是固定的。
2、容易計算:從原數據計算出MD5值很容易。
3、抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所得到的MD5值都有很大區別。
4、弱抗碰撞:已知原數據和其MD5值,想找到一個具有相同MD5值的數據(即僞造數據)是非常困難的。
5、強抗碰撞:想找到兩個不同的數據,使它們具有相同的MD5值,是非常困難的。

根據以上的特定我們能總結出幾個根據以上特點衍生出來可以供我們使用的特性:

1.方便存儲:MD5加密出來都是32位的字符串,能夠給定固定大小的空間存儲,傳輸,驗證
2.文件加密:MD5運用在文件加密上很有優勢,因爲只需要32爲字符串就能對一個巨大的文件進行驗證完整性
3.不 可 逆:MD5加密出來只會截取末尾32位,具有良好的安全性,如果是對於參數加密很難僞造MD5
4.加密損耗低:MD5加密對於性能的消耗微乎其微(我獲得的結果是:0.001毫秒)

2.實際上需要如何應用呢

我在實踐中常常會用到的MD5校驗加密一般運用場景:用戶密碼,請求參數,文件校驗

2.1.用戶密碼

對於用戶密碼加密最高境界就是:別人獲得你數據庫的用戶資料別人也沒有辦法獲知密碼.要達到就要有一套複雜的加密規則,一般常用的規則比如:

MD5(MD5(用戶名+用戶密碼)+MD5(KEY+項目名+公司名)) 這樣可以避免和別人碰庫不排除別人用MD5來攻擊你的服務器來匹配.

當然還可以自定很多種加密方法,就算知道加密方法也幾乎無法去推送出用戶原密碼是什麼

2.2.請求參數校驗

都與服務器來言排除系統問題最大的問題就是害怕請求被攔截,攔截修改之後就有很多漏洞的可能性了

爲了避免被攔截,參數被修改這種文件的常用方法就是對請求參數進行校驗,就算攔截了請求參數修改了只要模擬不出MD5加密出來的值,在服務器過濾器直接就會進行攔截.

我這邊推薦的請求校驗方法在傳遞參數的時候帶上 MD5值 隨機數 時間戳 當然這幾個都是由客戶端生成 
MD5=MD5(隨機數+時間戳+MD5(KEY+公司名+項目名)) 當然這個規則也是可以定製的

請求參數在服務器攔截器就用客戶端傳遞過來的 隨機數 時間戳 來做校驗如果不通過就不讓繼續訪問(在這裏的隨機數 時間戳在後面的請求安全請求唯一性驗證中會起到很大的作用所以建議保留)

2.3文件校驗

當然對於一些圖片已經一些很小很小的文件來說可以不用MD5校驗應爲基本上都是一次請求就完成了上傳,而且顯示的時候也不需要驗證圖片完不整.

但是如果是遇到了大文件上傳MD5 就起到作用了,當然不是吧一個幾個G 的文件一次性上傳使用MD5校驗,這邊100%會失敗 就算傳遞到服務端了 這個時間是不能被接受的 ,而且服務器最好是對請求做好限制(以後會開一篇來單獨探討文件上傳的問題)

我們對於大文件上傳的處理方式是進行分片上傳,也就是所謂的斷點續傳,裏面的實現機制

如果有一個5MB的文件 客戶端把它分割成5份 1MB的文件 在上傳的時候 上傳兩個MD5值 一個是當前上傳的片1MB文件流的MD5 還有一個就是拼接之後的MD5(如果現在上傳的是第二片 這個MD5就應該是第一片加上第二片的MD5)通過這樣的方式能保證文件的完整性

當如果文件傳到一半斷了,用戶換了臺機器傳 通過驗證文件MD5 值就可以得知用戶已經傳到了第幾片 就可以告訴用戶從第幾片開始傳遞 就解決了這個問題

二、MD5的加密原理

MD5以512位分組來處理輸入的信息,且每一分組又被劃分爲16個32位子分組,經過了一系列的處理後,算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位散列值。

在MD5算法中,首先需要對信息進行填充,使其字節長度對512求餘數的結果等於448。因此,信息的字節長度(Bits Length)將被擴展至N*512+448,即N*64+56個字節(Bytes),N爲一個正整數。填充的方法如下,在信息的後面填充一個1和無數個0,直到滿足上面的條件時才停止用0對信息的填充。然後再在這個結果後面附加一個以64位二進制表示的填充前的信息長度。經過這兩步的處理,現在的信息字節長度=N*512+448+64=(N+1)*512,即長度恰好是512的整數倍數。這樣做的原因是爲滿足後面處理中對信息長度的要求。MD5中有四個32位被稱作鏈接變量(Chaining Variable)的整數參數,他們分別爲:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。 當設置好這四個鏈接變量後,就開始進入算法的四輪循環運算,循環的次數是信息中512位信息分組的數目。

將上面四個鏈接變量複製到另外四個變量中:A到a,B到b,C到c,D到d。 主循環有四輪(MD4只有三輪),每輪循環都很相似。第一輪進行16次操作。每次操作對a、b、c和d中的其中三個作一次非線性函數運算,然後將所得結果加上第四個變量(文本中的一個子分組和一個常數)。

再將所得結果向右環移一個不定的數,並加上a、b、c或d中之一。最後用該結果取代a、b、c或d中之一。 以一下是每次操作中用到的四個非線性函數(每輪一個)。

其中,?是異或,∧是與,∨是或, 是反符號。 

如果X、Y和Z的對應位是獨立和均勻的,那麼結果的每一位也應是獨立和均勻的。F是一個逐位運算的函數。即,如果X,那麼Y,否則Z。函數H是逐位奇偶操作符。所有這些完成之後,將A,B,C,D分別加上a,b,c,d。然後用下一分組數據繼續運行算法,最後的輸出是A,B,C和D的級聯。最後得到的A,B,C,D就是輸出結果,A是低位,D爲高位,DCBA組成128位輸出結果。

三、利用JAVA實現MD5加密

       在說MD5加密的實現之前,我們需要先學習一下MessageDigest類:

1.概述

MessageDigest 類是一個引擎類,它是爲了提供諸如 SHA1 或 MD5 等密碼上安全的報文摘要功能而設計的。密碼上安全的報文摘要可接受任意大小的輸入(一個字節數組),併產生固定大小的輸出,該輸出稱爲一個摘要或散列。摘要具有以下屬性:

  • 無法通過計算找到兩個散列成相同值的報文。
  • 摘要不反映任何與輸入有關的內容。

使用報文摘要可以生成數據唯一且可靠的標識符。有時它們被稱爲數據的“數字指紋”。

2.使用方法

(1).計算摘要的第一步是創建報文摘要實例。

     像所有的引擎類一樣,獲取某類報文摘要算法的 MessageDigest 對象的途徑是調用 MessageDigest 類中的 getInstance 靜態 factory 方法:

MessageDigest.getInstance("MD5") 

(2)更新報文摘要對象

計算數據的摘要的第二步是向已初始化的報文摘要對象提供數據。這將通過一次或多次調用以下某個 update(更新)方法來完成:

public void update(byte[] input, int offset, int len);

(3)計算摘要

通過調用 update 方法提供數據後,程序就調用以下某個digest(摘要)方法來計算摘要:

    public byte[] digest()
    public byte[] digest(byte[] input)
    public int digest(byte[] buf, int offset, int len)

前兩個方法返回計算出的摘要。後一個方法把計算出的摘要儲存在所提供的buf 緩衝區中,起點是 offsetlenbuf中分配給該摘要的字節數。該方法返回實際存儲在 buf中的字節數。

對接受輸入字節數組變量的 digest方法的調用等價於用指定的輸入調用:

    public void update(byte[] input)

,接着調用不帶參數的 digest 方法.




import com.sinosoft.ebusiness.nsp.service.util.InitAttributeInfos;

import java.security.MessageDigest;

/**
* Created by Tanyunlong on 2016/10/19.
*/
public class MD5Test {

    public static void main(String args[]){
         String result=getMD5("aaaabc!");
         System.err.println(result);
         System.out.println(result);
    }

        /**
        * 生成MD5
        * @return
        */
       public  static  String getMD5(String message){
           String md5Result="";
           try{
               //1.創建一個提供信息摘要算法的對象,初始化爲MD5算法對象
               MessageDigest md=MessageDigest.getInstance("MD5");
               //2.將消息變爲byte數組
               byte[] input=message.getBytes();
               //3.計算後獲得字節數組,128位長度的MD5加密
               byte[] buff=md.digest(input);
               //4.把數組每一個字節(一個字節佔8位)換成16進制的md5字符串
               md5Result=bytesHex(buff);

           }catch (Exception e){
               e.printStackTrace();
           }

           return md5Result;
      }

         public static String bytesHex(byte[]bytes){
             StringBuffer md5Result =new StringBuffer();
             //把數組每一字節換成換成16進制連成md5字符串
             int digital;
             for (int i=0;i<bytes.length;i++){


                 digital=bytes[i];
                 if (digital<0){
                     digital+=256;
                 }
                 if (digital<16){
                     md5Result.append("0");
                 }
                 md5Result.append(Integer.toHexString(digital));
             }
           return md5Result.toString().toUpperCase();
         }



}



import java.security.MessageDigest;

public class MD5Test {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     * 轉換字節數組爲16進制字串
     * @param b 字節數組
     * @return 16進制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }

    /**
     * 轉換byte到16進制
     * @param b 要轉換的byte
     * @return 16進制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * MD5編碼
     * @param origin 原始字符串
     * @return 經過MD5加密之後的結果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            //創建信息摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            //用明文字符串計算消息摘要
            md.update(resultString.getBytes("UTF-8"));
            //讀取消息摘要,並轉換爲16進制字符串
            resultString = byteArrayToHexString(md.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

}














發佈了51 篇原創文章 · 獲贊 45 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章