天天看點

PKI - 消息摘要算法 - MD、SHA、MAC - 校驗資料完整性1. MD52. SHA3. MAC4. 其他消息摘要算法5. 循環備援校驗算法 – CRC算法6. 擴充參考

PKI - 消息摘要算法 - MD、SHA、MAC - 校驗資料完整性

  • 1. MD5
    • 1.1 JDK實作
    • 1.2 Bouncy Castle實作
    • 1.3 Commons Codec
  • 2. SHA
    • 2.1 JDK實作
    • 2.2 Bouncy Castle實作
    • 2.3 Commons Codec
  • 3. MAC
    • 3.1 JDK
    • 3.2 Bouncy Castle
  • 4. 其他消息摘要算法
  • 5. 循環備援校驗算法 -- CRC算法
  • 6. 擴充
    • 6.1 “挑戰/響應”身份認證
  • 參考

消息摘要算法又稱雜湊演算法,其核心在于散列函數的單向性。即通過散列函數可獲得對應的散列值,但不可通過該散列值反推其原始資訊。這是消息摘要算法的安全性的根本所在。

消息摘要算法主要分為三大類:MD(

Message Digest

,消息摘要算法)、SHA(

Secure Hash Algorithm

,安全雜湊演算法)和MAC(

Message Authentication Code

,消息認證碼算法)。

MD系列算法包括MD2、MD4和

MD5

共3種算法;

SHA算法主要包括其代表算法

SHA1

和SHA1算法的變種SHA2系列算法(SHA224、

SHA256

、SHA384、

SHA512

)以及SHA3(SHA3-224、SHA3-256、SHA3-384、SHA3-512等);SHA-2的算法和SHA1是一樣的,差別在于其生成的摘要的長度;SHA-3是與SHA-2算法不同的,可替換的加密雜湊演算法。

MAC算法綜合了上述兩種算法,主要包括

HmacMD5

HmacSHA1

HmacSHA256

、HmacSHA384和

HmacSHA512

算法。

盡管上述内容列舉了各種消息摘要算法,但仍不能滿足應用需要。基于這些消息摘要算法,又衍生出了

RipeMD

系列(包含RipeMD128、RipeMD160、RipeMD256、RipeMD320)、Tiger、GOST3411和Whirlpool算法。

1. MD5

1.1 JDK實作

java.security 包提供 java.security.MessageDigest 類進行消息摘要處理,輸出位元組數組。

消息摘要的一個特點是每次輸出的摘要值都相同,下面的Test-Case對此進行了驗證。

public byte[] md5Jdk8(String data) throws Exception {
    return MessageDigest.getInstance("MD5").digest(data.getBytes("UTF-8"));
}

@Test
public void md5Jdk8Test() throws Exception {
    final byte[] md5_1 = md5Jdk8("我是阿湯哥");
    final byte[] md5_2 = md5Jdk8("我是阿湯哥");
    assertEquals(md5_1, md5_2);
}
           

1.2 Bouncy Castle實作

要使用BC實作需要添加provider,

Security.addProvider(new BouncyCastleProvider())

,同時可以将位元組數組轉換為16進制編碼的字元串進行輸出。

public String md5Bc(String data) throws Exception {
    // 加入Bouncy Castle Provider支援
    Security.addProvider(new BouncyCastleProvider());

    final byte[] md5s = MessageDigest.getInstance("MD5").digest(data.getBytes("UTF-8"));
    return new String(Hex.encode(md5s), "UTF-8");
}

@Test
public void md5BcTest() throws Exception {
    final String md5_1 = md5Bc("我是阿湯哥");
    final String md5_2 = md5Bc("我是阿湯哥");
    System.out.println("MD5 first  : " + md5_1);
    System.out.println("MD5 second : " + md5_2);
    assertEquals(md5_1, md5_2);
}
           

1.3 Commons Codec

org.apache.commons.codec.digest.DigestUtils

,雖然

DigestUtils

隻是對

MessageDigist

的簡單封裝,但為實際開發提供了不少便利。

public String md5Codec(String data) throws Exception {
    return DigestUtils.md5Hex(data);
}

@Test
public void md5CodecTest() throws Exception {
    final String md5_1 = md5Codec("我是阿湯哥");
    final String md5_2 = md5Codec("我是阿湯哥");
    System.out.println("MD5 first  : " + md5_1);
    System.out.println("MD5 second : " + md5_2);
    assertEquals(md5_1, md5_2);
}
           

輸出結果

MD5 first  : 15e13fd90ab9822cf0ea4e1d29301535
MD5 second : 15e13fd90ab9822cf0ea4e1d29301535
           

2. SHA

SHA(Secure Hash Algorithm,安全雜湊演算法)是消息摘要算法的一種,被廣泛認可為MD5算法的繼任者。它是由美國國家安全局(NSA,National Security Agency)設計,經美國國家标準與技術研究院(NIST,National Institute of Standards and Technology)釋出的一系列密碼散列函數。SHA算法家族目前共有SHA1、SHA-2(SHA224、SHA256、SHA384、SHA512)、SHA3(SHA3-224、SHA3-256、SHA3-384、SHA3-512等)。

SHA算法的實作方式與MD5相似,隻需要在參數指定對應的算法名稱即可。SHA為SHA-1的縮寫。

2.1 JDK實作

public byte[] sha256jdk(String data) throws Exception {
    return MessageDigest.getInstance("SHA-256").digest(data.getBytes("UTF-8"));
}

@Test
public void sha256jdkTest() throws Exception {
    final byte[] sha256_1 = sha256jdk("我是阿湯哥");
    final byte[] sha256_2 = sha256jdk("我是阿湯哥");
    assertEquals(sha256_1, sha256_2);
}
           

2.2 Bouncy Castle實作

public String sha256Bc(String data) throws Exception {
    Security.addProvider(new BouncyCastleProvider());

    final byte[] digest = MessageDigest.getInstance("SHA-256").digest(data.getBytes("UTF-8"));
    return new String(Hex.encode(digest), "UTF-8");
}

@Test
public void sha256BcTest() throws Exception {
    final String sha256_1 = sha256bc("我是阿湯哥");
    final String sha256_2 = sha256bc("我是阿湯哥");
    System.out.println("SHA256 first  : " + sha256_1);
    System.out.println("SHA256 second : " + sha256_2);
    assertEquals(sha256_1, sha256_2);
}
           

2.3 Commons Codec

public String sha256Codec(String data) throws Exception {
    return DigestUtils.sha256Hex(data);
}

@Test
public void sha256CodecTest() throws Exception {
    final String sha256_1 = sha256Codec("我是阿湯哥");
    final String sha256_2 = sha256Codec("我是阿湯哥");
    System.out.println("SHA256 first  : " + sha256_1);
    System.out.println("SHA256 second : " + sha256_2);
    assertEquals(sha256_1, sha256_2);
}
           

執行結果

SHA256 first  : 7a78507d8bf0387f52683974184252b8b41cedd6ca2373df8dec6e6e2ca219e9
SHA256 second : 7a78507d8bf0387f52683974184252b8b41cedd6ca2373df8dec6e6e2ca219e9
           

3. MAC

MAC(Message Authentication Code,消息認證碼算法)是含有密鑰散列函數算法,相容了MD和SHA算法的特性,并在此基礎上加入了密鑰。因為MAC算法融合了密鑰散列函數(keyed-Hash),通常我們也把MAC稱為HMAC(keyed-Hash Message Authentication Code)。MAC算法和HMAC算法基本上可等同對待。

MAC算法主要集合了MD和SHA兩大系列消息摘要算法。MD系列算法有HmacMD2、HmacMD4和HmacMD5三種算法;

SHA系列算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384和HmacSHA512五種算法。

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

MAC與MD和SHA的主要差別在于加入了密鑰,密鑰是雙方事先約定的,第三方不可能知道。

3.1 JDK

JDK7支援的算法包括HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和HmacSHA512。

生成密鑰

public byte[] generateKey() throws NoSuchAlgorithmException {
    // 初始化 key generator
    final KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
    // 生成密鑰
    final SecretKey secretKey = keyGenerator.generateKey();
    // 擷取密鑰
    return secretKey.getEncoded();
}
           

生成消息摘要

public byte[] getMessageDigist(byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
    // 還原密鑰
    final SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacMD5");
    // 執行個體化 Mac
    final Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm());
    // 初始化 Mac
    mac.init(secretKeySpec);
    // 生成消息摘要
    return mac.doFinal(message);
}
           

3.2 Bouncy Castle

BouncyCastle作為補充,提供了HmacMD2、HmacMD4和HmacSHA224三種算法支援。

此外BouncyCastle還支援消息摘要的十六進制編碼。

public byte[] generateKey() throws NoSuchAlgorithmException {
    // 添加BouncyCastleProvider支援
    Security.addProvider(new BouncyCastleProvider());
    // 初始化 key generator
    final KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD4");
    // 生成密鑰
    final SecretKey secretKey = keyGenerator.generateKey();
    // 擷取密鑰
    return secretKey.getEncoded();
}

public byte[] getMessageDigist(byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
    // 添加BouncyCastleProvider支援
    Security.addProvider(new BouncyCastleProvider());
    // 還原密鑰
    final SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacMD4");
    // 執行個體化 Mac
    final Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm());
    // 初始化 Mac
    mac.init(secretKeySpec);
    // 生成消息摘要
    return mac.doFinal(message);
}

@Test
public void testMac() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
    // 擷取密鑰
    final byte[] secretKey = generateKey();
    // 擷取消息摘要
    final byte[] md_1 = getMessageDigist(secretKey, "我是阿湯哥".getBytes("UTF-8"));
    final byte[] md_2 = getMessageDigist(secretKey, "我是阿湯哥".getBytes("UTF-8"));

    System.out.println(new String(Hex.encode(md_1), "UTF-8"));

    assertArrayEquals(md_1, md_2);
}
           

執行結果

9fd5f598eca3f4422c88a622b81e5e82
           

4. 其他消息摘要算法

除了MD、SHA和MAC這三大主流消息摘要算法外,還有一些我們不了解的消息摘要算法,包括RipeMD列、Tiger、Whirlpool和GOST3411算法。

RipeMD系列算法和MAC系列算法相結合,又産生了HmacRipeMD128和HmacRipeMD160兩種算法。

Bouncy Castle對RipeMD系列算法和HmacRipeMD系列算法進行了實作。

5. 循環備援校驗算法 – CRC算法

CRC(Cyclic Redundancy Check,循環備援校驗)是可以根據資料産生簡短固定位數的一種散列函數,主要用來檢測或校驗資料傳輸/儲存後出現的錯誤。生成的散列值在傳輸或儲存之前計算出來并且附加到資料後面。在使用資料之前,對資料的完整性做校驗。一般來說,循環備援校驗的值都是32位的二進制數,以8位十六進制字元串形式表示。它是一類重要的線性分組碼,編碼和解碼方法簡單,檢錯和糾錯能力強,在通信領域廣泛地用于實作差錯控制。

消息摘要算法與CRC算法同屬散列函數,并且CRC算法很可能就是消息摘要算法的前身。

在JDK中CRC算法是由java.util.zip.CRC32類實作的。

6. 擴充

6.1 “挑戰/響應”身份認證

HMAC的一個典型應用是用在“挑戰/響應”(Challenge/Response)身份認證中,認證流程如下:

(1)先由用戶端向伺服器發出一個驗證請求。

(2)伺服器接到此請求後生成一個随機數并通過網絡傳輸給用戶端(此為挑戰)。

(3)用戶端将收到的随機數提供給ePass,由ePass使用該随機數與存儲在ePass中的密鑰進行

HMAC-MD5

運算并得到一個結果作為認證證據傳給伺服器(此為響應)。

(4)與此同時,伺服器也使用該随機數與存儲在伺服器資料庫中的該客戶密鑰進行

HMAC-MD5

運算,如果伺服器的運算結果與用戶端傳回的響應結果相同,則認為用戶端是一個合法使用者。

參考

hmac

梁棟. Java加密與解密的藝術(第2版). 機械工業出版社. Kindle 版本.

繼續閱讀