天天看點

MD5 - 加密算法簡要介紹與JAVA實作

【1】MD5是什麼

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

該算法的檔案号為:

​RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)​

​。

MD5的作用是讓大容量資訊在用數字簽名軟體簽署私人密鑰前被"壓縮"成一種保密的格式(就是把一個任意長度的位元組串變換成一定長的十六進制數字串)。除了MD5以外,其中比較有名的還有sha-1、RIPEMD以及Haval等。

對MD5算法簡要的叙述可以為:MD5以512位分組來處理輸入的資訊,且每一分組又被劃分為16個32位子分組,經過了一系列的處理後,算法的輸出由四個32位分組組成,将這四個32位分組級聯後将生成一個128位散列值。

128位散列值轉換為16進制字元串為32個字元。即,加密後的結果為32位16進制的字元串。

經典的哈希(散列)算法還有:MD2、MD4 和 SHA-1(目的是将任意長輸入通過算法變為固定長輸出,且保證輸入變化一點輸出都不同,且不能反向解密)。

【2】MD5的特點

① 長度固定

不管多長的字元串,加密後長度都是一樣長,友善平時資訊的統計和管理。

② 容易計算

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

③ 抗修改性

對原資料進行任何改動,哪怕隻修改1個位元組,所得到的MD5值都有很大差別。

④ 強抗碰撞

已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即僞造資料)是非常困難的(并非不可以)。

【3】MD5加密過程

第一種,加密字元串

① 擷取資訊摘要對象:md5

通過資訊摘要單例的構造函數擷取:

MessageDigest md5 = MessageDigest.getInstance("MD5");      

② 擷取摘要位元組數組

兩種方式:

byte[] bytes = str.getBytes();
byte[] digest = md5.digest(bytes);

或

byte[] bytes = str.getBytes();
md5.update(bytes);
byte[] digest = md5.digest();      

③ 把摘要數組中的每一個位元組轉換成16進制,并拼在一起就得到了MD5值.

第二種,加密檔案

方法傳入的是檔案對象 : file

① 因為是檔案不是方法,是以不是像剛才那樣通過摘要擷取字元串。

② 使用到另一個方法即可:就是資訊摘要對象更新:md5.update(byte[] input)方法,用法是通過讀取流,不斷的更新從流中讀到的"資訊數組"。

③ 然後通過”資訊摘要對象”擷取摘要,不用參數:md5.digest(),此時傳回的數組就已經是包含内容的摘要數組了。

【4】MD5工具類

package com.web.encrypt;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


public class MD5Util {
  // 用來将位元組轉換成 16 進制表示的字元
  private static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd','e', 'f' };
  protected static MessageDigest messagedigest = null;
  
  static {
    try {
      messagedigest = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException nsaex) {
      System.err.println(MD5Util.class.getName()
          + "初始化失敗,MessageDigest不支援MD5Util。");
      nsaex.printStackTrace();
    }
  }
  
  // 加密字元串
  public static String getMD5(String str){
    
    byte[] bs = str.getBytes();
    
    return getMD5FromByte(bs);
  }
  
  private static String getMD5FromByte(byte[] source) {
    String s = null;
    
    try {
      java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
      md.update(source);
      byte tmp[] = messagedigest.digest();
      // MD5 的計算結果是一個 128 位的長整數(16個位元組),轉為16進制則為32個字元
      char str[] = new char[16 * 2]; 
  // 每個位元組用 16 進制表示的話,使用兩個字元(一個8位->2個4位->2個16進制字元),是以表示成 16 進制需要 32 個字元
      int k = 0; // 表示轉換結果中對應的字元位置
      for (int i = 0; i < 16; i++) { 
        // 從第一個位元組開始,對 MD5 的每一個位元組
        // 轉換成 16 進制字元的轉換
        byte byte0 = tmp[i]; // 取第 i 個位元組
        str[k++] = hexDigits[byte0 >>> 4 & 0xf];
         // 取位元組中高 4 位的數字轉換,>>>為邏輯右移,将符号位一起右移
        str[k++] = hexDigits[byte0 & 0xf];
         // 取位元組中低 4 位的數字轉換
      }
      s = new String(str); 
      // 換後的結果轉換為字元串

    } catch (Exception e) {
      e.printStackTrace();
    }
    return s;
  }
  
  // 加密檔案對象
  public static String getFileMD5String(File file) throws FileNotFoundException {  
    String value = null;  
    FileInputStream in = new FileInputStream(file);  
    try {  
      MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());  
      MessageDigest md5 = MessageDigest.getInstance("MD5");  
      md5.update(byteBuffer);  
      BigInteger bi = new BigInteger(1, md5.digest());  
      value = bi.toString(16);  
    } catch (Exception e) {  
      e.printStackTrace();  
    } finally {  
      if(null != in) {  
        try {  
          in.close();  
        } catch (IOException e) {  
          e.printStackTrace();  
        }  
      }  
    }  
    return value;  
  }  
  
  //參數為檔案路徑
  public static String getFileMD5String(String  filePath) throws FileNotFoundException {  
          String value = null;  
          File file=new File(filePath);
          FileInputStream in = new FileInputStream(file);  
      try {  
          MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());  
          MessageDigest md5 = MessageDigest.getInstance("MD5");  
          md5.update(byteBuffer);  
          BigInteger bi = new BigInteger(1, md5.digest());  
          value = bi.toString(16);  
      } catch (Exception e) {  
          e.printStackTrace();  
      } finally {  
              if(null != in) {  
                  try {  
                  in.close();  
              } catch (IOException e) {  
                  e.printStackTrace();  
              }  
          }  
      }  
         return value;  
      }  
      
  //參數為url
  public static String getFileMD5StringByURL(String urlString) throws IOException {   

    URL url = new URL(urlString);
    
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    
    BufferedInputStream fis = null;
    
    fis = new BufferedInputStream(connection.getInputStream());
    
    byte[] buffer = new byte[1024];
    int numRead = 0;
    while ((numRead = fis.read(buffer)) > 0) {
      
      messagedigest.update(buffer, 0, numRead);
    }
    fis.close();
    return bufferToHex(messagedigest.digest());
  }
  
  //參數為位元組數組
  public static String getMD5String(byte[] bytes) {
    messagedigest.update(bytes);
    return bufferToHex(messagedigest.digest());
  }
  
  // 将位元組數組轉換為16進制的字元串
  private static String bufferToHex(byte bytes[]) {
    int m = 0;
    int n = bytes.length;
    StringBuffer stringbuffer = new StringBuffer(2 * n);
    int k = m + n;
    for (int l = m; l < k; l++) {
      appendHexPair(bytes[l], stringbuffer);
    }
    return stringbuffer.toString();
  }

  private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
    char c0 = hexDigits[(bt & 0xf0) >> 4];
    // 取位元組中高 4 位的數字轉換, >>> 為邏輯右移,将符号位一起右移
    char c1 = hexDigits[bt & 0xf];
    // 取位元組中低 4 位的數字轉換 
    stringbuffer.append(c0);
    stringbuffer.append(c1);
  }
  
}