【Java小工匠】消息摘要--MD算法

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)注,查閱更多精彩歷史!

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Message Digest Algorithm MD5(消息摘要算法第五版)為計(jì)算機(jī)安全領(lǐng)域廣泛使用的一種散列函...
    Pandakingli閱讀 945評(píng)論 0 0
  • 這里先簡(jiǎn)單介紹單向散列函數(shù)、消息摘要和哈希碰撞的的概念 單向散列函數(shù): 將任意長(zhǎng)度的信息轉(zhuǎn)換為較短的固定長(zhǎng)度的值,...
    坤_7a1e閱讀 3,651評(píng)論 0 0
  • 消息摘要算法 友情提示,本文檔的圖床使用極簡(jiǎn)圖床進(jìn)行圖片存儲(chǔ),默認(rèn)存儲(chǔ)到七牛云空間 本文檔依賴的jar maven...
    Mr_魏閱讀 1,158評(píng)論 0 3
  • 勝利是夜風(fēng)里驕傲的茅草 我們收割著鋒利情緒去殺死你 勝利是屬于星空的而不屬于仰望的人 我們把淚水破碎的形狀裝進(jìn)去 ...
    烏鴉之白閱讀 334評(píng)論 3 4
  • 發(fā) 1s 2s 才 printf("hello world"); $f(x)=x$ $$\lbrace \sum...
    xiongjiezk閱讀 334評(píng)論 0 0

友情鏈接更多精彩內(nèi)容