文章目錄
消息摘要(Message Digest)又稱為數字摘要(Digital Digest)。它是一個唯一對應一個消息或文本的固定長度的值,它由一個單向Hash加密函數對消息進行作用而產生。如果消息在途中改變了,則接收者通過對收到消息的新產生的摘要與原摘要比較,就可知道消息是否被改變了。因此消息摘要保證了消息的完整性。
消息摘要采用單向Hash函數將需加密的明文"摘要"成一串固定位數(如128bit)的密文,這一串密文亦稱為數字指紋(Finger Print),它有固定的長度,且不同的明文摘要成密文,其結果總是不同的,而同樣的明文其摘要必定一致。這樣這串摘要便可成為驗證明文是否是“真身”的“指紋”了。
消息摘要具有不可逆性,在消息摘要生成過程中,會丟失很多原文的信息,而且無法找回。一個好的摘要算法,是極難產生Hash碰撞的,也就是找到另一段明文經計算后產生相同的摘要。
相關文章:
- https://www.cnblogs.com/yueshutong/p/11275420.html
- https://www.iteye.com/blog/snowolf-379860
有以下幾種:
1、MD5 (Message Digest algorithm 5 消息摘要算法版本5)
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等。
案例實現:
/** * MD5 測試 * <br/> * 參考:https://www.jianshu.com/p/be000cf837b2 */
class Md5Test {
public static void main(String[] args) throws NoSuchAlgorithmException {
/* 得注意加密后字符串的大小寫 */
// 待加密字符
String originalStr = "111111";
System.out.println(String.format("待加密字符: %s", originalStr));
// 已加密字符
String alreadyDigestStr = "96E79218965EB72C92A549DD5A330112";
System.out.println(String.format("已加密字符: %s", alreadyDigestStr));
/* jdk 實現 */
System.out.println("--------------jdk 實現-----------------");
// 獲取信息摘要對象
MessageDigest md5 = MessageDigest.getInstance("MD5");
// 完成摘要
byte[] digest = md5.digest(originalStr.getBytes());
// 將摘要轉換成 16 進制字符 (大寫)
String javaMd5Str = DatatypeConverter.printHexBinary(digest);
System.out.println(String.format("%s 加密結果:%s", originalStr, javaMd5Str));
// 匹配驗證
System.out.println(String.format("驗證結果:%b", Objects.equals(javaMd5Str, alreadyDigestStr)));
/* Apache commons-codec 實現 */
System.out.println("---------------Apache commons-codec 實現------------");
// 小寫
String apacheMd5Str = DigestUtils.md5Hex(originalStr.getBytes()).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, apacheMd5Str));
System.out.println(String.format("驗證結果:%b", Objects.equals(apacheMd5Str, alreadyDigestStr)));
/* spring提供 */
System.out.println("-----------spring提供-----------");
// 小寫
String springMd5Str = org.springframework.util.DigestUtils.md5DigestAsHex(originalStr.getBytes()).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, springMd5Str));
System.out.println(String.format("驗證結果:%b", Objects.equals(springMd5Str, alreadyDigestStr)));
}
}
運行效果如下:
MD算法可用於密碼保護。在用戶注冊時,利用MD算法取密碼的摘要值,存入數據庫。在用戶登錄時,將用戶密碼再次消息摘要后,與數據庫密碼進行比較即可得出用戶登錄結果。流程圖如下:
MD5加密,是屬於不可逆的。我們知道正常使用MD5加密技術,同一字符,加密后的16進制數是不變的,自從出現彩虹表,對於公司內部員工來說,可以反查數據,獲取不可能的權限,所以出現了salt算法。
md5加鹽算法,代碼如下:
package com.blog.www.util.coder;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Objects;
import java.util.Random;
/** * MD5 加隨機鹽加密 * <br/> * 參考: <a href='https://www.cnblogs.com/peaceliu/p/7825706.html'>MD5加密+加鹽</a> * <br/> * 基於:{@link DigestUtils} * <p> * 創建人:leigq <br> * 創建時間:2018-12-04 15:42 <br> * <p> * 修改人: <br> * 修改時間: <br> * 修改備注: <br> * </p> */
public class MD5WithSalt {
/** * 生成含有隨機鹽的加密字符串 * <br>創建人: leigq * <br>創建時間: 2018-12-04 15:35 * <br> * * @param data 待加密的字符 * @return 加密后的字符(含 16 位隨機鹽),大寫 */
public static String encrypt(String data) {
Random r = new Random();
StringBuilder sb = new StringBuilder(16);
sb.append(r.nextInt(99999999))
.append(r.nextInt(99999999));
int len = sb.length();
if (len < 16) {
for (int i = 0; i < 16 - len; i++) {
sb.append("0");
}
}
String salt = sb.toString();
String md5WithSaltStr = DigestUtils.md5Hex(data + salt);
char[] cs = new char[48];
for (int i = 0; i < 48; i += 3) {
cs[i] = md5WithSaltStr.charAt(i / 3 * 2);
char c = salt.charAt(i / 3);
cs[i + 1] = c;
cs[i + 2] = md5WithSaltStr.charAt(i / 3 * 2 + 1);
}
return new String(cs).toUpperCase();
}
/** * 校驗密碼是否正確 * <br>創建人: leigq * <br>創建時間: 2018-12-04 15:38 * <br> * * @param data 待驗證的字符(明文) * @param md5StrContainRandomSalt 加密后的字符(含 16 位隨機鹽) * @return 驗證結果 */
public static boolean verify(String data, String md5StrContainRandomSalt) {
// 32 位加密字符(不含鹽)
char[] cs1 = new char[32];
// 16 位的隨機鹽
char[] cs2 = new char[16];
for (int i = 0; i < 48; i += 3) {
cs1[i / 3 * 2] = md5StrContainRandomSalt.charAt(i);
cs1[i / 3 * 2 + 1] = md5StrContainRandomSalt.charAt(i + 2);
cs2[i / 3] = md5StrContainRandomSalt.charAt(i + 1);
}
String salt = new String(cs2);
return Objects.equals(DigestUtils.md5Hex(data + salt).toUpperCase(), new String(cs1));
}
}
測試:
/** * md5 加隨機鹽測試 */
class MD5WithSaltTest {
public static void main(String[] args) {
// 待加密字符
String originalStr = "111111";
// 已加鹽加密字符
String md5WithSaltStr = MD5WithSalt.encrypt(originalStr);
System.out.println(String.format("%s 加密結果:%s", originalStr, md5WithSaltStr));
// 不能直接再次加密驗證,因為使用了隨機鹽,每次加密出來的字符串都不同
boolean verifyResult = MD5WithSalt.verify(originalStr, md5WithSaltStr);
System.out.println(String.format("驗證結果:%b", verifyResult));
}
}
測試結果如下:
2、SHA (Secure Hash Algorithm 安全散列算法)
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位),由於它產生的數據摘要的長度更長,因此更難以發生碰撞,因此較之MD5更為安全,它是未來數據摘要算法的發展方向。由於SHA系列算法的數據摘要長度較長,因此其運算速度與MD5相比,也相對較慢。
目前SHA1的應用較為廣泛,主要應用於CA和數字證書中,另外在目前互聯網中流行的BT軟件中,也是使用SHA1來進行文件校驗的。
代碼實現:
/** * SHA 加密算法測試 */
class SHATest {
public static void main(String[] args) throws NoSuchAlgorithmException {
// 待加密字符
String originalStr = "111111";
System.out.println(String.format("待加密字符: %s", originalStr));
// 已加密字符
String alreadyDigestStr = "3D4F2BF07DC1BE38B20CD6E46949A1071F9D0E3D";
System.out.println(String.format("已加密字符: %s", alreadyDigestStr));
/* jdk 實現 */
// 獲取信息摘要對象 (SHA1),另外還有 SHA-224、SHA-256、SHA-384、SHA-512
MessageDigest md5 = MessageDigest.getInstance("SHA");
// 完成摘要
byte[] digest = md5.digest(originalStr.getBytes());
// 將摘要轉換成 16 進制字符 (大寫)
String javaShaStr = DatatypeConverter.printHexBinary(digest);
System.out.println(String.format("%s 加密結果:%s", originalStr, javaShaStr));
// 匹配驗證
System.out.println(String.format("驗證結果:%b", Objects.equals(javaShaStr, alreadyDigestStr)));
/* Apache commons-codec 實現 */
String apacheShaStr = DigestUtils.sha1Hex(originalStr.getBytes()).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, apacheShaStr));
System.out.println(String.format("驗證結果:%b", Objects.equals(apacheShaStr, alreadyDigestStr)));
/* SHA256 */
System.out.println(DigestUtils.sha256Hex(originalStr.getBytes()));
/* SHA384 */
System.out.println(DigestUtils.sha384Hex(originalStr.getBytes()));
/* SHA512 */
System.out.println(DigestUtils.sha512Hex(originalStr.getBytes()));
}
}
運行結果如下:
消息鑒別:
3、MAC (Hash Message Authentication Code 散列消息鑒別碼)
MAC算法 (Message Authentication Codes消息認證碼算法) 含有密鑰的散列函數算法,兼容了MD和SHA算法的特性,並在此基礎上加上了密鑰。因此MAC算法也經常被稱作HMAC算法。消息的散列值由只有通信雙方知道的密鑰來控制。此時Hash值稱作MAC。
經過MAC算法得到的摘要值也可以使用十六進制編碼表示,其摘要值得長度與實現算法的摘要值長度相同。例如 HmacSHA算法得到的摘要長度就是SHA1算法得到的摘要長度,都是160位二進制數,換算成十六進制的編碼為40位。
流程分析:
甲乙雙方進行數據交換可以采取如下流程:
1.甲方向乙方公布摘要算法(就是指定要使用的摘要算法名)
2.甲乙雙方按照約定構造密鑰,雙方擁有相同的密鑰(一般是一方構造密鑰后通知另外一方,此過程不需要通過程序實現,就是雙方約定個字符串,但是這個字符串可不是隨便設定的,也是通過相關算法獲取的)
3.甲方使用密鑰對消息做摘要處理,然后將消息和生成的摘要消息一同發送給乙方
4.乙方收到消息后,使用甲方已經公布的摘要算法+約定好的密鑰 對收到的消息進行摘要處理。然后比對自己的摘要消息和甲方發過來的摘要消息。甄別消息是否是甲方發送過來的。
代碼實現:
/** * MAC 算法測試 */
class MacTest {
public static void main(String[] args) throws NoSuchAlgorithmException, DecoderException, InvalidKeyException {
// 待加密字符
String originalStr = "111111";
System.out.println(String.format("待加密字符: %s", originalStr));
// 已加密字符
String alreadyDigestStr = "74B31DBA9E16CCF752B294A18271BC6A";
System.out.println(String.format("已加密字符: %s", alreadyDigestStr));
/* jdk 實現 */
// 初始化 KeyGenerator, jdk 提供 HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和 HmacSHA512 四種算法
// KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
// 產生密鑰
// SecretKey secretKey = keyGenerator.generateKey();
// 默認密鑰
// byte[] defaultKey = secretKey.getEncoded();
// 自定義密鑰
byte[] myKey = Hex.decodeHex(new char[]{'a','a','a','a','a','a','a','a','a','a'});
// 還原密鑰
SecretKey restoreSecretKey = new SecretKeySpec(myKey, "HmacMD5");
// 實例化 MAC
Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());
// 初始化 MAC
mac.init(restoreSecretKey);
//執行摘要
byte[] hmacMD5Bytes = mac.doFinal(originalStr.getBytes());
String encodeHexString = Hex.encodeHexString(hmacMD5Bytes).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, encodeHexString));
System.out.println(String.format("驗證結果:%b", Objects.equals(encodeHexString, alreadyDigestStr)));
/* apache 實現 */
// HmacMD5
HmacUtils hmacMd5 = new HmacUtils(HmacAlgorithms.HMAC_MD5, myKey);
String apacheHmacMd5 = hmacMd5.hmacHex(originalStr.getBytes()).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, apacheHmacMd5));
System.out.println(String.format("驗證結果:%b", Objects.equals(apacheHmacMd5, alreadyDigestStr)));
// HmacSHA1
HmacUtils hmacSha1 = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, KeyGenerator.getInstance(HmacAlgorithms.HMAC_SHA_1.getName()).generateKey().getEncoded());
String apacheHmacHex1 = hmacSha1.hmacHex(originalStr.getBytes()).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, apacheHmacHex1));
// HmacSHA256
HmacUtils hmacSha256 = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, KeyGenerator.getInstance(HmacAlgorithms.HMAC_SHA_256.getName()).generateKey().getEncoded());
String apacheHmacSha256 = hmacSha256.hmacHex(originalStr.getBytes()).toUpperCase();
System.out.println(String.format("%s 加密結果:%s", originalStr, apacheHmacSha256));
// HmacSHA384 , 類似上面
// HmacSHA512, 類似上面
}
}
運行結果:
上面這幾種摘要算法,可謂是非可逆加密,就是不可解密的加密方法。我們通常只把他們作為加密的基礎。單純的以上三種的加密並不可靠。