Java常用加密校驗轉換算法彙總
作者:邵發
本文是Java學習指南系列教程的官方配套文檔。內容介紹Java裏的常用算法的使用,包含轉換Hex / Base64等數據轉換算法,CRC32 / MD5 / HMAC-SHA1數據簽名和校驗算法,DES / AES等對稱加密算法。
由於篇幅的限制,本文只對各種算法的使用給出代碼示例。如果對原理有不明白的地方,可以參考Java學習指南系列的《項目應用篇》,裏面對每一個算法的應用背景和原理有較爲詳細的解釋。本文附帶項目源碼及相關JAR包。
1. Hex轉換
在Java裏,一般性的數據用byte[]表示,即字節數組。由於byte[]本身不可顯示,所以爲了顯示和傳遞的方便,有時候需要把它轉化爲十六進制顯示。
1.1 byte[] => Hex String
例如,
byte[] code = { -5, 0, 10, 99, 20 };
String hex = Hex.encodeHexString(code, false);
code表示5個字節的數據,將其轉成十六進制表字符串爲:FB000A6314 。在十六進制表示下,每兩個字符表示1字節的數據,即FB 00 0A 64 14共五個字節。
1.2 Hex String => byte[]
反轉來,也可以
byte[] code2 = Hex.decodeHex(hex);
解碼後得到長度爲5字節的數組code2,和原始數據內容是一樣的。
Hex是一種數據轉換算法,它沒有改變數據的值,僅僅是換了一個表示方式。就好比機器貓和多萊夢,說的是同一個事物。
2. Base64轉換
Base64,即64進制的轉換。在64進制下,一共有64個基礎字符。即,
'A', 'B', 'C', …, 'Y', 'Z',
'a', 'b', 'c', …, 'y', 'z',
'0', '1', '2', …, '8', '9',
'+', '-'
2.1 byte[] => Base64 String
以下示例,把4字節的數據轉成Base64的表示,例如。
byte[] code = { 12, 4 , 9, -2};
String str = Base64.encodeBase64String(code);
轉換的結果爲: DAQJ/g== ,雖然結果看起來有點奇怪,但這只是一個表示方式,數據的內容沒有發生變化。
2.2 Base64 String => byte[]
反過來,也可以把Base64表示的字符串,再解碼爲字節數據。例如。
byte[] code2 = Base64.decodeBase64(str);
解碼之後,得到的字節數組和原始內容是一致的。
3. MD5校驗
業界用於校驗的算法有多種,比如:
Parity : 奇偶校驗
ECC : 錯誤校驗與糾正
Checksum : 和校驗
CRC32 : 循環冗餘校驗
MD5 : 消息摘要
SHA1 : 安全哈希
其中,MD5和SHA1是Java項目裏使用比較多的校驗算法。
所謂校驗算法,用於在傳輸過程中的完整性的校驗。比如,A給B發送一個大文件,B在接收到之後惴惴不安,因爲他不能確定這麼大一個文件在傳輸過程中沒有錯誤發生(比如某個字節傳錯了)。B怎麼才能夠確認這個文件的完整性呢?需採用校驗算法。
MD5的基本使用公式: Code = MD5 ( Data)
其中 Data爲輸入的一段數據,經過MD5運算之後,得到16字節的結果,稱爲MD5碼。
特點:
1 無論Data有多長,輸出的MD5碼總是16字節
2 如果Data不同,則輸出的MD5碼也不相同。(極大概率不同)
3.1 示例:求MD5校驗碼
// 待處理的數據,任意長度
byte[] data = { 12, 8, 9, -1, 87, 0, -47, 99, 14, 54 };
// 創建算法實例 ,MD5 或 SHA1
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(data);
byte[] code = md.digest();
// 轉成HEX顯示 (參考上一章)
String result = Hex.encodeHexString(code, false);
需要注意的是,MD5算法的輸出結果是一個長爲16字節的byte[],通常需要轉成十六進制的字符串顯示。所以我們通常看到的MD5值是由32字符表示的。
3.2 對字符串求MD5
在Java項目中,通常需要對字符串求MD5碼。示例如下。
// 待處理的字符串
String text = "阿發你好 /afanihao.cn";
// 轉成字節數據
byte[] data = text.getBytes("UTF-8");
// 求消息摘要
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(data);
byte[] code = md.digest();
// 轉成HEX顯示
String result = Hex.encodeHexString(code, false);
System.out.println("校驗碼: " + result);
使用MD5算法,也可以對一個很大的文件求MD5碼。示例代碼略。
4. SHA1校驗
SHA1也是一種校驗算法,比MD5更強,輸出20字節的校驗碼。它的使用方法和MD5一樣,只需要把算法的名字改成SHA1就行了。
byte[] data = { 12, 8, 9, -1, 87, 0, -47, 99, 14, 54 };
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(data);
byte[] code = md.digest();
String result = Hex.encodeHexString(code, false);
5. CRC32校驗
CRC32也是一種校驗算法,生成4字節的校驗碼。它在Java領域使用得不多,稍微瞭解一下即可。
5.1 示例:根據byte[]生成CRC32校驗碼
// 待處理的數據,任意長度
byte[] data = { 12, 8, 9, -1, 87, 0, -47, 99, 14, 54 };
// 創建算法實例
CRC32 crc = new CRC32();
crc.update( data );
long value = crc.getValue();
// 轉成HEX顯示 (參考上一章)
String result = Long.toHexString(value);
System.out.println("校驗碼: " + result);
6. HMAC-SHA1簽名
所謂簽名,也是一種校驗算法,只是這個校驗碼是無法僞造的。簽名算法用於保證數據的真實現性。
例如,
Code = HMAC-SHA1 ( Data, Key )
其中,Data爲輸入的數據,Key爲指定的密鑰。在經過HMAC-SHA1運算後,得到一個20字節的校驗碼。
這個校驗碼是無法僞造的,只有持有Key的人,才能生成相同的校驗碼。在傳遞的過程,傳輸雙方都知曉這個密鑰,才能得到相同的校驗碼。
6.1 示例:HMAC-SHA1簽名
// 數據
byte[] data = { 12, 8, 9, -1, 87, 0, -47, 99, 14, 54 };
// 密鑰
String key = "test2019";
// HMAC-SHA1 算法
Key keySpec = new SecretKeySpec(key.getBytes(), "HMACSHA1");
Mac mac = Mac.getInstance("HMACSHA1");
mac.init(keySpec);
// 更新數據
mac.update(data);
// 取得最終結果
byte[] code = mac.doFinal();
String result = Hex.encodeHexString(code, false);
System.out.println("簽名: " + result);
6.2 HmacSha1Util 工具類
在Java項目裏,常見對String的簽名操作,爲此封裝出一個工具類HmacSha1Util。示例用法如下:
String text = "afanihao.cn | 阿發你好 | Java學習指南";
String key = "your_key_2019";
String result = HmacSha1Util(text , key);
7. DES加密
下面介紹兩種常見的加密算法:DES和AES。其中,DES出現的較早,而AES是它的升級替代品。
什麼叫加密?加密意味着祕密,只有持有祕鑰者可以解讀。
(1) 加密後的數據可以解密,還原爲原始數據
(2) 只有持有祕鑰者可以解密
加密算法的一般公式爲:
Cipher = Encrypt( Plain, Key )
其中,Plain爲原文,Key爲密鑰。經加密算法運算後,得到密鑰。
反過來,還可以解密,公式爲:
Plain = Decrypt ( Cipher, Key)
這個加密和加密的過程是對稱的(互逆的),來回使用的是同一處密鑰,所以稱爲對稱加密算法。
7.1 示例:DES加密
public static byte[] encrypt(byte[] plain, byte[] key) throws Exception
{
// 使用 PKCS5補齊方式
Cipher c = Cipher.getInstance("DES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "DES");
c.init(Cipher.ENCRYPT_MODE, keySpec);
// 加密
byte [] output = c.doFinal(plain);
return output;
}
其中,plain爲原文,key是密角,返回值爲密文。
在Java項目中,密鑰通常爲String,需要自己轉成byte[]。對於DES來說,要求密鑰長度爲8個字節。
7.2 示例:DES解密
public static byte[] decrypt(byte[] cipher, byte[] key) throws Exception
{
// 使用 PKCS5補齊方式
Cipher c = Cipher.getInstance("DES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "DES");
c.init(Cipher.DECRYPT_MODE, keySpec);
byte [] output = c.doFinal(cipher);
return output;
}
同樣地,在實際項目中處理的往往是String,而結果也往往要轉成HEX形式表示。所以要自己添加額外的處理,自己要稍微封裝一下再使用。
8. AES加密
AES是用於取代DES的更高級更強的加密算法,但用法基本是類同的。AES加密密鑰長度爲16字節,每16字節一組進行加密。
8.1 示例:AES加密
public static byte[] encrypt(byte[] plain, byte[] key) throws Exception
{
// AES
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, keySpec);
// 加密
byte[] output = c.doFinal(plain);
return output;
}
8.2 示例:AES解密
public static byte[] decrypt(byte[] cipher, byte[] key) throws Exception
{
// AES
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
c.init(Cipher.DECRYPT_MODE, keySpec);
byte[] output = c.doFinal(cipher);
return output;
}
其中,在準備密鑰的時候,需保證密鑰長度不少於16字節。
8.3 AESUtil工具類
由於在Java項目中要處理的信息一般是String,所以對上述AES算法做一個簡單的封裝,得到AESUtil工具類。
示例用法如下:
String key = "your_key_2910";
String plain = "阿發你好 | afanihao.cn | Java學習指南系列教程 ";
String cipher = AESUtil.encrypt(plain, key); // 加密
String plain2 = AESUtil.decrypt(cipher, key); // 解密
這樣在項目中調用就非常的簡單了,只需要自己指定一下密鑰,就可以進行加密解密了。
以上介紹了幾種常見算法的使用,如果有對應用背景、原理和項目使用不太清楚的地方,可以參考Java學習指南系列教程的《項目應用篇》。 本篇演示所用的項目源碼和JAR包在此處可以獲取。