精华内容
下载资源
问答
  • md5算法实现
    千次阅读
    2020-11-02 21:06:30

    MD5算法原理及代码实现

    MD5:Message-Digest Algorithm 是一种不可逆的字符串变换算法,严格来说并不能称之为加密算法。原因在于无法通过一定的密钥key进行还原,这也是为什么该算法不可逆,因为其本身是对任意长度的字符串转换为一个128bit的大整数。
    

    MD5算法加密步骤

    1、填充

    MD5加密算法可以针对大文件甚至几GB的文件进行加密,打个比方,就像是一头整牛进入加工车间,出来的是一袋冷藏肉一样,这个过程必然也是不可逆的。
    要加密的信息必然是不定长度的。而输出要是一个128bits的,由四个32位分组组成的,然后将四个32位分组级联成一个128位散列值。首先进行填充:
    1.将信息填充成其位长对512取余的448的长度(马上解释)
    在信息后面填充一个1和N个0,直到满足第一个条件
    2.在结果后面附加一个以64位二进制表示的填充前信息长度,极端情况64位不够表示长度就取低64位的
    经过1.2两步后我们得到一个信息位长  N*512+448+64=(N+1)*512的填充好的信息
    

    注意:尽管我们说MD5算法是不可逆的,但是在现实情况会有两种情况

    一、用户输入简单密码例如123456那么MD5加密后的结果为32位的字符串,多次输入123456结果相同,就有了彩虹表,可以对简单密码输出结果进行对比,从而破解密码。

    二、两段不同输入可能对应同一种输出结果,称之为杂凑冲撞。有实际案例。

    2.初始化变量

    定义四个大端字节序组成的初始链接变量,由16进制组成,命名为A.B.C.D
    令:
    A=0x01234567,
    
    B=0x89ABCDEF,
    
    C=0xFEDCBA98,
    
    D=0x76543210。  
    此为物理顺序,程序中定义中,考虑到内存数据存储大小端的问题,将高字节存于内存低地址,低字节存于内存高地址。
    定义四个线性函数:
    F(X,Y,Z)=(X&Y)|((~X)&Z)
    
    G(X,Y,Z)=(X&Z)|(Y&(~Z))
    
    H(X,Y,Z)=X^Y^Z
    
    I(X,Y,Z)=Y^(X|(~Z))
    
    

    3.循环运算

    一、输入数据每512字节分为16组,则一组64位(8个字节)
    二、设Mj表示消息的第j个子分组(从0到15),
    FF(a,b,c,d,Mj,s,ti)表示a=b+((a+F(b,c,d)+Mj+ti)<<<s)
    
    GG(a,b,c,d,Mj,s,ti)表示a=b+((a+G(b,c,d)+Mj+ti)<<<s)
    
    HH(a,b,c,d,Mj,s,ti)表示a=b+((a+H(b,c,d)+Mj+ti)<<<s)
    
    II(a,b,c,d,Mj,s,ti)表示a=b+((a+I(b,c,d)+Mj+ti)<<<s)
    三、主循环有四轮,第一轮进行16次操作,每次操作对abcd其中三个进行非线性函数运算,然后加上第四个变量,文本的一个子分组和一个常熟,再将所得的结果向左移一个不定的数
    四、最后输出的就是一个a.b.c.d的级联。
    

    MD5算法的优劣

    1、相对于MD4进行三轮,MD5多一轮,相对安全性提高

    2、每一步均有唯一的加法常数

    3、为减弱第二轮中函数G的对称性从(X&Y)|(X&Z)|(Y&Z)变为(X&Z)|(Y&(~Z))
    4、第一步加上了上一步的结果,这将引起更快的雪崩效应.
    5、改变了第二轮和第三轮中访问消息子分组的次序,使其更不相似.
    6、近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应.各轮的位移量互不相同

    但同样的,因为用户输入基本不会太长,所有也有了响应的彩虹表破解用户密码的事情偶有发生,这这时就用到了加盐策略。

    另:trump的Twitter密码被一名荷兰黑客猜中为“MAGA2020!”

    JAVA实现MD5

    import java.security.MessageDigest;
    // MassageDigest 信息摘要算法 提供MD5和SHA,且输出固定长度的HASH值,一般为32位
    public class MyMD5 {
    
        static char[] hex = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
        public static void main(String[] args) {
            try{
                MessageDigest md5 = MessageDigest.getInstance("MD5");//申明使用MD5算法
                md5.update("a".getBytes());
               //update方法处理数据 reset 重置
                System.out.println("md5(a)="+byte2str(md5.digest()));
                md5.update("a".getBytes());
                md5.update("bc".getBytes());
                System.out.println("md5(abc)="+byte2str(md5.digest()));
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    
    import java.security.MessageDigest;
    
    public class MyMD5 {
    
        static char[] hex = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
        public static void main(String[] args) {
            try{
                MessageDigest md5 = MessageDigest.getInstance("MD5");//申明使用MD5算法
                md5.update("a".getBytes());//
                System.out.println("md5(a)="+byte2str(md5.digest()));
                md5.update("a".getBytes());
                md5.update("bc".getBytes());
                System.out.println("md5(abc)="+byte2str(md5.digest()));
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    
         //将字节数组转换成十六进制字符串  
        private static String byte2str(byte []bytes){
            int len = bytes.length;   
            StringBuffer result = new StringBuffer();    
            for (int i = 0; i < len; i++) {   
                byte byte0 = bytes[i];   
                result.append(hex[byte0 >>> 4 & 0xf]);   
                result.append(hex[byte0 & 0xf]);   
            }
            return result.toString();
        }
    } 
         * 将字节数组转换成十六进制字符串
         * @param bytes
         * @return
         */
        private static String byte2str(byte []bytes){
            int len = bytes.length;   
            StringBuffer result = new StringBuffer();    
            for (int i = 0; i < len; i++) {   
                byte byte0 = bytes[i];   
                result.append(hex[byte0 >>> 4 & 0xf]);   
                result.append(hex[byte0 & 0xf]);   
            }
            return result.toString();
        }
    } 
    
    更多相关内容
  • MD5.h #ifndef MD5_H #define MD5_H #include #include <fstream> /* Type define */ typedef unsigned char byte; typedef unsigned int uint32; typedef unsigned int uint4; using std::string; using std::...
  • md5.h #ifndef _MD5_H_ #define _MD5_H_ #include #include using namespace std; class MD5 { public: typedef unsigned char uchar8; //make sure it is 8bit typedef char char8; //make sure
  • 用matlab实现的一个md5算法,有详细的注释说明
  • md5算法实现

    2015-04-25 19:35:35
    md5算法实现
  • 完整版数据摘要模块(MD5算法实现) 飞扬工作室.rar
  • 数据摘要模块(MD5算法实现) 飞扬工作室.rar
  • 数据摘要模块(MD5算法实现) 飞扬工作室.zip易语言程序源码资源下载数据摘要模块(MD5算法实现) 飞扬工作室.zip易语言程序源码资源下载 1.适合学生做毕业设计用 2.适合程序员学习研究用 3.适合新手自学研究使用
  • 主要介绍了JavaScript实现MD5算法,以完整实例形式分析了基于JavaScript实现MD5算法的具体步骤与相关技巧,需要的朋友可以参考下
  • MD5算法实现DEMO.7z

    2019-07-01 13:49:31
    MD5算法实现代码和DEMO,使用C++编写。
  • 在逆向程序的时候,经常会碰到加密的算法的问题,前面分析UC的逆向工程师的面试题2的时候,发现使用了MD5的加密算法MD5算法是自己实现的,不是使用的算法库函数)。尤其是在逆向分析网络协议的时候,一般的程序...
  • 易语言模块数据摘要模块(MD5算法实现) 飞扬工作室.rar
  • MD5算法实现

    2017-11-13 10:46:22
    C语言实现MD5算法,帮助大家快速的运用该算法,希望对大家有所帮助。
  • MD5算法实现

    2012-05-24 22:07:16
    C:\Program Files\Microsoft Visual Studio\MyProjects\11\1.cpp ,由C/C++实现MD5算法
  • MD5算法 C语言实现

    2016-07-08 13:54:02
    MD5算法C语言实现
  • C语言MD5算法实现

    2013-10-24 14:58:41
    C语言实现MD5,经测试验证,散列过程完全正确,可供开发者使用!
  • 主要介绍了java 实现MD5加密算法的简单实例的相关资料,这里提供实例帮助大家应用这样的加密算法,需要的朋友可以参考下
  • lwip协议栈源码提取 链接:https://pan.baidu.com/s/1Lcwap2cC0fNd7FhY44JhPQ 提取码:w74p
  • 经典T-SQL-MD5算法实现

    2013-08-06 16:10:33
    能过sql server TSQL 实现MD5算法过程函数。
  • MD5算法实现MD5算法实现MD5算法实现MD5算法实现MD5算法实现MD5算法实现MD5算法实现MD5算法实现MD5算法实现MD5算法实现
  • RSA算法和MD5算法实现的数字签名RSA算法和MD5算法实现的数字签名RSA算法和MD5算法实现的数字签名
  • MD5算法实现的C代码

    2009-05-23 15:17:16
    VC++ 环境下实现MD5算法,能处理字符串
  • 本篇文章主要介绍了Java实现MD5消息摘要算法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 描述了MD5算法实现的原理,并通过程序实现md5算法功能
  • MD5算法C++实现

    2016-08-27 11:11:07
    MD5算法的C++实现的代码,提供给需要的同学,主要的目的方便自己逆向工程的时候要用到,方便查找。
  • 按照MD5摘要算法在matlab上实现MD5算法
  • C语言MD5算法实现(基于Mbedtls)

    千次阅读 2020-01-21 14:40:22
    最近项目中需要通过C语言实现MD5算法,这里我通过Mbedtls库来进行实现。 1、下载Mbedtls 首先我们将Mbedtls代码放入到工程中,相关传送门如下: Mbedtls官方下载地址 官方网址是国外的下载慢,所以也附上本文...

    最近项目中需要通过C语言实现MD5算法,这里我通过Mbedtls库来进行实现。

    1、下载Mbedtls

    首先我们将Mbedtls代码放入到工程中,相关传送门如下:

    Mbedtls官方下载地址

    官方网址是国外的下载慢,所以也附上本文使用到的Mbedtls代码,传送门如下:

    Mbedtls加解密工具代码

    2、引入Mbedtls头文件

    这里我们在工程中的CMakeLists.txt中引入Mbedtls的头文件,代码如下:

    # for debug
    # add_compile_options(-g)
    
    project("device-authentication")
    
    cmake_minimum_required(VERSION 3.5)
    
    INCLUDE_DIRECTORIES(
    	../include/
        ../../src/net/mbedtls/include
    	../../src/smalgo/sms4/include
    )
    
    SET(my_src_crypto
        ../../src/net/mbedtls/library/aes.c
        ../../src/net/mbedtls/library/aesni.c
        ../../src/net/mbedtls/library/base64.c
        ../../src/net/mbedtls/library/rsa.c
        ../../src/net/mbedtls/library/rsa_internal.c
        ../../src/net/mbedtls/library/entropy.c
        ../../src/net/mbedtls/library/entropy_poll.c
        ../../src/net/mbedtls/library/bignum.c
        ../../src/net/mbedtls/library/sha1.c
        ../../src/net/mbedtls/library/sha256.c
        ../../src/net/mbedtls/library/sha512.c
        ../../src/net/mbedtls/library/md.c
        ../../src/net/mbedtls/library/md5.c
        ../../src/net/mbedtls/library/md_wrap.c
        ../../src/net/mbedtls/library/ripemd160.c
        ../../src/net/mbedtls/library/platform_util.c
        ../../src/net/mbedtls/library/oid.c
        ../../src/net/mbedtls/library/timing.c
        ../../src/net/mbedtls/library/net_sockets.c
    	../../src/smalgo/sms4/cbc128.c
    	../../src/smalgo/sms4/sms4_cbc.c
    	../../src/smalgo/sms4/sms4_common.c
    	../../src/smalgo/sms4/sms4_enc.c
    	../../src/smalgo/sms4/sms4_setkey.c
    )
    SET(my_src_crypto_dbg
        ../../src/net/mbedtls/library/ctr_drbg.c
    )
    
    SET(SRC_LIST_ENCRYPT_BIN
    		oem_porting.c
    		sdk_porting.c
    		authref.c
    		test.c
    		${my_src_crypto}
    		${my_src_crypto_dbg}
    		)
    
    SET(SRC_LIST_DECRYPT_LIB 
        oem_porting.c
        sdk_porting.c
        authref.c
        auth.c
        ${my_src_crypto}
        ${my_src_crypto_dbg}
    		)
    
    #SET(SRC_LIST_AUTH_DEV
    #    oem_porting.c
    #    sdk_porting.c
    #    authref.c
    #    ${my_src_crypto}
    #    ${my_src_crypto_dbg}
    #)
    
    add_definitions(-fPIC)
    #ADD_LIBRARY(authd STATIC ${SRC_LIST_AUTH_DEV})
    ADD_LIBRARY(authoal STATIC ${SRC_LIST_DECRYPT_LIB})
    ADD_EXECUTABLE(eaidkAuth ${SRC_LIST_ENCRYPT_BIN})

    工程结构如下:

       

    引入完成之后我们就可以开始MD5代码编写。

    3、Md5代码编写

    authref.h 代码如下:

    #ifndef __AUTHREF_H__
    #define __AUTHREF_H__
    #include <assert.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #undef DEBUG
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    //md5加密
    uint8_t md5_enc(uint8_t *encrypt, uint8_t *decrypt);
    
    #ifdef __cplusplus
    }
    #endif
    #endif //__AUTHREF_H__
    

    authref.c 代码如下:

    #include "authref.h"
    #include "mbedtls/entropy.h"
    #include "mbedtls/ctr_drbg.h"
    #include "mbedtls/md5.h"
    
    uint8_t md5_enc(uint8_t *encrypt, uint8_t *decrypt) {
        mbedtls_md5_context md5_ctx;
        mbedtls_md5_init(&md5_ctx);
        mbedtls_md5_starts(&md5_ctx);
        mbedtls_md5_update(&md5_ctx, encrypt, strlen((char *) encrypt));
        mbedtls_md5_finish(&md5_ctx, decrypt);
    }

    到此我们就实现了基于Mbedtls的MD5算法。

    展开全文
  • MD5算法原理与常用实现

    千次阅读 2020-04-16 23:39:27
    所以MD5MD算法家族中,目前最常用的一种加密算法。 任何信息,都可以通过MD5算法运算生成一个16字节(128位)的散列值,但却无法通过这16个字节的散列值获得加密前的信息。 这就是MD5最重要的一个特性:加密不可逆...

    定义

    MD全称Message-Digest,即信息摘要,所以MD家族的算法也叫信息摘要算法
    MD家族有MD2、MD3、MD4、MD5,一代比一代强。
    所以MD5是MD算法家族中,目前最常用的一种加密算法。

    任何信息,都可以通过MD5算法运算生成一个16字节(128位)的散列值,但却无法通过这16个字节的散列值获得加密前的信息。
    最终这16个散列值,通常用一个长度为32的十六进制字符串来表示。
    这就是MD5最重要的一个特性:加密不可逆。

    MD5特点

    加密不可逆,即无法通过密文得到原文。
    不变性,即相同的原文,通过MD5算法得到的密文总是相同的。
    散列性,即对原文作轻微的改动,都可导致最终的密文完全改变。

    常见应用场景

    1、校验文件的完整性

    如果张三给李四传了一个文件,如何确认这个文件传给李四是完整的呢
    张三传文件前,先对文件做一个MD5加密,同时把MD5加密的密文传给李四
    李四收到文件,也对该文件做MD5加密,如果得到的密文和张三给的密文一样,就说明文件是完整的。

    2、存储用户密码

    用户密码,理论上也不能直接明文存储在数据库中,因为一旦数据库被破解,用户的密码就全部丢失了
    所以可以将用户密码做一个MD5加密,然后将密文存在数据库中
    用户登录的时候,可以将用户的密码进行MD5加密,然后比对密文和数据库中的密文是否一致,来判断用户前台填的密码是否正确。
    这只是一个思路,一般不会这么简单,一般生产环境会对用户密码加盐加密等等的处理,用户信息更加重要的,则需要更加复杂的计算逻辑。

    原理

    MD5的加密过程,整体来看,就是先定义四个值,然后用这四个值,对原文信息进行计算,并得到新的四个值,然后再对原文进行计算,再得到新的四个值,如此循环一定次数,最终对最后的这四个值进行简单的字符串拼接,就得到了最终的密文。
    主要就是下面这3步:

    1、填补信息

    用原文长度位数对512求余,如果结果不为448,就填充到448位。填充是第一位填1,后面填0。512-448=64,用这剩余的64位,记录原文长度。
    最终得到一个填补完的信息(总长=原文长度+512位)

    2、拿到初始值

    四个初始值,是MD5这个算法提前定义好的,分别是4个32位的值,总共刚好128位。
    我们用ABCD命名:
    A=0x01234567
    B=0x89ABCDEF
    C=0xFEDCBA98
    D=0x76543210

    3、真正的计算

    计算分为多次循环,每次循环,都是用ABCD和原文在第一步填补完的信息,进行计算,最终得到新的ABCD。最后将最后一次ABCD拼成字符串,就是最终的密文。
    循环先分为主循环,每个主循环中又套有子循环。
    主循环次数 = 原文长度/512。
    子循环次数 = 64次。

    我们看看单次子循环都做了什么:

    下面是单次子循环真正的计算逻辑(这段实现摘自网友):
    在这里插入图片描述
    图中,A,B,C,D就是哈希值的四个分组。每一次循环都会让旧的ABCD产生新的ABCD。一共进行多少次循环呢?由处理后的原文长度决定。

    假设处理后的原文长度是M
    主循环次数 = M / 512
    每个主循环中包含 512 / 32 * 4 = 64 次 子循环。

    上面这张图所表达的就是单次子循环的流程。

    下面对图中其他元素一一解释:

    1.绿色F
    图中的绿色F,代表非线性函数。官方MD5所用到的函数有四种:

    F(X, Y, Z) =(X&Y) | ((~X) & Z)
    G(X, Y, Z) =(X&Z) | (Y & (~Z))
    H(X, Y, Z) =X^Y^Z
    I(X, Y, Z)=Y^(X|(~Z))
    

    在主循环下面64次子循环中,F、G、H、I 交替使用,第一个16次使用F,第二个16次使用G,第三个16次使用H,第四个16次使用I。

    2.红色“田”字
    很简单,红色的田字代表相加的意思。

    3.Mi
    Mi是第一步处理后的原文。在第一步中,处理后原文的长度是512的整数倍。把原文的每512位再分成16等份,命名为M0 ~ M15,每一等份长度32。在64次子循环中,每16次循环,都会交替用到M1 ~ M16之一。

    4.Ki
    一个常量,在64次子循环中,每一次用到的常量都是不同的。

    5.黄色的<<
    FF(a,b,c,d,Mj,s,ti)表示a=b+((a+F(b,c,d)+Mj+ti)<<<s)
    <<<s表示循环左移s位

    第一轮
     a=FF(a,b,c,d,M0,7,0xd76aa478)
     b=FF(d,a,b,c,M1,12,0xe8c7b756)
     c=FF(c,d,a,b,M2,17,0x242070db)
     d=FF(b,c,d,a,M3,22,0xc1bdceee)
     a=FF(a,b,c,d,M4,7,0xf57c0faf)
     b=FF(d,a,b,c,M5,12,0x4787c62a)
     c=FF(c,d,a,b,M6,17,0xa8304613)
     d=FF(b,c,d,a,M7,22,0xfd469501)
     a=FF(a,b,c,d,M8,7,0x698098d8)
     b=FF(d,a,b,c,M9,12,0x8b44f7af)
     c=FF(c,d,a,b,M10,17,0xffff5bb1)
     d=FF(b,c,d,a,M11,22,0x895cd7be)
     a=FF(a,b,c,d,M12,7,0x6b901122)
     b=FF(d,a,b,c,M13,12,0xfd987193)
     c=FF(c,d,a,b,M14,17,0xa679438e)
     d=FF(b,c,d,a,M15,22,0x49b40821)
     
    第二轮
     a=GG(a,b,c,d,M1,5,0xf61e2562)
     b=GG(d,a,b,c,M6,9,0xc040b340)
     c=GG(c,d,a,b,M11,14,0x265e5a51)
     d=GG(b,c,d,a,M0,20,0xe9b6c7aa)
     a=GG(a,b,c,d,M5,5,0xd62f105d)
     b=GG(d,a,b,c,M10,9,0x02441453)
     c=GG(c,d,a,b,M15,14,0xd8a1e681)
     d=GG(b,c,d,a,M4,20,0xe7d3fbc8)
     a=GG(a,b,c,d,M9,5,0x21e1cde6)
     b=GG(d,a,b,c,M14,9,0xc33707d6)
     c=GG(c,d,a,b,M3,14,0xf4d50d87)
     d=GG(b,c,d,a,M8,20,0x455a14ed)
     a=GG(a,b,c,d,M13,5,0xa9e3e905)
     b=GG(d,a,b,c,M2,9,0xfcefa3f8)
     c=GG(c,d,a,b,M7,14,0x676f02d9)
     d=GG(b,c,d,a,M12,20,0x8d2a4c8a)
     
    第三轮
     a=HH(a,b,c,d,M5,4,0xfffa3942)
     b=HH(d,a,b,c,M8,11,0x8771f681)
     c=HH(c,d,a,b,M11,16,0x6d9d6122)
     d=HH(b,c,d,a,M14,23,0xfde5380c)
     a=HH(a,b,c,d,M1,4,0xa4beea44)
     b=HH(d,a,b,c,M4,11,0x4bdecfa9)
     c=HH(c,d,a,b,M7,16,0xf6bb4b60)
     d=HH(b,c,d,a,M10,23,0xbebfbc70)
     a=HH(a,b,c,d,M13,4,0x289b7ec6)
     b=HH(d,a,b,c,M0,11,0xeaa127fa)
     c=HH(c,d,a,b,M3,16,0xd4ef3085)
     d=HH(b,c,d,a,M6,23,0x04881d05)
     a=HH(a,b,c,d,M9,4,0xd9d4d039)
     b=HH(d,a,b,c,M12,11,0xe6db99e5)
     c=HH(c,d,a,b,M15,16,0x1fa27cf8)
     d=HH(b,c,d,a,M2,23,0xc4ac5665)
     
    第四轮
     a=II(a,b,c,d,M0,6,0xf4292244)
     b=II(d,a,b,c,M7,10,0x432aff97)
     c=II(c,d,a,b,M14,15,0xab9423a7)
     d=II(b,c,d,a,M5,21,0xfc93a039)
     a=II(a,b,c,d,M12,6,0x655b59c3)
     b=II(d,a,b,c,M3,10,0x8f0ccc92)
     c=II(c,d,a,b,M10,15,0xffeff47d)
     d=II(b,c,d,a,M1,21,0x85845dd1)
     a=II(a,b,c,d,M8,6,0x6fa87e4f)
     b=II(d,a,b,c,M15,10,0xfe2ce6e0)
     c=II(c,d,a,b,M6,15,0xa3014314)
     d=II(b,c,d,a,M13,21,0x4e0811a1)
     a=II(a,b,c,d,M4,6,0xf7537e82)
     b=II(d,a,b,c,M11,10,0xbd3af235)
     c=II(c,d,a,b,M2,15,0x2ad7d2bb)
     d=II(b,c,d,a,M9,21,0xeb86d391)
    

    MD5为什么不可逆

    MD5不可逆的原因,从原理上来看,
    第一是他使用了散列函数,即上面的FGHI函数。
    第二是他在里面用了大量的移位操作,即<<<,这些是不可逆的
    比如有10110011,我们左移三位,变成了10011000,高三位的101被顶了,低三位用0代替了,那此时就绝对不可能用10011000再逆向得到10110011了。

    java实现和使用

    public class MD5Util {
        public static void main(String[] args) throws IOException {
            System.out.println(encodeString("123"));
        }
    
        public static String encodeString(String plainText) throws UnsupportedEncodingException {
            return encodeBytes(plainText.getBytes("UTF-8"));
        }
    
        public static String encodeBytes(byte[] bytes) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update(bytes);
                byte b[] = md.digest();
    
                int i;
    
                StringBuffer buf = new StringBuffer("");
                for (int offset = 0; offset < b.length; offset++) {
                    i = b[offset];
                    if (i < 0) {
                        i += 256;
                    }
                    if (i < 16) {
                        buf.append("0");
                    }
                    buf.append(Integer.toHexString(i));
                }
                return buf.toString();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "";
        }
    
    }
    
    展开全文
  • MD5算法的Java实现

    万次阅读 多人点赞 2018-12-03 20:19:31
    MD5 即Message-Digest Algorithm 5 (信息-摘要算法5) MD4 (1990)、MD5(1992, RFC 1321) 由Ron Rivest发明,是广泛 使用的Hash 算法,用于确保信息传输的完整性和一致性。 MD5 使用little-endian(小端模式),输入...

    一、算法原理概述

    MD5 即Message-Digest Algorithm 5 (信息-摘要算法5)

    • MD4 (1990)、MD5(1992, RFC 1321) 由Ron Rivest发明,是广泛 使用的Hash 算法,用于确保信息传输的完整性和一致性。
    • MD5 使用little-endian(小端模式),输入任意不定长度信息,以 512-bit 进行分组,生成四个32-bit 数据,最后联合输出固定 128-bit 的信息摘要。
    • MD5 算法的基本过程为:填充、分块、缓冲区初始化、循环压 缩、得出结果。
    • MD5 不是足够安全的。
      • Hans Dobbertin在1996年找到了两个不同的512-bit 块,它们 在MD5 计算下产生相同的hash 值。
      • 至今还没有真正找到两个不同的消息,它们的MD5 的hash 值相等。

    基本流程

    1543732734508

    1543733052946

    填充padding

    • 在长度为Kbits 的原始消息数据尾部填充长度为Pbits 的标识 100…0,1< P < 512 (即至少要填充1个bit),使得填充后的消息位 数为:K + P=448 (mod 512).
      • 注意到当K=448 (mod 512) 时,需要P= 512.
    • 再向上述填充好的消息尾部附加K 值的低64位(即K mod 264), 最后得到一个长度位数为K + P+ 64= 0 (mod 512) 的消息。

    分块

    • 把填充后的消息结果分割为L个512-bit 分组:Y0, Y1, …, YL-1。
    • 分组结果也可表示成N个32-bit 字M0, M1, …, MN-1,N= Lx16。

    初始化

    • 初始化一个128-bit 的MD 缓冲区,记为CVq,表示成4个32-bit 寄存器(A, B, C,D);CV0= IV。迭代在MD 缓冲区进行,最后一 步的128-bit 输出即为算法结果。

    压缩函数

    1543733261546

    每轮循环中的一次迭代运算逻辑

    1. 对A迭代:a <— b+ ((a+ g(b, c, d) + X[k] + T[i]) <<<s)
    2. 缓冲区(A, B, C, D) 作循环轮换: (B, C, D, A) <—(A, B, C, D)

    说明:

    • a, b, c, d: MD 缓冲区(A, B, C, D) 的当前值。
    • g: 轮函数(F, G, H, I中的一个)。
    • <<<s: 将32位输入循环左移(CLS) s位。
    • X[k] : 当前处理消息分组的第k个(k= 0…15) 32位字,即 M(qx16+k)。
    • T[i] : T表的第i个元素,32位字;T表总共有64个元素,也 称为加法常数。
      • : 模232 加法。

    二、总体结构

    public class MD5 {
    	//一系列常量数值
        
        //开始使用MD5加密
        private String start(String message){
            //分块
            
            //填充
            
            //即小于448bit的情况,先填充100...0再填充K值的低64位
            //此时只会新增一个分组
            if(rest <= 56){
                //填充100...0
                //填充K值低64位
                //处理分组
                 H(divide(inputBytes, i*64));
            //即大于448bit的情况,先填充100...0再填充K值的低64位
            //此时会新增两个分组
            }else{
                //填充10000000
                //填充00000000
                //填充低64位
                //处理第一个尾部分组
                H(divide(inputBytes, i*64));
                //处理第二个尾部分组
                 H(divide(inputBytes, i*64));
            }
            //将Hash值转换成十六进制的字符串
            //小端方式!
        }
        
        //从inputBytes的index开始取512位,作为新的512bit的分组
        private static long[] divide(byte[] inputBytes,int start){
        	//存储一整个分组,就是512bit,数组里每个是32bit,就是4字节,为了消除符号位的影响,所以使用long
            
        }
        
        // groups[] 中每一个分组512位(64字节)
        // MD5压缩函数
        private void H(long[] groups) {
        	//缓冲区(寄存器)数组
            //四轮循环
            for(int n = 0; n < 4; n++) {
            	//16轮迭代
            	for(int i = 0; i < 16; i++) {
                }
            }
            //加入之前计算的结果
            //防止溢出
        }
        public static void main(String []args){
            MD5 md=new MD5();
            String message = "helloMD5";
            System.out.println("MD5-Algorithm:\n\nOrigin Message: " + message);
            System.out.println("Result Message: " + md.start(message));
            System.out.println("Result Message(UpperCase): " + md.resultMessage.toUpperCase());
            //F0F99260B5A02508C71F6D81C15E9A44
            //3ED9E5F6855DBCDBCD95AC6C4FE0C0A5
        }
    }
    

    三、模块分解

    填充

    填充时有两种情况

    • 如果去掉整数个512bit的小组,剩下的位数不超过448bit,那样就可以先填充100…00(如果正好是448bit就不用填充了),再填充用K值的低64位64bit,那样的话就只是新增了一个小组
    • 如果去掉整数个512bit的小组,剩下的位数超过了448bit,那样不够填充64bit,所以需要填充100…00到512bit,构成一个小组;然后再在第二个小组填充448bit个0,最后填充K值的低64位bit,这样就会新增两个小组
    //填充
            int rest = byteLen % 64;
            //即将填充的一个分组
            byte [] paddingBytes=new byte[64];
            //原来的尾部数据
            for(int i=0;i<rest;i++)
            	paddingBytes[i]=inputBytes[byteLen-rest+i];
            //即小于448bit的情况,先填充100...0再填充K值的低64位
            //此时只会新增一个分组
            if(rest <= 56){
                //填充100...0
                if(rest<56){
                	//填充10000000
                	paddingBytes[rest]=(byte)(1<<7);
                	//填充00000000
                    for(int i=1;i<56-rest;i++)
                    	paddingBytes[rest+i]=0;
                }
                //填充K值低64位
                for(int i=0;i<8;i++){
                	paddingBytes[56+i]=(byte)(K&0xFFL);
                    K=K>>8;
                }
                //处理分组
                H(divide(paddingBytes,0));
            //即大于448bit的情况,先填充100...0再填充K值的低64位
            //此时会新增两个分组
            }else{
                //填充10000000
                paddingBytes[rest]=(byte)(1<<7);
                //填充00000000
                for(int i=rest+1;i<64;i++)
                	paddingBytes[i]=0;
                //处理第一个尾部分组
                H(divide(paddingBytes,0));
                
                //填充00000000
                for(int i=0;i<56;i++)
                	paddingBytes[i]=0;
    
                //填充低64位
                for(int i=0;i<8;i++){
                	//这里很关键,使用小端方式,即Byte数组先存储len的低位数据,然后右移len
                	paddingBytes[56+i]=(byte)(K&0xFFL);
                    K=K>>8;
                }
                //处理第二个尾部分组
                H(divide(paddingBytes,0));
            }
    

    分块

    这里调用了divide分组函数,将完整小组分成个数为16,单个元素为32bit的数组

    再直接调用H压缩函数,进行四轮循环和16次迭代

    //分块
    //完整小组(512bit)(64byte)的个数
            int groupCount = byteLen/64;
            for(int i = 0;i < groupCount;i++){
            	//每次取512bit
                //处理一个分组
                H(divide(inputBytes, i*64));
            }
    

    分组函数

    这个函数传入的参数是一个字节数组,以及一个代表从哪里开始截取的int

    效果就是将这个字节数组从start开始的64个字节组成一个

    元素个数为16,单个元素为32bit的数组

    采用的方法是每次取四个字节,采用小端的方式拼接成一个long型的整数

    因为int在某些情况下是4个字节,所以就是正好32bit,但是带符号,就影响后面数据的运算

    //从inputBytes的index开始取512位,作为新的512bit的分组
        private static long[] divide(byte[] inputBytes,int start){
        	//存储一整个分组,就是512bit,数组里每个是32bit,就是4字节,为了消除符号位的影响,所以使用long
            long [] group=new long[16];
            for(int i=0;i<16;i++){
            	//每个32bit由4个字节拼接而来
            	//小端的从byte数组到bit恢复方法
                group[i]=byte2unsign(inputBytes[4*i+start])|
                    (byte2unsign(inputBytes[4*i+1+start]))<<8|
                    (byte2unsign(inputBytes[4*i+2+start]))<<16|
                    (byte2unsign(inputBytes[4*i+3+start]))<<24;
            }
            return group;
        }
    

    MD5压缩函数

    用了两层循环

    第一层表示四轮循环

    第二层表示16轮迭代

    中间按照缓冲区运算的要求处理数据

    这里直接处理的是result数组,也就是真实的缓冲区,所以在开始暂存了它们当时的值为a,b,c,d

    运算完毕后要加上这些值

    运算过程中以及运算结束会有一些防溢出的操作

    (有时候没有这个就会出错)

    // groups[] 中每一个分组512位(64字节)
        // MD5压缩函数
        private void H(long[] groups) {
        	//缓冲区(寄存器)数组
            long a = result[0], b = result[1], c = result[2], d = result[3];
            //四轮循环
            for(int n = 0; n < 4; n++) {
            	//16轮迭代
            	for(int i = 0; i < 16; i++) {
                	result[0] += (g(n, result[1], result[2], result[3])&0xFFFFFFFFL) + groups[k[n][i]] + T[n][i];
                    result[0] = result[1] + ((result[0]&0xFFFFFFFFL)<< S[n][i % 4] | ((result[0]&0xFFFFFFFFL) >>> (32 - S[n][i % 4])));
                    //循环轮换
                    long temp = result[3];
                    result[3] = result[2];
                    result[2] = result[1];
                    result[1] = result[0];
                    result[0] = temp;
                }
            }
            //加入之前计算的结果
            result[0] += a;
            result[1] += b;
            result[2] += c;
            result[3] += d;
            //防止溢出
            for(int n = 0; n < 4 ; n++) {
            	result[n] &=0xFFFFFFFFL;
            }
        }
    

    最后结果转换为字符串

    之前得到的结果就是result数组,四个元素,每个元素是一个32bit的数据

    现在要把他们转换为字符串

    但是需要小端的处理方式

    long的低位作为字符串的高位

    每次以一个字节处理,32bit四个字节分别通过与运算和移位运算分离出来,再让long的低位在前,高位在后,得到十六进制字符串就是MD5编码的结果

    //将Hash值转换成十六进制的字符串
            //小端方式!
            for(int i=0;i<4;i++){
            	resultMessage += Long.toHexString(result[i] & 0xFF) +
                		Long.toHexString((result[i] & 0xFF00) >> 8) +
                		Long.toHexString((result[i] & 0xFF0000) >> 16) +
                		Long.toHexString((result[i] & 0xFF000000) >> 24);
      
            }
    

    四、数据结构

    long []groups 存储小组

    String resultMessage存储结果字符串

    //四个寄存器的初始向量IV,采用小端存储
    static final long A=0x67452301L
    static final long B=0xefcdab89L
    static final long C=0x98badcfeL
    static final long D=0x10325476L

    private long [] result={A,B,C,D}; 模拟存储结果的四个寄存器

    static final long T[][]迭代过程中采用的近似值int(2^32 x |sin(i)|)

    static final int k[][] 表示X[k]中的的k取值,决定如何使用消息分组中的字

    static final int S[][] 各次迭代中采用的做循环移位的s值

    private static long g(int i, long b, long c, long d) 4轮循环中使用的生成函数(轮函数)g

    五、运行结果

    输入原始消息:helloMD5

    得到编码结果为:3ED9E5F6855DBCDBCD95AC6C4FE0C0A5

    在这里插入图片描述

    站长之家的工具验证,结果正确1543732370194

    六、源代码

    /**
     * @author Janking
     */
    public class MD5 {
        //存储小组
        long[] groups = null;
        //存储结果
        String resultMessage = "";
    
        //四个寄存器的初始向量IV,采用小端存储
        static final long A = 0x67452301L;
        static final long B = 0xefcdab89L;
        static final long C = 0x98badcfeL;
        static final long D = 0x10325476L;
    
        //java不支持无符号的基本数据(unsigned),所以选用long数据类型
        private long[] result = {A, B, C, D};
    
        static final long T[][] = {
                {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
                        0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
                        0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
                        0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821},
    
                {0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
                        0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
                        0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
                        0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a},
    
                {0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
                        0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
                        0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
                        0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665},
    
                {0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
                        0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
                        0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
                        0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}};
        //表示X[k]中的的k取值,决定如何使用消息分组中的字
        static final int k[][] = {
                {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
                {1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12},
                {5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2},
                {0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9}};
    
        //各次迭代中采用的做循环移位的s值
        static final int S[][] = {
                {7, 12, 17, 22},
                {5, 9, 14, 20},
                {4, 11, 16, 23},
                {6, 10, 15, 21}};
    
        //4轮循环中使用的生成函数(轮函数)g
        private static long g(int i, long b, long c, long d) {
            switch (i) {
                case 0:
                    return (b & c) | ((~b) & d);
                case 1:
                    return (b & d) | (c & (~d));
                case 2:
                    return b ^ c ^ d;
                case 3:
                    return c ^ (b | (~d));
                default:
                    return 0;
            }
        }
    
        //开始使用MD5加密
        private String start(String message) {
            //转化为字节数组
            byte[] inputBytes = message.getBytes();
            //6A 61 6E 6b 69 6e 67
            //获取字节数组的长度
            int byteLen = inputBytes.length;
            //得到K值(以bit作单位的message长度)
            long K = (long) (byteLen << 3);
            //完整小组(512bit)(64byte)的个数
            int groupCount = byteLen / 64;
    
            //分块
            for (int i = 0; i < groupCount; i++) {
                //每次取512bit
                //处理一个分组
                H(divide(inputBytes, i * 64));
            }
    
            //填充
            int rest = byteLen % 64;
            //即将填充的一个分组
            byte[] paddingBytes = new byte[64];
            //原来的尾部数据
            for (int i = 0; i < rest; i++)
                paddingBytes[i] = inputBytes[byteLen - rest + i];
            //即小于448bit的情况,先填充100...0再填充K值的低64位
            //此时只会新增一个分组
            if (rest <= 56) {
                //填充100...0
                if (rest < 56) {
                    //填充10000000
                    paddingBytes[rest] = (byte) (1 << 7);
                    //填充00000000
                    for (int i = 1; i < 56 - rest; i++)
                        paddingBytes[rest + i] = 0;
                }
                //填充K值低64位
                for (int i = 0; i < 8; i++) {
                    paddingBytes[56 + i] = (byte) (K & 0xFFL);
                    K = K >> 8;
                }
                //处理分组
                H(divide(paddingBytes, 0));
                //即大于448bit的情况,先填充100...0再填充K值的低64位
                //此时会新增两个分组
            } else {
                //填充10000000
                paddingBytes[rest] = (byte) (1 << 7);
                //填充00000000
                for (int i = rest + 1; i < 64; i++)
                    paddingBytes[i] = 0;
                //处理第一个尾部分组
                H(divide(paddingBytes, 0));
    
                //填充00000000
                for (int i = 0; i < 56; i++)
                    paddingBytes[i] = 0;
    
                //填充低64位
                for (int i = 0; i < 8; i++) {
                    //这里很关键,使用小端方式,即Byte数组先存储len的低位数据,然后右移len
                    paddingBytes[56 + i] = (byte) (K & 0xFFL);
                    K = K >> 8;
                }
                //处理第二个尾部分组
                H(divide(paddingBytes, 0));
            }
            //将Hash值转换成十六进制的字符串
            //小端方式!
            for (int i = 0; i < 4; i++) {
                //解决缺少前置0的问题
                resultMessage += String.format("%02x", result[i] & 0xFF) +
                        String.format("%02x", (result[i] & 0xFF00) >> 8) +
                        String.format("%02x", (result[i] & 0xFF0000) >> 16) +
                        String.format("%02x", (result[i] & 0xFF000000) >> 24);
    
            }
            return resultMessage;
        }
    
        //从inputBytes的index开始取512位,作为新的512bit的分组
        private static long[] divide(byte[] inputBytes, int start) {
            //存储一整个分组,就是512bit,数组里每个是32bit,就是4字节,为了消除符号位的影响,所以使用long
            long[] group = new long[16];
            for (int i = 0; i < 16; i++) {
                //每个32bit由4个字节拼接而来
                //小端的从byte数组到bit恢复方法
                group[i] = byte2unsign(inputBytes[4 * i + start]) |
                        (byte2unsign(inputBytes[4 * i + 1 + start])) << 8 |
                        (byte2unsign(inputBytes[4 * i + 2 + start])) << 16 |
                        (byte2unsign(inputBytes[4 * i + 3 + start])) << 24;
            }
            return group;
        }
    
        //其实byte相当于一个字节的有符号整数,这里不需要符号位,所以把符号位去掉
        public static long byte2unsign(byte b) {
            return b < 0 ? b & 0x7F + 128 : b;
        }
    
        // groups[] 中每一个分组512位(64字节)
        // MD5压缩函数
        private void H(long[] groups) {
            //缓冲区(寄存器)数组
            long a = result[0], b = result[1], c = result[2], d = result[3];
            //四轮循环
            for (int n = 0; n < 4; n++) {
                //16轮迭代
                for (int i = 0; i < 16; i++) {
                    result[0] += (g(n, result[1], result[2], result[3]) & 0xFFFFFFFFL) + groups[k[n][i]] + T[n][i];
                    result[0] = result[1] + ((result[0] & 0xFFFFFFFFL) << S[n][i % 4] | ((result[0] & 0xFFFFFFFFL) >>> (32 - S[n][i % 4])));
                    //循环轮换
                    long temp = result[3];
                    result[3] = result[2];
                    result[2] = result[1];
                    result[1] = result[0];
                    result[0] = temp;
                }
            }
            //加入之前计算的结果
            result[0] += a;
            result[1] += b;
            result[2] += c;
            result[3] += d;
            //防止溢出
            for (int n = 0; n < 4; n++) {
                result[n] &= 0xFFFFFFFFL;
            }
        }
    
        public static void main(String[] args) {
            MD5 md = new MD5();
            String message = "helloMD5";
            System.out.println("MD5-Algorithm:\n\nOrigin Message: " + message);
            System.out.println("Result Message: " + md.start(message));
            System.out.println("Result Message(UpperCase): " + md.resultMessage.toUpperCase());
            //Origin Message: helloMD5
            //Result Message: 3ed9e5f6855dbcdbcd95ac6c4fe0c0a5
            //Result Message(UpperCase): 3ED9E5F6855DBCDBCD95AC6C4FE0C0A5
        }
    }
    
    

    七、参考资料

    MD5算法原理

    DES算法实现

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 137,973
精华内容 55,189
关键字:

md5算法实现

友情链接: u_execmem.rar