數(shù)據(jù)摘要算法(信息摘要)是密碼學算法中非常重要的一個分支,它通過對所有數(shù)據(jù)提取指紋信息以實現(xiàn)數(shù)據(jù)簽名、數(shù)據(jù)完整性校驗等功能,由于算法具有其不可逆性,有時候也會被用做敏感信息加密。
消息摘要算法的主要特征是加密過程不需要密鑰,并且經(jīng)過加密的數(shù)據(jù)無法被解密,可以被解密逆向的只有CRC32算法,只有輸入相同的明文數(shù)據(jù)經(jīng)過相同的消息摘要算法才能得到相同的密文。
消息摘要算法也叫散列函數(shù)、哈希函數(shù),嚴格的說并不算是一種加密算法。消息摘要算法的主要作用不是完成數(shù)據(jù)的加密與解密工作的,它是用來驗證數(shù)據(jù)完整性的重要技術(shù)。通過摘要函數(shù),可以為數(shù)據(jù)創(chuàng)建“數(shù)字指紋”(散列值)。
需要指出的是MAC(Message Authentication Code)算法是支持密鑰的一種摘要算法。
2 特點碰撞
)在計算機領域,我們希望能知道別人傳遞的消息是否完整、是否有被篡改,這里就用到信息摘要算法。
例如我們要在數(shù)據(jù)庫中保存用戶密碼,首先肯定不能保存明文,既然要使用密文保存,那使用對稱加密還是非對稱加密呢?結(jié)合實際場景和安全性考慮,兩種都不合適!因為這兩種都要考慮密鑰的管理,如果密鑰泄漏那密碼則有被破解的風險。在這種場景下,我們可以使用單向散列函數(shù)來解決這個問題,這樣即使算法和密文都泄漏,也無法逆向計算出明文。
當然散列函數(shù)還有更多用途,比如文件一致性校驗、數(shù)字簽名等。
4 常用的非對稱加密算法信息摘要算法來源于CRC算法,最初CRC算法是用來驗證數(shù)據(jù)完整性的,即我們常見的奇偶校驗碼、循環(huán)冗余校驗,在CRC基礎上發(fā)展出了MD和SHA兩大算法家族,CRC比這些算法都要早,MD算法比SHA算法早,SHA算法是對MD算法的改進。再后來則發(fā)展出了可以帶有密碼的信息摘要算法-MAC算法。
信息摘要算法包括三大類,MD、SHA和MAC算法,MD的分類是按照版本規(guī)定的,SHA一般是按照產(chǎn)生的消息長度分類的,但是SHA系列算法在學術(shù)上會按照算法版本區(qū)分SHA-0、SHA-1、SHA-2、SHA-3,
MAC算法是Hmac加融合的其他算來命名的。
MD5算法是典型的消息摘要算法,它由MD4、MD3、MD2算法改進而來。無論是哪種MD算法,它們都需要獲得一個隨機長度的信息并產(chǎn)生一個128位的二進制信息摘要,轉(zhuǎn)換成16進制就是32位的字符串。我們見到的大部分MD5算法的數(shù)字指紋都是32位十六進制的字符串。
隨著計算機發(fā)展水平的提高,MD5算法已不再適合安全要求較高的場合。
4.2 SHA(Secure Hash Algorithm)安全散列SHA算法基于MD4算法基礎之上,其與MD算法不同之處在于摘要長度,SHA算法的摘要長度更長,安全性更高。
SHA-0算法,1983年第一個版本,因降低密碼安全性撤回
SHA-1算法,1995年發(fā)布,通常我們也把SHA-1算法簡稱為SHA算法。其在安全協(xié)定中使用廣泛。包括TLS/SSL,SSH等。SHA-0和SHA-1算法可對大長度為264的字節(jié)信息做摘要處理,得到160位的摘要信息,40為16進制的字符串
SHA-2算法,是SHA-224,SHA-256,SHA-384,SHA-512四種算法的總稱。
MAC算法結(jié)合了MD5和SHA算法的優(yōu)勢,并加入了密鑰的支持,是一種更為安全的消息摘要算法。我們也通常把MAC算法稱為HMAC算法(keyed-Hash Message Authentication Code)。
MD系列算法與SHA系列算法分別是兩種不同形式的信息摘要算法,都是沒有密鑰的散列函數(shù)算法。MAC兼容了MD與SHA這兩種的特性,并且在這個基礎上添加了密鑰。通常又把MAC稱為HMAC,即含有密鑰的散列函數(shù)算法。
MAC算法則為MD與SHA系列算法的基礎上添加了密鑰,構(gòu)成了新的算法:HmacMD2、HmacMD4、HmacMD5、HmacSHA1、HmacSHA224、HmacSHA356、HmacSHA384、HmacSHA512。
5 JDK支持的信息摘要算法JDK8原生算法列表,可參第一篇博文: https://blog.csdn.net/yunyun1886358/article/details/128592503#311_JDK_Provider_63
6 Bouncy Castle 支持的信息摘要算法Bouncy Castle算法列表,可參第一篇博文:
https://editor.csdn.net/md/?articleId=128592503#323_Bouncy_Castle_Provider_568
碰撞
和不可逆
是破解摘要算法的基礎。
碰撞
就是先基于消息1用信息摘要算法算出一個摘要值,再根據(jù)這個值,逆算出另外一個不同的消息2,但是它們的摘要值是一樣的。
如果我們能找到一個消息2,生成的摘要與原始消息1是一樣的,那么無論消息1和2是否一樣,我們都可以將消息2發(fā)送給校驗方,并通過校驗。因為校驗方只持有摘要值,并且根據(jù)摘要值無法還原消息1,校驗方也不知道消息1是什么,他只校驗消息2通過算法生成的摘值和他持有的是一致,校驗就通過了。
2004年,我國中科院院士王小云證實md5算法無法防止碰撞,因此,不適用于安全性認證。她的研究成果表明了給定消息 M1,能夠計算獲取 M2,使得 M2 產(chǎn)生的散列值與 M1 產(chǎn)生的散列值相同。
7.2 MD5的破解都說MD5已經(jīng)被證明是一種不安全的算法,那么如何對MD5進行碰撞呢 ?
7.2.1 窮舉法窮舉法就是不停地嘗試各種字符的排列組合,看哪一個組合的MD5碼能對上。缺點是太耗費時間。舉個例子,假設我們要破解一個6位大小寫字母和數(shù)字混合的密碼,那么一共有 (26 + 26 + 10) ^ 6 種組合。這個數(shù)的大小超過500億。
7.2.2 字典法字典法就是把計算結(jié)果以映射表的形式存放起來,一個原文對應著一個MD5值。將已知的MD5碼查表,就可直接反查出原文。字典法體現(xiàn)了算法設計的“以空間換時間”的思想。缺點是比較耗費空間,而且實際上還是要窮舉一遍所有的輸入,只不過把窮舉的結(jié)果存了起來。
一個用字典法實現(xiàn)md5解密的網(wǎng)站:https://md5.cn/
如果說窮舉法太耗費時間,字典法太耗費存儲空間的話,我們能不能考慮在時間消耗和空間消耗之間折中呢?我們可以考慮用鏈表將一系列有意義的原文和 MD5 碼串起來。
大概方法就是將一部分MD5摘要值和原文按照相近性分別保存到不同的哈希鏈表中,然后根據(jù)要破解的MD5摘要值計算出原始消息可能存在那條鏈表中,然后這條鏈表的頭開始計算,然后得到碰撞值。
一個已經(jīng)計算好的彩虹表: http://project-rainbowcrack.com/table.htm
7.2.4 差分攻擊上面介紹的窮舉法、字典法和彩虹表法都是暴力破解,適用于任何的消息摘要算法。真正意義上 MD5 算法的破解,是 2004 年山東大學王小云教授提出的 MD5 碰撞方法。她所用到的方法正是差分攻擊。
這種方法概括起來說是這樣的:給定一個 1024 位的原文 M1,加上一個特定的常數(shù)得到的新的明文 M2。M1 和 M2 的 MD5 碼是一樣的。
7.3 降低MD5的破解概率有一種方法可以降低MD5碰撞的概率,那就是在計算摘要的時候加鹽
,也就是
MD5(原文 + 鹽)
鹽可以是任意字母、數(shù)字、或是字母或數(shù)字的組合,但必須是隨機產(chǎn)生的,最好每次摘要加的鹽都不一樣。
例如網(wǎng)站用戶注冊,保存用戶密碼時,為每個用戶生成一個隨機的鹽,然后將 MD5( 明文密碼 + 鹽)摘要值存入用戶表,并且在用戶表中添加一個字段,用來保存鹽。
由于加了鹽,即便數(shù)據(jù)庫泄露了,但是由于密碼都是加了鹽之后的散列,數(shù)據(jù)字典已經(jīng)無法直接匹配,明文密碼被碰撞成功的概率也大大降低。
是不是加了鹽之后就絕對安全了呢?當然沒有。攻擊者還是可以他們數(shù)據(jù)字典中的密碼,加上我們泄露數(shù)據(jù)庫中的 Salt,然后散列,然后再匹配。這要就需要重新生成數(shù)據(jù)字典,但是由于我們的鹽是隨機產(chǎn)生的,假如我們的用戶數(shù)據(jù)表中有 100w 條數(shù)據(jù),那么就有100w個鹽。如數(shù)據(jù)字典中有 1000w 條數(shù)據(jù),那么如果要生成新的數(shù)據(jù)字典,那么數(shù)字典的明文加上鹽之后再散列的數(shù)據(jù)字典數(shù)據(jù)量就應該是 1,000,000* 10,000,000 = 10,000,000,000,000,共10兆條數(shù)據(jù),成本非常之高。
8 算法調(diào)用示例下面的代碼將JDK提供的幾種信息摘要算法用枚枚舉類進行了封裝。
package com.qupeng.crypto.algorithm.oop;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class DigestAlgorithmTest {@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void digest() throws NoSuchAlgorithmException {Assert.assertEquals("Ow7l+x4vPrlj/ZrwYVPHjw==", DigestAlgorithm.MD2.digest("12345678901234567890"));
Assert.assertEquals("/YXmLZvrRUKHcexohBiycQ==", DigestAlgorithm.MD5.digest("12345678901234567890"));
Assert.assertEquals("fgoSQr2O+QRPJ9ykX19yrVoRJb8=", DigestAlgorithm.SHA_1.digest("12345678901234567890"));
Assert.assertEquals("LGQud880sKr3FSrFxbJtG9c/24h0vLDZHHyRPw==", DigestAlgorithm.SHA_224.digest("12345678901234567890"));
Assert.assertEquals("t72NaA22sXKHBeXh1BlYyTryjk2KKJ4A3DckaRxbXTw+RBznZieVycTGAgbzNWkp", DigestAlgorithm.SHA_384.digest("12345678901234567890"));
Assert.assertEquals("btZF7w4avqG/Hk6TX/BPnhjTmBI4f2PNo0FbRiQPBAU=", DigestAlgorithm.SHA_256.digest("12345678901234567890"));
Assert.assertEquals("qjt73ZjsRK8fOVu9X38npc2VadeU0DJ0cyO/SxUh++dyWHWmi0QKvfBVneUBW6+HO7nAHK5j7OqTrVR6c5dBbg==", DigestAlgorithm.SHA_512.digest("12345678901234567890"));
}
@Test(expected=NoSuchAlgorithmException.class)
public void digestBySha512224() throws NoSuchAlgorithmException {DigestAlgorithm.SHA_512_224.digest("12345678901234567890");
}
@Test
public void digestBySha512256() throws NoSuchAlgorithmException {exception.expect(NoSuchAlgorithmException.class);
exception.expectMessage("SHA-512/256 MessageDigest not available");
DigestAlgorithm.SHA_512_256.digest("12345678901234567890");
}
@Test
public void digestMAC() throws InvalidKeyException, NoSuchAlgorithmException {String digestStr = DigestAlgorithm.HMAC_MD5.digestMAC("12345678901234567890", 128);
Assert.assertEquals(DigestAlgorithm.HMAC_MD5.getDigestMessageLength(), Base64.getDecoder().decode(digestStr).length * 8);
digestStr = DigestAlgorithm.HMAC_SHA1.digestMAC("12345678901234567890", 128);
Assert.assertEquals(DigestAlgorithm.HMAC_SHA1.getDigestMessageLength(), Base64.getDecoder().decode(digestStr).length * 8);
digestStr = DigestAlgorithm.HMAC_SHA224.digestMAC("12345678901234567890", 128);
Assert.assertEquals(DigestAlgorithm.HMAC_SHA224.getDigestMessageLength(), Base64.getDecoder().decode(digestStr).length * 8);
digestStr = DigestAlgorithm.HMAC_SHA256.digestMAC("12345678901234567890", 128);
Assert.assertEquals(DigestAlgorithm.HMAC_SHA256.getDigestMessageLength(), Base64.getDecoder().decode(digestStr).length * 8);
digestStr = DigestAlgorithm.HMAC_SHA384.digestMAC("12345678901234567890", 128);
Assert.assertEquals(DigestAlgorithm.HMAC_SHA384.getDigestMessageLength(), Base64.getDecoder().decode(digestStr).length * 8);
digestStr = DigestAlgorithm.HMAC_SHA512.digestMAC("12345678901234567890", 128);
Assert.assertEquals(DigestAlgorithm.HMAC_SHA512.getDigestMessageLength(), Base64.getDecoder().decode(digestStr).length * 8);
}
}
package com.qupeng.crypto.algorithm.oop;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public enum DigestAlgorithm {MD2("MD2", 128),
MD5("MD5", 128),
SHA_1("SHA-1", 160),
SHA_224("SHA-224", 224),
SHA_256("SHA-256", 256),
SHA_384("SHA-384", 384),
SHA_512("SHA-512", 512),
SHA_512_224("SHA-512/224", 512),
SHA_512_256("SHA-512/256", 512),
HMAC_MD5("HmacMD5", 128),
HMAC_SHA1("HmacSHA1", 160),
HMAC_SHA224("HmacSHA224", 224),
HMAC_SHA256("HmacSHA256", 256),
HMAC_SHA384("HmacSHA384", 384),
HMAC_SHA512("HmacSHA512", 512);
private String algorithm = "";
private int digestMessageLength = -1;
DigestAlgorithm(String algorithm, int digestMessagLength) {this.algorithm = algorithm;
this.digestMessageLength = digestMessagLength;
}
public String digest(String plainText) throws NoSuchAlgorithmException {MessageDigest messageDigest = MessageDigest.getInstance(this.algorithm);
byte [] bytes = messageDigest.digest(plainText.getBytes());
String digestStr = Base64.getEncoder().encodeToString(bytes);
System.out.println(String.format("%s(%d) plain text: %s ->digest text: %s", this.algorithm, this.digestMessageLength, plainText, digestStr));
return digestStr;
}
public String digestMAC(String plainText, int keyLength) throws NoSuchAlgorithmException, InvalidKeyException {KeyGenerator keyGenerator = KeyGenerator.getInstance(this.algorithm);
keyGenerator.init(keyLength);
SecretKey secretKey = keyGenerator.generateKey();
Mac mac = Mac.getInstance(this.algorithm);
mac.init(secretKey);
byte[] hmacMD5Bytes = mac.doFinal(plainText.getBytes());
String digestStr = Base64.getEncoder().encodeToString(hmacMD5Bytes);
System.out.println(String.format("%s(%d) plain text: %s ->digest text: %s", this.algorithm, this.digestMessageLength, plainText, digestStr));
return digestStr;
}
public int getDigestMessageLength() {return digestMessageLength;
}
}
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧