1. 程式人生 > >MD、SHA、MAC訊息摘要演算法實現與應用

MD、SHA、MAC訊息摘要演算法實現與應用

1.訊息摘要概述

訊息摘要(Message Digest)又稱為數字摘要(Digital Digest)。它是一個唯一對應一個訊息或文字的固定長度的值,它由一個單向Hash加密函式對訊息進行作用而產生。如果訊息在途中改變了,則接收者通過對收到訊息的新產生的摘要與原摘要比較,就可知道訊息是否被改變了。因此訊息摘要保證了訊息的完整性。

訊息摘要採用單向Hash函式將需加密的明文"摘要"成一串固定位數(如128bit)的密文,這一串密文亦稱為數字指紋(Finger Print),它有固定的長度,且不同的明文摘要成密文,其結果總是不同的,而同樣的明文其摘要必定一致。這樣這串摘要便可成為驗證明文是否是“真身”的“指紋”了。

訊息摘要具有不可逆性,在訊息摘要生成過程中,會丟失很多原文的資訊,而且無法找回。一個好的摘要演算法,是極難產生Hash碰撞的,也就是找到另一段明文經計算後產生相同的摘要。

2.訊息摘要演算法-MD2、MD4、MD5

MD是應用非常廣泛的一個演算法家族,尤其是 MD5(Message-Digest Algorithm 5,訊息摘要演算法版本5),它由MD2、MD3、MD4發展而來,由Ron Rivest(RSA公司)在1992年提出,目前被廣泛應用於資料完整性校驗、資料(訊息)摘要、資料加密等。MD2、MD4、MD5 都產生16位元組(128位)的校驗值,一般用32位十六進位制數表示。MD2的演算法較慢但相對安全,MD4速度很快,但安全性下降,MD5比MD4更安全、速度更快。

目前在網際網路上進行大檔案傳輸時,都要得用MD5演算法產生一個與檔案匹配的、儲存MD5值的文字檔案(字尾名為 .md5或.md5sum),這樣接收者在接收到檔案後,就可以利用與 SFV 類似的方法來檢查檔案完整性,目前絕大多數大型軟體公司或開源組織都是以這種方式來校驗資料完整性,而且部分作業系統也使用此演算法來對使用者密碼進行加密,另外,它也是目前計算機犯罪中資料取證的最常用演算法。與MD5 相關的工具有很多,如 WinMD5等。

MD演算法的實現

演算法 摘要長度 實現方
MD2 128 JDK
MD4 128 Bouncy Castle
MD5 128 JDK

在執行下面的所有Java程式之前,你需要引入Bouncy Castle和Commons Codec的依賴:

<dependency>
   <groupId>org.bouncycastle</groupId>
   <artifactId>bcprov-jdk15</artifactId>
   <version>1.46</version>
</dependency>
<dependency>
   <groupId>commons-codec</groupId>
   <artifactId>commons-codec</artifactId>
   <version>1.10</version>
</dependency>

Java程式碼實現:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.crypto.digests.MD4Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class MD5 {

  public static final String src = "md5 test";

  public static void main(String[] args) throws NoSuchAlgorithmException {
    jdkMD5();
    jdkMD2();
    ccMD5();
    ccMD2();
    bcMD5();
    bcMD4();
    bc2jdkMD4();
  }

  // 用jdk實現:MD5
  public static void jdkMD5() throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] md5Bytes = md.digest(src.getBytes());
    System.out.println("JDK MD5:" + bytesToHexString(md5Bytes));

  }

  // 用jdk實現:MD2
  public static void jdkMD2() throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("MD2");
    byte[] md2Bytes = md.digest(src.getBytes());
    System.out.println("JDK MD2:" + bytesToHexString(md2Bytes));
  }


  // 用bouncy castle實現:MD5
  public static void bcMD5() {
    MD5Digest digest = new MD5Digest();
    digest.update(src.getBytes(), 0, src.getBytes().length);
    byte[] md5Bytes = new byte[digest.getDigestSize()];
    digest.doFinal(md5Bytes, 0);
    System.out.println("bouncy castle MD5:" + bytesToHexString(md5Bytes));

  }


  // 用bouncy castle實現:MD4
  public static void bcMD4() {
    MD4Digest digest = new MD4Digest();
    digest.update(src.getBytes(), 0, src.getBytes().length);
    byte[] md4Bytes = new byte[digest.getDigestSize()];
    digest.doFinal(md4Bytes, 0);
    System.out.println("bouncy castle MD4:" + bytesToHexString(md4Bytes));
  }

  // 用bouncy castle與jdk結合實現:MD4
  public static void bc2jdkMD4() throws NoSuchAlgorithmException {
    Security.addProvider(new BouncyCastleProvider());
    MessageDigest md = MessageDigest.getInstance("MD4");
    byte[] md4Bytes = md.digest(src.getBytes());
    System.out.println("bc and JDK MD4:" + bytesToHexString(md4Bytes));
  }

  // 用common codes實現實現:MD5
  public static void ccMD5() {
    System.out.println("common codes MD5:" + DigestUtils.md5Hex(src.getBytes()));
  }

  // 用common codes實現實現:MD2
  public static void ccMD2() {
    System.out.println("common codes MD2:" + DigestUtils.md2Hex(src.getBytes()));
  }

  /**
   * byte[] 轉 16進位制
   */
  private static String bytesToHexString(byte[] src) {
    StringBuilder stringBuilder = new StringBuilder();
    if (src == null || src.length <= 0) {
      return null;
    }
    for (int i = 0; i < src.length; i++) {
      int v = src[i] & 0xFF;
      String hv = Integer.toHexString(v);
      if (hv.length() < 2) {
        stringBuilder.append(0);
      }
      stringBuilder.append(hv);
    }
    return stringBuilder.toString();
  }

}

MD演算法可用於密碼保護:

3.訊息摘要演算法-SHA

SHA(Secure Hash Algorithm)是由美國專門制定密碼演算法的標準機構——美國國家標準技術研究院(NIST)制定的,SHA系列演算法的摘要長度分別為:SHA-1為20位元組(160位)、SHA-224為32位元組(224位)、SHA-256為32位元組(256位)、SHA-384為48位元組(384位)、SHA-512為64位元組(512位),由於它產生的資料摘要的長度更長,因此更難以發生碰撞,因此也更為安全,它是未來資料摘要演算法的發展方向。由於SHA系列演算法的資料摘要長度較長,因此其運算速度與MD5相比,也相對較慢。

目前SHA1的應用較為廣泛,主要應用於CA和數字證書中,另外在目前網際網路中流行的BT軟體中,也是使用SHA1來進行檔案校驗的。

SHA演算法的實現

演算法 摘要長度 實現方
SHA-1 160 JDK
SHA-224 224 Bouncy Castle
SHA-256 256 JDK
SHA-384 384 JDK
SHA-512 512 JDK

Java程式碼實現:

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class SHA {

  public static final String src = "sha test";

  public static void main(String[] args)
      throws UnsupportedEncodingException, NoSuchAlgorithmException {
    jdkSHA1();
    bcSHA1();
    bcSHA224();
    bcSHA224b();
    generateSha256();
    ccSHA1();
  }

  // 用jdk實現:SHA1
  public static void jdkSHA1() throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA");
    md.update(src.getBytes());
    byte[] bytes = md.digest();
    //byte[]轉16進位制
    BigInteger bigInt = new BigInteger(1, bytes);
    System.out.println("jdk sha-1:" + bigInt.toString(16));
  }

  // 用jdk實現:SHA256
  public static void generateSha256()
      throws UnsupportedEncodingException, NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    md.update(src.getBytes("UTF-8")); // Change this to "UTF-16" if needed
    byte[] digest = md.digest();
    BigInteger bigInt = new BigInteger(1, digest);
    System.out.println("Sha256 hash: " + bigInt.toString(16));
  }

  // 用bouncy castle實現:SHA1
  public static void bcSHA1() {
    Digest digest = new SHA1Digest();
    digest.update(src.getBytes(), 0, src.getBytes().length);
    byte[] sha1Bytes = new byte[digest.getDigestSize()];
    digest.doFinal(sha1Bytes, 0);
    BigInteger bigInt = new BigInteger(1, sha1Bytes);
    System.out.println("bc sha-1:" + bigInt.toString(16));
  }


  // 用bouncy castle實現:SHA224
  public static void bcSHA224() {
    Digest digest = new SHA224Digest();
    digest.update(src.getBytes(), 0, src.getBytes().length);
    byte[] sha224Bytes = new byte[digest.getDigestSize()];
    digest.doFinal(sha224Bytes, 0);
    BigInteger bigInt = new BigInteger(1, sha224Bytes);
    System.out.println("bc sha-224:" + bigInt.toString(16));
  }

  // 用bouncy castle與jdk結合實現:SHA224
  public static void bcSHA224b() throws NoSuchAlgorithmException {
    Security.addProvider(new BouncyCastleProvider());
    MessageDigest md = MessageDigest.getInstance("SHA224");
    md.update(src.getBytes());
    BigInteger bigInt = new BigInteger(1, md.digest());
    System.out.println("bc and JDK sha-224:" + bigInt.toString(16));
  }

  // 用common codes實現實現:SHA1
  public static void ccSHA1() {
    System.out.println("common codes SHA1 - 1 :" + DigestUtils.sha1Hex(src.getBytes()));
    System.out.println("common codes SHA1 - 2 :" + DigestUtils.sha1Hex(src));
  }

}

訊息鑑別:

4.訊息摘要演算法-MAC

MAC演算法 (Message Authentication Codes訊息認證碼演算法) 含有金鑰的雜湊函式演算法,相容了MD和SHA演算法的特性,並在此基礎上加上了金鑰。因此MAC演算法也經常被稱作HMAC演算法。訊息的雜湊值由只有通訊雙方知道的金鑰來控制。此時Hash值稱作MAC。

經過MAC演算法得到的摘要值也可以使用十六進位制編碼表示,其摘要值得長度與實現演算法的摘要值長度相同。例如 HmacSHA演算法得到的摘要長度就是SHA1演算法得到的摘要長度,都是160位二進位制數,換算成十六進位制的編碼為40位。

流程分析:

甲乙雙方進行資料交換可以採取如下流程:

1.甲方向乙方公佈摘要演算法(就是指定要使用的摘要演算法名)

2.甲乙雙方按照約定構造金鑰,雙方擁有相同的金鑰(一般是一方構造金鑰後通知另外一方,此過程不需要通過程式實現,就是雙方約定個字串,但是這個字串可不是隨便設定的,也是通過相關演算法獲取的)

3.甲方使用金鑰對訊息做摘要處理,然後將訊息和生成的摘要訊息一同傳送給乙方

4.乙方收到訊息後,使用甲方已經公佈的摘要演算法+約定好的金鑰 對收到的訊息進行摘要處理。然後比對自己的摘要訊息和甲方發過來的摘要訊息。甄別訊息是否是甲方傳送過來的。

MAC演算法的實現:

演算法 摘要長度 備註
HmacMD5 128 JAVA6實現
HmacSHA1 160 JAVA6實現
HmacSHA256 256 JAVA6實現
HmacSHA384 384 JAVA6實現
HmacSHA512 512 JAVA6實現
HmacMD2 128 BouncyCastle實現
HmacMD4 128 BouncyCastle實現
HmacSHA224 224 BouncyCastle實現

Java程式碼實現:

import java.math.BigInteger;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;

public class HMAC {

  public static final String src = "hmac test";

  public static void main(String[] args) {
    jdkHmacMD5();
    bcHmacMD5();
  }

  // 用jdk實現:
  public static void jdkHmacMD5() {
    try {
      // 初始化KeyGenerator
      KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
      // 產生金鑰
      SecretKey secretKey = keyGenerator.generateKey();
      // 獲取金鑰
//          byte[] key = secretKey.getEncoded();
      byte[] key = Hex.decodeHex(new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e'});
      // 還原金鑰
      SecretKey restoreSecretKey = new SecretKeySpec(key, "HmacMD5");
      // 例項化MAC
      Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());
      // 初始化MAC
      mac.init(restoreSecretKey);
      // 執行摘要
      byte[] hmacMD5Bytes = mac.doFinal(src.getBytes());
      System.out.println("jdk hmacMD5:" + Hex.encodeHexString(hmacMD5Bytes));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // 用bouncy castle實現:
  public static void bcHmacMD5() {
    HMac hmac = new HMac(new MD5Digest());
    // 必須是16進位制的字元,長度必須是2的倍數
    hmac.init(new KeyParameter(org.bouncycastle.util.encoders.Hex.decode("123456789abcde")));
    hmac.update(src.getBytes(), 0, src.getBytes().length);
    // 執行摘要
    byte[] hmacMD5Bytes = new byte[hmac.getMacSize()];
    hmac.doFinal(hmacMD5Bytes, 0);
    BigInteger bigInteger = new BigInteger(1,hmacMD5Bytes);
    System.out.println("bc hmacMD5:" + bigInteger.toString(16));
  }

}

參考文章

常用訊息摘要演算法介紹

訊息摘要演算法-MAC系