1、MD算法的基的概念
? ?MD5算法是典型的消息摘要算法,其前身有MD2、MD3和MD4算法,它由MD4、MD3和MD2算法改進(jìn)而來(lái)。不論是哪一種MD算法,它們都需 要獲得一個(gè)隨機(jī)長(zhǎng)度的信息并產(chǎn)生一個(gè)128位的信息摘要。如果將這個(gè)128位的二進(jìn)制摘要信息換算成十六進(jìn)制,可以得到一個(gè)32位的字符串,故我們見(jiàn)到的 大部分MD5算法的數(shù)字指紋都是32為十六進(jìn)制的字符串。
2、MD算法的發(fā)展史
2.1 MD2算法
? ?1989年,著名的非對(duì)稱算法RSA發(fā)明人之一----麻省理工學(xué)院教授羅納德.李維斯特開(kāi)發(fā)了MD2算法。這個(gè)算法首先對(duì)信息進(jìn)行數(shù)據(jù)補(bǔ)位,使信 息的字節(jié)長(zhǎng)度是16的倍數(shù)。再以一個(gè)16位的檢驗(yàn)和做為補(bǔ)充信息追加到原信息的末尾。最后根據(jù)這個(gè)新產(chǎn)生的信息計(jì)算出一個(gè)128位的散列值,MD2算法由 此誕生。
2.2 MD4算法
? ?1990年,羅納德.李維斯特教授開(kāi)發(fā)出較之MD2算法有著更高安全性的MD4算法。在這個(gè)算法中,我們?nèi)孕鑼?duì)信息進(jìn)行數(shù)據(jù)補(bǔ)位。不同的是,這種補(bǔ) 位使其信息的字節(jié)長(zhǎng)度加上448個(gè)字節(jié)后成為512的倍數(shù)(信息字節(jié)長(zhǎng)度mod 512 =448)。此外,關(guān)于MD4算的處理和MD2算法有很大的差別。但最終仍舊會(huì)獲得一個(gè)128為的散列值。MD4算法對(duì)后續(xù)消息摘要算法起到了推動(dòng)作用, 許多比較有名的消息摘要算法都是在MD4算法的基礎(chǔ)上發(fā)展而來(lái)的,如MD5、SHA-1、RIPE-MD和HAVAL算法等。
2.3 MD5算法
? ?1991年,繼MD4算法后,羅納德.李維斯特教授開(kāi)發(fā)了MD5算法,將MD算法推向成熟。MD5算法經(jīng)MD2、MD3和MD4算法發(fā)展而來(lái),算法復(fù)雜程度和安全強(qiáng)度打打提高,但浙西MD算法的最終結(jié)果都是產(chǎn)生一個(gè)128位的信息摘要。這也是MD系列算法的特點(diǎn)。MD5算法的算法特點(diǎn)如下:
(1)壓縮性:任意長(zhǎng)度的數(shù)據(jù),算出的MD5值長(zhǎng)度都是固定的。
(2)容易計(jì)算:從原數(shù)據(jù)計(jì)算出MD5值很容易。
(3)抗修改性:對(duì)原數(shù)據(jù)進(jìn)行任何改動(dòng),哪怕只修改1個(gè)字節(jié),所得到的MD5值都有很大區(qū)別。
(4)弱抗碰撞:已知原數(shù)據(jù)和其MD5值,想找到一個(gè)具有相同MD5值的數(shù)據(jù)(即偽造數(shù)據(jù))是非常困難的。
(5)強(qiáng)抗碰撞:想找到兩個(gè)不同的數(shù)據(jù),使它們具有相同的MD5值,是非常困難的。
2.4、MD5破解方面
? ?在破解md5方面,最常用的方法是“跑字典”,有兩種方法得到字典,一種是日常搜集的用做密碼的字符串表,另一種是用排列組合方法生成的,先用MD5程序計(jì)算出這些字典項(xiàng)的MD5值,然后再用目標(biāo)的MD5值在這個(gè)字典中檢索。我們假設(shè)密碼的最大長(zhǎng)度為8位字節(jié)(8 Bytes),同時(shí)密碼只能是字母和數(shù)字,共26+26+10=62個(gè)字節(jié),排列組合出的字典的項(xiàng)數(shù)則是P(62,1)+P(62,2)….+P(62,8),那也已經(jīng)是一個(gè)很天文的數(shù)字了,存儲(chǔ)這個(gè)字典就需要TB級(jí)的磁盤陣列,而且這種方法還有一個(gè)前提,就是能獲得目標(biāo)賬戶的密碼MD5值的情況下才可以。
所以總體而言,md5加密是十分安全的,即使有一些瑕疵,但并不影響具體的使用,外加md5是免費(fèi)的,所以它的應(yīng)用還是十分廣泛的。
3、MD5算法應(yīng)用
3.1、Md5 密碼存儲(chǔ)加鹽
? ? MD5算法,可以用來(lái)保存用戶的密碼信息。為了更好的保存,可以在保存的過(guò)程中,加入鹽。/在保存用戶密碼的時(shí)候,鹽可以利用生成的隨機(jī)數(shù)??梢詫⒚艽a結(jié)合MD5加鹽,生成的數(shù)據(jù)摘要和鹽保存起來(lái) 。以便于下次用戶驗(yàn)證使用。在用戶表里面,也保存salt。
3.2、Md5 文件完整性校驗(yàn)
? ? 每個(gè)文件都可以用MD5驗(yàn)證程序算出一個(gè)固定的MD5值,是獨(dú)一無(wú)二的。一般來(lái)說(shuō),開(kāi)發(fā)方會(huì)在軟件發(fā)布時(shí)預(yù)先算出文件的MD5值,如果文件被盜用,加了木馬或者被篡改版權(quán),那么它的MD5值也隨之改變,也就是說(shuō)我們對(duì)比文件當(dāng)前的MD5值和它標(biāo)準(zhǔn)的MD5值來(lái)檢驗(yàn)它是否正確和完整。
(1)例如網(wǎng)盤中的秒傳4G文件,可以使用用戶需要上傳的文件進(jìn)行Md5運(yùn)算,判斷與服務(wù)器中是否存在該文件,如果存在只需添加文件索引,不存在再真正上傳。
(2)例如自動(dòng)升級(jí)的客戶端,判斷下載的程序安裝包是否完整,可以計(jì)算文件的MD5值,與服務(wù)器端計(jì)算的Md5值進(jìn)行比對(duì)。
4、MD5算法實(shí)現(xiàn)
4.1 JDK算法實(shí)現(xiàn)
package lzf.cipher.jdk;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author Java小工匠
*/
public class JdkMd5Utils {
public static final Charset UTF8 = Charset.forName("UTF-8");
public static final int SI = 8;
public static final int EI = 24;
// MD5 小寫16位
public static String md5L16(String str) {
return md5L32(str.getBytes(UTF8)).substring(SI, EI);
}
// MD5 大寫16位
public static String md5U16(String str) {
return md5U32(str.getBytes(UTF8)).substring(SI, EI);
}
// MD5 小寫16位
public static String md5L16(byte[] bytes) {
return md5L32(bytes).substring(SI, EI);
}
// MD5 大寫16位
public static String md5U16(byte[] bytes) {
return md5U32(bytes).substring(SI, EI);
}
// =========================================================
// 默認(rèn)Md5算法
public static String md5(String str) {
return md5U32(str);
}
// 默認(rèn)Md5算法
public static String md5(byte[] bytes) {
return md5U32(bytes);
}
// MD5 小寫32位
public static String md5L32(String str) {
return md5L32(str.getBytes(UTF8));
}
// MD5 小寫32位
public static String md5L32(byte[] bytes) {
try {
// 1、獲得MD5摘要算法的 MessageDigest 對(duì)象
MessageDigest digest = MessageDigest.getInstance("md5");
// 2、使用指定的字節(jié)更新摘要
digest.update(bytes);
// 3、獲得密文
byte[] rsBytes = digest.digest();
// 4、把密文轉(zhuǎn)換成十六進(jìn)制的字符串形式
return encodeHex(rsBytes, true);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
// MD5 大寫32位
public static String md5U32(String str) {
return md5U32(str.getBytes(UTF8));
}
// MD5 大寫32位
public static String md5U32(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("md5");
digest.update(bytes);
byte[] rsBytes = digest.digest();
return encodeHex(rsBytes, false);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
// 字節(jié)數(shù)組轉(zhuǎn)16 進(jìn)制
// 數(shù)據(jù)準(zhǔn)16進(jìn)制編碼
public static String encodeHex(final byte[] data) {
return encodeHex(data, true);
}
// 數(shù)據(jù)轉(zhuǎn)16進(jìn)制編碼
public static String encodeHex(final byte[] data, final boolean toLowerCase) {
final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER;
final int l = data.length;
final char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return new String(out);
}
public static void main(String[] args) {
String str = "Java小工匠";
String low16 = md5L16(str);
String upper16 = md5U16(str);
String low32 = md5L32(str);
String upper32 = md5U32(str);
System.out.println("16位小寫:" + low16);
System.out.println("16位大寫:" + upper16);
System.out.println("32位小寫:" + low32);
System.out.println("32位大寫:" + upper32);
}
}
4.2 CC 算法實(shí)現(xiàn)
package lzf.cipher.cc;
import java.nio.charset.Charset;
import org.apache.commons.codec.digest.DigestUtils;
/**
* @author Java小工匠
*/
public class CCMd5Utils {
public static final Charset UTF8 = Charset.forName("UTF-8");
public static final int SI = 8;
public static final int EI = 24;
// MD5 小寫16位
public static String md5L16(String str) {
return md5L32(str.getBytes(UTF8)).substring(SI, EI);
}
// MD5 大寫16位
public static String md5U16(String str) {
return md5U32(str.getBytes(UTF8)).substring(SI, EI);
}
// MD5 小寫16位
public static String md5L16(byte[] bytes) {
return md5L32(bytes).substring(SI, EI);
}
// MD5 大寫16位
public static String md5U16(byte[] bytes) {
return md5U32(bytes).substring(SI, EI);
}
// =========================================================
// 默認(rèn)Md5算法
public static String md5(String str) {
return md5U32(str);
}
// 默認(rèn)Md5算法
public static String md5(byte[] bytes) {
return md5U32(bytes);
}
// MD5 小寫32位
public static String md5L32(String str) {
return md5L32(str.getBytes(UTF8));
}
// MD5 小寫32位
public static String md5L32(byte[] bytes) {
return DigestUtils.md5Hex(bytes);
}
// MD5 大寫32位
public static String md5U32(String str) {
return md5U32(str.getBytes(UTF8));
}
// MD5 大寫32位
public static String md5U32(byte[] bytes) {
return DigestUtils.md5Hex(bytes).toUpperCase();
}
public static void main(String[] args) {
String str = "Java小工匠";
String low16 = md5L16(str);
String upper16 = md5U16(str);
String low32 = md5L32(str);
String upper32 = md5U32(str);
System.out.println("16位小寫:" + low16);
System.out.println("16位大寫:" + upper16);
System.out.println("32位小寫:" + low32);
System.out.println("32位大寫:" + upper32);
}
}
4.2 BC 算法實(shí)現(xiàn)
package lzf.cipher.bc;
import java.nio.charset.Charset;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.util.encoders.Hex;
/**
* @author Java小工匠
*/
public class BCMd5Utils {
public static final Charset UTF8 = Charset.forName("UTF-8");
public static final int SI = 8;
public static final int EI = 24;
// MD5 小寫16位
public static String md5L16(String str) {
return md5L32(str.getBytes(UTF8)).substring(SI, EI);
}
// MD5 大寫16位
public static String md5U16(String str) {
return md5U32(str.getBytes(UTF8)).substring(SI, EI);
}
// MD5 小寫16位
public static String md5L16(byte[] bytes) {
return md5L32(bytes).substring(SI, EI);
}
// MD5 大寫16位
public static String md5U16(byte[] bytes) {
return md5U32(bytes).substring(SI, EI);
}
// =========================================================
// 默認(rèn)Md5算法
public static String md5(String str) {
return md5U32(str);
}
// 默認(rèn)Md5算法
public static String md5(byte[] bytes) {
return md5U32(bytes);
}
// MD5 小寫32位
public static String md5L32(String str) {
return md5L32(str.getBytes(UTF8));
}
// MD5 小寫32位
public static String md5L32(byte[] data) {
Digest digest = new MD5Digest();
digest.update(data, 0, data.length);
byte[] rsData = new byte[digest.getDigestSize()];
digest.doFinal(rsData, 0);
return Hex.toHexString(rsData);
}
// MD5 大寫32位
public static String md5U32(String str) {
return md5U32(str.getBytes(UTF8));
}
// MD5 大寫32位
public static String md5U32(byte[] bytes) {
return md5L32(bytes).toUpperCase();
}
// MD4 算法
public static String md4(byte[] data) {
Digest digest = new MD5Digest();
digest.update(data, 0, data.length);
byte[] rsData = new byte[digest.getDigestSize()];
digest.doFinal(rsData, 0);
return Hex.toHexString(data);
}
public static void main(String[] args) {
String str = "Java小工匠";
String low16 = md5L16(str);
String upper16 = md5U16(str);
String low32 = md5L32(str);
String upper32 = md5U32(str);
String md4 = md4(str.getBytes());
System.out.println("16位小寫:" + low16);
System.out.println("16位大寫:" + upper16);
System.out.println("32位小寫:" + low32);
System.out.println("32位大寫:" + upper32);
System.out.println("md4:" + md4);
}
}
如果讀完覺(jué)得有收獲的話,歡迎點(diǎn)贊、關(guān)注、加公眾號(hào)【小工匠技術(shù)圈】
個(gè)人公眾號(hào),歡迎關(guān)注,查閱更多精彩歷史!