1. 程式人生 > >Android常用加密手段之MD5加密(字串加密和檔案加密)

Android常用加密手段之MD5加密(字串加密和檔案加密)

from: http://blog.csdn.net/briblue/article/details/52984551

前言

安全問題一直伴隨著網際網路的成長,如何有效地保護應用程式的資料是每一個開發者都應該考慮和努力的事情。這篇文章介紹Android平臺上常用的加密方式之MD5加密。

MD5

MD5即Message-Digest Algorithm 5(資訊-摘要演算法5),用於確保資訊傳輸完整一致。是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法),主流程式語言普遍已有MD5實現。將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,MD5的前身有MD2、MD3和MD4。

MD5演算法具有以下特點:

1、壓縮性:任意長度的資料,算出的MD5值長度都是固定的。

2、容易計算:從原資料計算出MD5值很容易。

3、抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。

4、強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。

MD5的作用是讓大容量資訊在用數字簽名軟體簽署私人金鑰前被”壓縮”成一種保密的格式(就是把一個任意長度的位元組串變換成一定長的十六進位制數字串)。

以上是百度百科上的簡介。

簡單來說,MD5演算法是固定的,比如同一個數字它的加密結果是固定的,並且它理論上是不可逆的。(不過,現在已經可以被破解了,更多資訊請自行上網搜尋)

MD5常見用途

  • 登陸密碼保護時將密碼進行MD5加密再上傳到資料庫,可以防止被密碼被劫持破解。如密碼是123456,如果明文上傳,被人獲取後能輕易盜取賬號,如果用md5加密後,它變成”49ba59abbe56e057”,這樣即使被劫持,也難以將這串字元反譯成123456
  • 檢驗檔案完整性 網路傳輸檔案時,受到網路環境的影響,有時會發生檔案傳輸不完整的現象。這個時候常見的方法就是用md5校驗碼。如果兩個檔案的md5一樣,那麼檔案就下載完整了,如果不一樣說明下載不完成。

Android平臺上MD5程式碼編寫

加密字串

在Android編寫MD加密程式碼示例如下:

public static String md5
(String content) { byte[] hash; try { hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("NoSuchAlgorithmException",e); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UnsupportedEncodingException", e); } StringBuilder hex = new StringBuilder(hash.length * 2); for (byte b : hash) { if ((b & 0xFF) < 0x10){ hex.append("0"); } hex.append(Integer.toHexString(b & 0xFF)); } return hex.toString(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

程式碼很簡單,通過MessageDigest.getInstance("MD5")得到一個MessageDigest物件,這個類是Java自帶的一個加密類。然後通過呼叫.digest(byte[])得到了加密後的位元組陣列。 
得到加密後的位元組陣列後,我們通常要把它們轉換成16進制式的字串。 
值得注意的是,在16進制中數字的正確表達應該是0x0f這種。一個數字如果超過了15就要進位,超過255就會溢位。比如十進位制中17用16進製表示就是0x11。所以加密後的位元組陣列中,每個byte構成一個16進位制的數,而這個16進位制數需要兩個char來表示。高位在前,低位在後。比如:

//byte[] result = byte[]{13,14,25,09}
//result[0]為0x0d,result[2]為0x19
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

所以很容易得到網上的轉換十進位制到十六進位制的方法:

private static String convertByteArrayToHex(byte[] byteArray){
        // 首先初始化一個字元陣列,用來存放每個16進位制字元
        char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };

        // new一個字元陣列,這個就是用來組成結果字串的(解釋一下:一個byte是八位二進位制,也就是2位十六進位制字元(2的8次方等於16的2次方))
        char[] resultCharArray =new char[byteArray.length * 2];

        // 遍歷位元組陣列,通過位運算(位運算效率高),轉換成字元放到字元陣列中去
        int index = 0;
        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b>>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b& 0xf];
        }

        // 字元陣列組合成字串返回
        return new String(resultCharArray);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

當然,本文的例子中轉換16進位制的效果與上面也是一樣的

StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10){
            //避免出現0x5這樣的數字,應該是0x05
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

演示效果: 
字串加密

加密檔案

我之前在專案開發中為了驗證檔案下載的完整性,想到了用md5去校驗。思路還是將檔案轉換成byte陣列,然後再進行Md5轉碼。可有時候,Android應用直接就崩掉了,原因是記憶體溢位。想想也是,如果一個檔案太大了,比如512M,那麼用byte陣列來表示檔案肯定就不合適。直接讀到記憶體肯定崩掉。於是我想到在java中讀取檔案可以用流的形式,那麼md5中有沒有類似的流處理呢?答案是肯定的,這個類就是DigestInputStream.示例程式碼如下:

public static String md5ForFile(File file){
        int buffersize = 1024;
        FileInputStream fis = null;
        DigestInputStream dis = null;

        try {
            //建立MD5轉換器和檔案流
            MessageDigest messageDigest =MessageDigest.getInstance("MD5");
            fis = new FileInputStream(file);
            dis = new DigestInputStream(fis,messageDigest);

            byte[] buffer = new byte[buffersize];
            //DigestInputStream實際上在流處理檔案時就在內部就進行了一定的處理
            while (dis.read(buffer) > 0);

            //通過DigestInputStream物件得到一個最終的MessageDigest物件。
            messageDigest = dis.getMessageDigest();

            // 通過messageDigest拿到結果,也是位元組陣列,包含16個元素
            byte[] array = messageDigest.digest();
            // 同樣,把位元組陣列轉換成字串
            StringBuilder hex = new StringBuilder(array.length * 2);
            for (byte b : array) {
                if ((b & 0xFF) < 0x10){
                    hex.append("0");
                }
                hex.append(Integer.toHexString(b & 0xFF));
            }
            return hex.toString();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

但有點需要注意的是,md5加密檔案可能要耗時很久,所以在Android平臺上開發最好是非同步進行。我demo例子加密了一個188M大小的視訊大概用了2秒。 
演示效果: 
檔案加密

引用