在開發過程中經常會遇到 MD5、SHA1、SHA256 等詞語,這些是加密演算法嗎?嚴格意義上講,這些並不是加密演算法,而是消息摘要演算法。咱就用人聽得懂的話來聊聊“消息摘要”。 ...
Java 密碼技術 - 02- 消息摘要(數字摘要)
在開發過程中經常會遇到 MD5、SHA1、SHA256 等詞語,這些是加密演算法嗎?嚴格意義上講,這些並不是加密演算法,而是消息摘要演算法。咱就用人聽得懂的話來聊聊“消息摘要”。
1 消息摘要簡介
在現實生活中,我們每個人都有指紋,指紋在一定意義是警方破案的有力證據之一。每個人的指紋與生俱來,李四無法盜用張三的指紋,張三也沒法篡改自己的指紋。
消息摘要也是如此。消息 —— 無論是任何類型的消息,在電腦中本質上就是數據,一堆二進位 bit 組成的數據,這些數據按照某種規則計算以後的結果是固定的。如果數據發生改變,則計算出來的結果就會變化,這個計算出來的結果就是消息摘要。
張三給李四發送一條消息,並且攜帶這條消息的消息摘要。李四收到消息時,按照相同的計算規則計算得到計算結果,將這個計算結果與張三發送消息時攜帶的消息摘要進行對比。如果在消息傳輸過程中,消息被劫持者篡改了內容,那麼計算結果與攜帶的消息摘要的值便不相等,從而可以判斷出消息被篡改了。
所以消息摘要,是一種驗證數據完整性的演算法,是對付信息面臨被篡改威脅的策略之一。
1.1 消息摘要的概念
消息摘要:Message Digest,又稱為數字摘要(Digital Digest),要高大上還可以將其叫為:單向散列函數(one-way hash function)、哈希函數、雜湊函數等。消息摘要是一個唯一對應一個消息的固定長度的值,由一個單向哈希加密函數對消息進行計算而得到。通俗點說,無論是什麼消息、無論什麼時間、什麼地點,只要採用同樣的計算規則(演算法),得到的結果都是一樣的;並且無論消息長還是短,同一個演算法計算得到的長度都是固定的。
1.2 消息摘要的特點
從上面的描述可以看出消息摘要的特點:
1)只要消息不同,對其摘要後產生的結果也不同;
2)相同的消息一定會得到相同的結果;
3)無論消息的長短,計算出來的消息摘要長度是固定的;例如:
MD5 摘要後的長度為 128 個bit;
SHA-1 摘要後的長度為 160 個bit;
4)消息摘要演算法是單向的,不可逆。
1.3 常見的消息摘要演算法
常見的消息摘要演算法有: MD5、SHA-1、SHA-256、SHA-512,其他還有 MD4、SHA-2、SHA-224、SHA-384 等。
2 Java 實現
2.1 MessageDigest 類
Java 中提供了 java.security.MessageDigest 類來實現數字摘要:
- 首先通過 getInstance 獲取實例,入參為摘要演算法,即 MD5、SHA-1 之類的;
- 調用實例的 digest 方法獲取摘要(結果為byte[]);
- 由於摘要後的結果為 byte[],不方便閱讀,可封裝一個十六進位的工具類:
public class HexUtils {
/**
* 十六進位字元串轉 byte[]
*/
public static byte[] toBytes(String hex) {
if (hex == null || hex.length() < 1) {
return null;
} else {
byte[] result = new byte[hex.length() / 2];
int j = 0;
for (int i = 0; i < hex.length(); i += 2) {
result[j++] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
}
return result;
}
}
/**
* byte[] 轉十六進位字元串
*/
public static String toHex(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder("");
if (bytes == null || bytes.length <= 0) {
return null;
}
for (byte b : bytes) {
int v = b & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
}
十六進位中一個字元對應 4 個bit,故轉換後的十六進位字元串長度:
- MD5:128 bit,十六進位為 32 個字元;
- SHA-1:160 bit,十六進位為 40 個字元;
- SHA-256:256 bit,十六進位為 64 個字元;
- SHA-512:512 bit,十六進位為 128個字元;
2.2 Java Demo
package com.yygnb.demo.crypto;
import com.yygnb.demo.utils.HexUtils;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class MessageDigestTest {
private String md(String input, String algorithm) throws Exception {
MessageDigest md = MessageDigest.getInstance(algorithm);
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
return HexUtils.toHex(digest);
}
@Test
public void testMessageDigest() throws Exception {
String input = "Hello,張三";
System.out.println(md(input, "MD5"));
System.out.println(md(input, "SHA-1"));
System.out.println(md(input, "SHA-256"));
System.out.println(md(input, "SHA-512"));
}
}
執行後的結果如下:
5e7a42e5da82f1c0a6557e6c29d651dc
c49a06ded3b4ae492741428779eb1ed04889500d
a5a9d6d78806e179d917a1c557839fcdb68e11527a619179efd49e53c8acd6fb
2eb3400b3999bfa6db1310cfafc8cbe3ab6d4e48d1e428c4db2627f5605a71973a546f5bb711b0189583d2fcf6f9d5cef83896657f4651a13975498e591db950
大家在自己電腦上運行的結果應該也是一樣的。
3 JS 實現
JavaScript 有很多開源庫支持消息摘要,如 md5.js、sha.js、jsencrypt 等,考慮到後面的文章要寫對稱加密、非對稱加密,這裡採用 crypto-js。crypto-js 提供了各種摘要演算法、加解密演算法的實現。
3.1 安裝依賴
安裝依賴:
yarn add crypto-js
由於我使用的是 TypeScript 語言,所以還需要安裝 crypto-js 的 TS 類型:
yarn add @types/crypto-js -D
3.2 引入函數
從 crypto-js 中引入需要使用到的函數,如 md5、sha1、hex 等,其中 hex 提供了 stringify 方法,將摘要結果轉為十六進位字元串。
import md5 from 'crypto-js/md5'
import sha1 from 'crypto-js/sha1'
import sha256 from 'crypto-js/sha256'
import sha512 from 'crypto-js/sha512'
import hex from 'crypto-js/enc-hex'
3.3 調用函數
const input = 'Hello,張三'
const s1 = hex.stringify(md5(input))
const s2 = hex.stringify(sha1(input))
const s3 = hex.stringify(sha256(input))
const s4 = hex.stringify(sha512(input))
console.log(s1)
console.log(s2)
console.log(s3)
console.log(s4)
由於被摘要的消息與上面 Java 的一樣,所以這裡 JS 摘要後的結果與上面 Java 的結果完全一致:
感謝你閱讀本文,如果本文給了你一點點幫助或者啟發,還請三連支持一下,點贊、關註、收藏,作者會持續與大家分享更多乾貨