MD5加密概述,原理及实现-程序员宅基地

技术标签: 笔记  MD5  

MD5概述:

MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要(32位的数字字母混合码)。

MD5主要特点:

不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样

(一个MD5理论上的确是可能对应无数多个原文的,因为MD5是有限多个的而原文可以是无数多个。比如主流使用的MD5将任意长度的“字节串映射为一个128bit的大整数。也就是一共有2^128种可能,大概是3.4*10^38,这个数字是有限多个的,而但是世界上可以被用来加密的原文则会有无数的可能性)

MD5的性质:

1、压缩性:任意长度的数据,算出的MD5值长度都是固定的(相当于超损压缩)。

2、容易计算:从原数据计算出MD5值很容易。

3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。

4、弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

5、强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。

虽说MD5有不可逆的特点

但是由于某些MD5破解网站,专门用来查询MD5码,其通过

把常用的密码先MD5处理,并将数据存储起来,然后跟需要查询的MD5结果匹配,这时就有可能通过匹配的MD5得到明文,所以有些简单的MD5码是反查到加密前原文的。

为了让MD5码更加安全,涌现了很多其他方法,如加盐。 盐要足够长足够乱 得到的MD5码就很难查到。

MD5用途:

1.防止被篡改:

1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果ab一样就代表中途未被篡改。

2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。

3SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

2.防止直接看到明文:

现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

3.防止抵赖(数字签名):

这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的数字签名

MD5加密算法原理及实现:

MD5算法原理:

1、数据填充

对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。

填充方法:在消息后面进行填充,填充第一位为1,其余为0。

(此时消息长度为N*512+448)

2、添加消息长度

在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。

在此步骤进行完毕后,最终消息长度就是512的整数倍。

(此时消息长度为(N+1)*512 )

3、数据处理

首先需要用到4个常数:

四个32位变量初始化(经过研究所得,为固定值)

A = 0×01234567 

B = 0×89ABCDEF 

C = 0xFEDCBA98 

D = 0×76543210  

它们称为链接变量(chaining variable)

 

以上为标准的幻数的(物理顺序)

如果在程序中(小端模式)定义应该是: 

A = 0x67452301

B = 0xEFCDAB89

C = 0x98BADCFE

D = 0x10325476

 

然后需要用到4个非线性函数

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));

(&是与, |是或, ~是非, ^是异或)

 

然后是4种操作

FF(a,b,c,d,Mi,s,tj)  表示a=b+((a+(F(b,c,d)+Mi+tj)<<< s)

GG(a,b,c,d,Mi,s,tj) 表示 a=b+((a+(G(b,c,d)+Mi+tj)<<< s) 

HH(a,b,c,d,Mi,s,tj) 表示 a=b+((a+(H(b,c,d)+Mi+tj)<<< s) 

II(a,b,c,d,Mi,s,tj)   表示a=b+((a+(I(b,c,d)+Mi+tj)<<< s) 

其中Mi表示消息的第i个子分组(从0到15,共16个),<<< s表示循环左移s位

常数tj为: 

在第j步中,tj是4294967296*abs(sin(j))的整数部分,i的单位是弧度。

(4294967296是2的32次方)

亦可用 0x100000000UL * abs(sin((double)j)) 计算

 

x循环左移s位:

( s << x ) | ( s >> (32 - x) )

 

4.MD5运算:

由类似的64次循环构成,分成4轮,每轮16次。每轮使用FF,GG,HH,II中的一种操作;

一轮中,a,b,c,d的使用顺序轮转;

例如第一轮:

第一次计算 FF(a,b,c,d,M0,s,t1)

                   a = a+(F(b,c,d)+M0+t1);

                   a = ( s <<a ) | ( s >> (32 - a) );

                   a = a + b;

第二次计算 FF(b,c,d,a,M1,s,t2)

                   b = b+(F(c,d,a)+M1+t2);

                   b = ( s <<b ) | ( s >> (32 - b) );

                   b = b + c;

 

这4轮共64步是: 

(初次使用的a,b,c,d为A,B,C,D的值,而Mi,s,tj根据下面的数值进行使用,可认为是常量,)

第一轮 

FF(a,b,c,d,M0,7,0xd76aa478) 

FF(d,a,b,c,M1,12,0xe8c7b756) 

FF(c,d,a,b,M2,17,0×242070db) 

FF(b,c,d,a,M3,22,0xc1bdceee) 

FF(a,b,c,d,M4,7,0xf57c0faf) 

FF(d,a,b,c,M5,12,0×4787c62a) 

FF(c,d,a,b,M6,17,0xa8304613) 

FF(b,c,d,a,M7,22,0xfd469501) 

FF(a,b,c,d,M8,7,0×698098d8) 

FF(d,a,b,c,M9,12,0×8b44f7af) 

FF(c,d,a,b,M10,17,0xffff5bb1) 

FF(b,c,d,a,M11,22,0×895cd7be) 

FF(a,b,c,d,M12,7,0×6b901122) 

FF(d,a,b,c,M13,12,0xfd987193) 

FF(c,d,a,b,M14,17,0xa679438e) 

FF(b,c,d,a,M15,22,0×49b40821) 

 

第二轮 

GG(a,b,c,d,M1,5,0xf61e2562) 

GG(d,a,b,c,M6,9,0xc040b340) 

GG(c,d,a,b,M11,14,0×265e5a51) 

GG(b,c,d,a,M0,20,0xe9b6c7aa) 

GG(a,b,c,d,M5,5,0xd62f105d) 

GG(d,a,b,c,M10,9,0×02441453) 

GG(c,d,a,b,M15,14,0xd8a1e681) 

GG(b,c,d,a,M4,20,0xe7d3fbc8) 

GG(a,b,c,d,M9,5,0×21e1cde6) 

GG(d,a,b,c,M14,9,0xc33707d6) 

GG(c,d,a,b,M3,14,0xf4d50d87) 

GG(b,c,d,a,M8,20,0×455a14ed) 

GG(a,b,c,d,M13,5,0xa9e3e905) 

GG(d,a,b,c,M2,9,0xfcefa3f8) 

GG(c,d,a,b,M7,14,0×676f02d9) 

GG(b,c,d,a,M12,20,0×8d2a4c8a) 

 

第三轮 

HH(a,b,c,d,M5,4,0xfffa3942) 

HH(d,a,b,c,M8,11,0×8771f681) 

HH(c,d,a,b,M11,16,0×6d9d6122) 

HH(b,c,d,a,M14,23,0xfde5380c) 

HH(a,b,c,d,M1,4,0xa4beea44) 

HH(d,a,b,c,M4,11,0×4bdecfa9) 

HH(c,d,a,b,M7,16,0xf6bb4b60) 

HH(b,c,d,a,M10,23,0xbebfbc70) 

HH(a,b,c,d,M13,4,0×289b7ec6) 

HH(d,a,b,c,M0,11,0xeaa127fa) 

HH(c,d,a,b,M3,16,0xd4ef3085) 

HH(b,c,d,a,M6,23,0×04881d05) 

HH(a,b,c,d,M9,4,0xd9d4d039) 

HH(d,a,b,c,M12,11,0xe6db99e5) 

HH(c,d,a,b,M15,16,0×1fa27cf8) 

HH(b,c,d,a,M2,23,0xc4ac5665) 

 

第四轮 

II(a,b,c,d,M0,6,0xf4292244) 

II(d,a,b,c,M7,10,0×432aff97) 

II(c,d,a,b,M14,15,0xab9423a7) 

II(b,c,d,a,M5,21,0xfc93a039) 

II(a,b,c,d,M12,6,0×655b59c3) 

II(d,a,b,c,M3,10,0×8f0ccc92) 

II(c,d,a,b,M10,15,0xffeff47d) 

II(b,c,d,a,M1,21,0×85845dd1) 

II(a,b,c,d,M8,6,0×6fa87e4f) 

II(d,a,b,c,M15,10,0xfe2ce6e0) 

II(c,d,a,b,M6,15,0xa3014314) 

II(b,c,d,a,M13,21,0×4e0811a1) 

II(a,b,c,d,M4,6,0xf7537e82) 

II(d,a,b,c,M11,10,0xbd3af235) 

II(c,d,a,b,M2,15,0×2ad7d2bb) 

II(b,c,d,a,M9,21,0xeb86d391) 

 

消息分以512位为一分组进行处理,每一个分组进行上述4轮共64次计算后,将A,B,C,D分别加上计算得到的a,b,c,d。当做新的A,B,C,D,并将这4个变量赋值给a,b,c,d再进行下一分组的运算。由于填充后的消息长度为(N+1)*512,则共需计算N+1个分组。计算所有数据分组后,这4个变量为最后的结果,即MD5值。

 

MD5算法实现:

 

实现一:

MD5.h

#ifndef _MD5H_
#define _MD5H_
#include <math.h>
#include <Windows.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>

void ROL(unsigned int &s, unsigned short cx); //32位数循环左移实现函数
void ltob(unsigned int &i); //B\L互转,接受UINT类型
unsigned int* MD5(const char* mStr); //MD5加密函数,并执行数据填充
unsigned int* MD5_2(const char* mStr); //MD5加密函数,并执行数据填充,更优

#endif

 MD5.cpp

#include "MD5.h"

//4组计算函数
inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
{
    return (X & Y) | ((~X) & Z);
}
inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
{
    return (X & Z) | (Y & (~Z));
}
inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
{
    return X ^ Y ^ Z;
}
inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
{
    return Y ^ (X | (~Z));
}

//32位数循环左移(或称右移)实现函数
void ROL(unsigned int &s, unsigned short cx)
{
    if (cx > 32)cx %= 32;
    s = (s << cx) | (s >> (32 - cx));
    return;
}

//B\L互转,接收UINT类型
void ltob(unsigned int &i)
{
    unsigned int tmp = i;//保存副本
    byte *psour = (byte*)&tmp, *pdes = (byte*)&i;
    pdes += 3;//调整指针,准备左右调转
    for (short j = 3; j >= 0; --j)
    {
        CopyMemory(pdes - j, psour + j, 1);
    }
    return;
}

//MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
void AccLoop(unsigned short label, unsigned int *lGroup, void *M)
{
    unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
    typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型

    //循环左移-位数表
    const unsigned int rolarray[4][4] = {
        { 7, 12, 17, 22 },
        { 5, 9, 14, 20 },
        { 4, 11, 16, 23 },
        { 6, 10, 15, 21 }
    };
    //数据坐标表
    const unsigned short mN[4][16] = {
        { 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 }
    };
    const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
    TAcc = ((label - 1) * 16) + 1;
    clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组

    //一轮循环开始(16组->16次)
    for (short i = 0; i < 16; ++i)
    {
        /*进行指针自变换*/
        i1 = lGroup + ((0 + i) % 4);
        i2 = lGroup + ((1 + i) % 4);
        i3 = lGroup + ((2 + i) % 4);
        i4 = lGroup + ((3 + i) % 4);

        if(0 == i%2)
        {
            //计算开始: A+F(B,C,D)+M[i]+T[i+1]
            tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
            //循环左移
            ROL(tmpi, rolarray[label - 1][i % 4]);
            //相加并赋值到下一个种子
            *i1 = *i2 + tmpi;
        }
        else
        {
            tmpi = (*i3 + clacArr[label - 1](*i4, *i1, *i2) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
            //循环左移
            ROL(tmpi, rolarray[label - 1][i % 4]);
            //相加并赋值到下一个种子
            *i3 = *i4 + tmpi;
        }
    }
    return;
}

//加密函数
unsigned int* MD5(const char* mStr)
{
    //计算缓冲区长度,并进行数据填充
    unsigned int mLen = strlen(mStr);
    if (mLen <= 0) 
    {
        return 0;
    }
    unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
    unsigned int FSbyte = FillSize / 8;               //以字节表示的填充数
    //预留512-448=64bit,填充原消息的长度
    unsigned int BuffLen = mLen + 8 + FSbyte;         //缓冲区长度
    unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
    CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区

    //数据填充
    md5Buff[mLen] = 0x80; //第一个bit填充1
    ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0
    unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充后64bit
    CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8);

    //数据运算
    unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
    unsigned int A = 0x67452301, B = 0xEFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
    unsigned int *lGroup = new unsigned int[4];
    lGroup[0] = A;
    lGroup[1] = B;
    lGroup[2] = C;
    lGroup[3] = D;

    for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
    {
        //进入4次计算的小循环,共4*16次
        for (unsigned short Lcount = 0; Lcount < 4;)
        {
            AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]);
        }
        //数据相加作为下一轮的种子或者最终输出
        A = (lGroup[0] += A);
        B = (lGroup[1] += B);
        C = (lGroup[2] += C);
        D = (lGroup[3] += D);

    }
    //转换内存中的布局后才能正常显示
    ltob(lGroup[0]);
    ltob(lGroup[1]);
    ltob(lGroup[2]);
    ltob(lGroup[3]);
    delete[] md5Buff;
    return lGroup;
}

/*
MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
种子数组排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B;
*/
void AccLoop_2(unsigned short label, unsigned int *lGroup, void *M)
{
    unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
    typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型
    const unsigned int rolarray[4][4] = {
        { 7, 12, 17, 22 },
        { 5, 9, 14, 20 },
        { 4, 11, 16, 23 },
        { 6, 10, 15, 21 }
    };//循环左移-位数表
    const unsigned short mN[4][16] = {
        { 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 }
    };//数据坐标表
    const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
    TAcc = ((label - 1) * 16) + 1; //根据第几轮循环初始化T表累加器
    clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组

    /*一轮循环开始(16组->16次)*/
    for (short i = 0; i < 16; ++i)
    {
        /*进行指针自变换*/
        i1 = lGroup + ((0 + i) % 4);
        i2 = lGroup + ((3 + i) % 4);
        i3 = lGroup + ((2 + i) % 4);
        i4 = lGroup + ((1 + i) % 4);

        /*第一步计算开始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接计算T表*/
        tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
        ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:循环左移
        *i1 = *i2 + tmpi;//第三步:相加并赋值到种子
    }
    return;
}

/*接口函数,并执行数据填充*/
unsigned int* MD5_2(const char* mStr)
{
    unsigned int mLen = strlen(mStr); //计算字符串长度
    if (mLen < 0) return 0;
    unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
    unsigned int FSbyte = FillSize / 8; //以字节表示的填充数
    unsigned int BuffLen = mLen + 8 + FSbyte; //缓冲区长度或者说填充后的长度
    unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
    CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区

    /*数据填充开始*/
    md5Buff[mLen] = 0x80; //第一个bit填充1
    ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函数为FillMemory
    unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充
    CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充长度
    /*数据填充结束*/

    /*运算开始*/
    unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
    unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
    //unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //种子副本数组,并作为返回值返回
    unsigned int *lGroup = new unsigned int[4];
    lGroup[0] = A;
    lGroup[1] = D;
    lGroup[2] = C;
    lGroup[3] = B;

    for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
    {
        /*进入4次计算的小循环*/
        for (unsigned short Lcount = 0; Lcount < 4;)
        {
            AccLoop_2(++Lcount, lGroup, &md5Buff[Bcount * 64]);
        }
        /*数据相加作为下一轮的种子或者最终输出*/
        A = (lGroup[0] += A);
        B = (lGroup[3] += B);
        C = (lGroup[2] += C);
        D = (lGroup[1] += D);
    }
    /*转换内存中的布局后才能正常显示*/
    ltob(lGroup[0]);
    ltob(lGroup[1]);
    ltob(lGroup[2]);
    ltob(lGroup[3]);
    delete[] md5Buff; //清除内存并返回
    return lGroup;
}

 

main.cpp

#include "MD5.h"

int main()
{
    char tmpstr[256], buf[4][10];
    std::cout << "请输入要加密的字符串:";
    std::cin >> tmpstr;

    //char buf[4][10];
    //char tmpstr[] ="admin";
    //MD5值:21232F297A57A5A743894A0E4A801FC3

    unsigned int* tmpGroup = MD5(tmpstr);
    sprintf_s(buf[0], "%8X", tmpGroup[0]);  //A
    sprintf_s(buf[1], "%8X", tmpGroup[1]);  //B
    sprintf_s(buf[2], "%8X", tmpGroup[2]);  //C
    sprintf_s(buf[3], "%8X", tmpGroup[3]);  //D
    std::cout <<"1-MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;

    tmpGroup = MD5_2(tmpstr);
    sprintf_s(buf[0], "%8X", tmpGroup[0]);  //A
    sprintf_s(buf[1], "%8X", tmpGroup[3]);  //B
    sprintf_s(buf[2], "%8X", tmpGroup[2]);  //C
    sprintf_s(buf[3], "%8X", tmpGroup[1]);  //D
    std::cout <<"2-MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;

    delete[] tmpGroup;

    return 0;
}

实现二:

#include <string.h>
#include <math.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>


/***********************************
* 非线性函数
* (&是与,|是或,~是非,^是异或) 
* 
* 这些函数是这样设计的:
*   如果X、Y和Z的对应位是独立和均匀的,
*   那么结果的每一位也应是独立和均匀的。 
* 
* 函数F是按逐位方式操作:如果X,那么Y,否则Z。
* 函数H是逐位奇偶操作符
**********************************/
#define F(x,y,z) ((x & y) | (~x & z))  
#define G(x,y,z) ((x & z) | (y & ~z))  
#define H(x,y,z) (x^y^z)  
#define I(x,y,z) (y ^ (x | ~z))  


/**************************************
*向左换移(右环移)n个单位
* ************************************/
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))  


/****************************************************
* 每次操作对a,b,c和d中的其中三个作一次非线性函数运算
*  F(b,c,d)   G(b,c,d)   H(b,c,d)   I(b,c,d)
*
* 然后将所得结果加上 第四个变量(a),
* F(b,c,d)+a
*
* 文本的一个子分组(x)
* F(b,c,d)+a+x
* 
* 和一个常数(ac)。
* F(b,c,d)+a+x+ac
*
* 再将所得结果向右环移一个不定的数(s),
* ROTATE_LEFT( F(b,c,d)+a+x+ac , s )
* 
* 并加上a,b,c或d中之一(b)。
* ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
* 
* 最后用该结果取代a,b,c或d中之一(a)。
* a=ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
* 
* ***************************************************/
#define FF(a,b,c,d,x,s,ac) { a += F(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
#define GG(a,b,c,d,x,s,ac) { a += G(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
#define HH(a,b,c,d,x,s,ac) { a += H(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
#define II(a,b,c,d,x,s,ac) { a += I(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }


//储存一个MD5 text信息 
typedef struct  
{  
    unsigned int count[2];    
    //记录当前状态,其数据位数   

    unsigned int state[4];    
    //4个数,一共32位 记录用于保存对512bits信息加密的中间结果或者最终结果  

    unsigned char buffer[64];
    //一共64字节,512位      
}MD5_CTX;  


//第一位1 其后若干个0,用于MD5Final函数时的补足
unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  


//函数声明
void MD5Init(MD5_CTX *context);  
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);  
void MD5Final(MD5_CTX *context,unsigned char digest[16]);  
void MD5Transform(unsigned int state[4],unsigned char block[64]);  
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);  
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);  


/************************
* 函数功能:初始化一个MD5 text
* 函数参数:MD5 text 指针
* ***********************/
//初始化
void MD5Init(MD5_CTX *context)  
{  
    context->count[0] = 0;  
    context->count[1] = 0;   
    //分别赋固定值  
    context->state[0] = 0x67452301;
    context->state[1] = 0xEFCDAB89;  
    context->state[2] = 0x98BADCFE;  
    context->state[3] = 0x10325476;  
}  


/************************************************
* 函数功能:对一个MD5 text,把输入的数据进行分组,并进行加密
* 未用到的数据把其储存在MD5 text中。
*
* 参数分析:
* MD5_CTX *context       :一个MD5 text   
* unsigned char *input   :新添加的数据  
* unsigned int inputlen  :新添加数据的长度(字节)
*
***********************************************/

void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen)  
{  
    unsigned int i = 0,index = 0,partlen = 0;  

    //index:当前状态的位数对64取余,其单位是字节
    //也可以写作:  index=(context->count[0]/8)%64
    index = (context->count[0] >> 3) & 0x3F;  

    //partlen:可以补齐64字节的字节数
    partlen = 64 - index;

    //下面代码是解决一个unsignde int 无法储存极大数据导致溢出的问题
    //当前位数加上新添加的位数,由于inputlen是以字节为单位,所以其转换为位数
    //相当于context->count[0] += inputlen*8;  
    context->count[0] += inputlen << 3;  

    //当其出现溢出的情况时,通过以下操作把两个16位的数连在一块,生成一个
    //32位的二进制数串,从而扩大其储存范围
    if(context->count[0] < (inputlen << 3))
    {
        context->count[1]++;
    }

    //该语句可替换为 context->count[1]+=(inputlen<<3)>>32;
    //便于理解
    context->count[1] += inputlen >> 29;  

    //当其输入字节数的大于其可以补足64字节的字节数,进行补足
    if(inputlen >= partlen)  
    {  
        //向buffer中补足partlen个字节,使其到达64字节
        memcpy(&context->buffer[index], input, partlen);

        //buffer达到64字节512位,则把其作为一组进行运算
        MD5Transform(context->state, context->buffer);  

        //如果输入的数据还可以组成多个64字节,则把其可以组成
        //的作为若干组进行运算
        for(i = partlen;i+64 <= inputlen;i+=64)  
            MD5Transform(context->state, &input[i]);  

        //恢复0值,照应 下面 把输入 剩余字节(不能组成64字节组) 储存的操作
        index = 0;          
    }   
    //否则,把输入的数据按顺序放在原来数据后面
    else  
    {  
        i = 0;  
    }  

    //放置剩余数据
    memcpy(&context->buffer[index],&input[i],inputlen-i);  
}  


/*************************************************
* 函数功能:对数据进行补足,并加入数据位数信息,并进一步加密
* 
* 参数分析:
* MD5_CTX *context          :一个MD5 text
* unsigned char digest[16]  :储存加密结果的数组
*************************************************/

void MD5Final(MD5_CTX *context,unsigned char digest[16])  
{  
    unsigned int index = 0,padlen = 0;  

    //bits: 8个字节,64位
    unsigned char bits[8];  

    //index:对64取余结果
    index = (context->count[0] >> 3) & 0x3F;  
    //因为要填充满足使其位长对512求余的结果等于448(56位)
    //所以当其所剩余的数小于56字节,则填充56-index字节,
    //否则填充120-index字节
    //这里padlen代表其所需填充的字节
    padlen = (index < 56)?(56-index):(120-index);  

    //然后,在这个结果后面附加一个以64位二进制表示的填充前数据长度。
    //把填充前数据数据长度转换后放到bit字符数组中
    MD5Encode(bits,context->count,8);

    //根据已经存储好的数组PADDING,在信息的后面填充一个1和无数个0,
    //直到满足上面的条件时才停止用0对信息的填充
    //其填充后进行了一系列的加密操作,其定剩余48个字节
    MD5Update(context,PADDING,padlen);  

    //在最后添加进8个字节的数据长度信息,最后凑成一组,进行一次加密处理
    MD5Update(context,bits,8);  

    //把最终得到的加密信息变成字符输出,共16字节
    MD5Encode(digest,context->state,16);  
}  


/**********************************************************
* 函数功能:利用位操作,按1->4方式把数字分解成字符
*
* 参数分析:
* unsigned char  *output :输出的字符的数组
* unsigned int   *input  :输入数字的数组
* unsigned int   len     : 输入数字数组的长度(单位:位) 
* *********************************************************/

void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)  
{  
    unsigned int i = 0,j = 0;  
    while(j < len)  
    {  
        //这里& 0xFF为取后8位
        //i代表数字数组下标
        //j代表字符数组下标
        //把数字的8、8-16、16-24、24-32分别赋值给字符
        output[j] = input[i] & 0xFF;    
        output[j+1] = (input[i] >> 8) & 0xFF;  
        output[j+2] = (input[i] >> 16) & 0xFF;  
        output[j+3] = (input[i] >> 24) & 0xFF;  
        i++;  
        j+=4;  
    }  
}  


/**********************************************************
* 函数功能:利用位操作,按4->1方式把字符合成数字
*
* 参数分析:
* unsigned int  *output :输出的数字的数组
* unsigned char *input  :输入字符的数组
* unsigned int  len     : 输入字符的长度 (单位:位)
* *********************************************************/

void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)  
{  
    unsigned int i = 0,j = 0;  
    while(j < len)  
    {  
        //利用位操作,把四个单位为1字节的字符,合成一个单位为4字节的数字
        //因为FF GG HH II和非线性函数都只能对数字进行处理
        //第一个字符占前8位,第二个占8-16位,第三个占16-24位,第四个占
        //24-32位。
        //i代表数字数组下标
        //j代表字符数组下标
        output[i] = (input[j]) |  
            (input[j+1] << 8) |  
            (input[j+2] << 16) |  
            (input[j+3] << 24);  
        i++;  
        j+=4;   
    }  
}  


/**************************************************************
* 函数功能:对512位的block数据进行加密,并把加密结果存入state数组中
* 对512位信息(即block字符数组)进行一次处理,每次处理包括四轮
*state[4]:md5结构中的state[4],用于保存对512bits信息加密的中间结果或者最终结果
* block[64]:欲加密的512bits信息或其中间数据
***************************************************************/
void MD5Transform(unsigned int state[4], unsigned char block[64])  
{  
    //a b c d继承上一个加密的结果,所以其具有继承性
    unsigned int a = state[0];  
    unsigned int b = state[1];  
    unsigned int c = state[2];  
    unsigned int d = state[3];  

    //这里只需用到16个,我把原来的unsiged int x[64]  改为了 x[16]
    unsigned int x[16];  

    //把字符转化成数字,便于运算
    MD5Decode(x,block,64);  


    //具体函数方式固定,不再赘述

    /*************第一轮******************/
    FF(a, b, c, d, x[ 0], 7, 0xd76aa478);   
    FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);   
    FF(c, d, a, b, x[ 2], 17, 0x242070db);   
    FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);   

    FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);   
    FF(d, a, b, c, x[ 5], 12, 0x4787c62a);   
    FF(c, d, a, b, x[ 6], 17, 0xa8304613);   
    FF(b, c, d, a, x[ 7], 22, 0xfd469501);   

    FF(a, b, c, d, x[ 8], 7, 0x698098d8);   
    FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);   
    FF(c, d, a, b, x[10], 17, 0xffff5bb1);   
    FF(b, c, d, a, x[11], 22, 0x895cd7be);   

    FF(a, b, c, d, x[12], 7, 0x6b901122);   
    FF(d, a, b, c, x[13], 12, 0xfd987193);   
    FF(c, d, a, b, x[14], 17, 0xa679438e);   
    FF(b, c, d, a, x[15], 22, 0x49b40821);   


    /*************第二轮*****************/
    GG(a, b, c, d, x[ 1], 5, 0xf61e2562);   
    GG(d, a, b, c, x[ 6], 9, 0xc040b340);   
    GG(c, d, a, b, x[11], 14, 0x265e5a51);   
    GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);   

    GG(a, b, c, d, x[ 5], 5, 0xd62f105d);   
    GG(d, a, b, c, x[10], 9,  0x2441453);   
    GG(c, d, a, b, x[15], 14, 0xd8a1e681);   
    GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);   

    GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);   
    GG(d, a, b, c, x[14], 9, 0xc33707d6);   
    GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);   
    GG(b, c, d, a, x[ 8], 20, 0x455a14ed);   

    GG(a, b, c, d, x[13], 5, 0xa9e3e905);   
    GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);   
    GG(c, d, a, b, x[ 7], 14, 0x676f02d9);   
    GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);   


    /*************第三轮*****************/
    HH(a, b, c, d, x[ 5], 4, 0xfffa3942);   
    HH(d, a, b, c, x[ 8], 11, 0x8771f681);   
    HH(c, d, a, b, x[11], 16, 0x6d9d6122);   
    HH(b, c, d, a, x[14], 23, 0xfde5380c);   

    HH(a, b, c, d, x[ 1], 4, 0xa4beea44);   
    HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);   
    HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);   
    HH(b, c, d, a, x[10], 23, 0xbebfbc70);   

    HH(a, b, c, d, x[13], 4, 0x289b7ec6);   
    HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);   
    HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);   
    HH(b, c, d, a, x[ 6], 23,  0x4881d05);   

    HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);   
    HH(d, a, b, c, x[12], 11, 0xe6db99e5);   
    HH(c, d, a, b, x[15], 16, 0x1fa27cf8);   
    HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);   



    /*************第四轮******************/
    II(a, b, c, d, x[ 0], 6, 0xf4292244);   
    II(d, a, b, c, x[ 7], 10, 0x432aff97);   
    II(c, d, a, b, x[14], 15, 0xab9423a7);   
    II(b, c, d, a, x[ 5], 21, 0xfc93a039);   

    II(a, b, c, d, x[12], 6, 0x655b59c3);   
    II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);   
    II(c, d, a, b, x[10], 15, 0xffeff47d);   
    II(b, c, d, a, x[ 1], 21, 0x85845dd1);   

    II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);   
    II(d, a, b, c, x[15], 10, 0xfe2ce6e0);   
    II(c, d, a, b, x[ 6], 15, 0xa3014314);   
    II(b, c, d, a, x[13], 21, 0x4e0811a1);   

    II(a, b, c, d, x[ 4], 6, 0xf7537e82);   
    II(d, a, b, c, x[11], 10, 0xbd3af235);   
    II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);   
    II(b, c, d, a, x[ 9], 21, 0xeb86d391);   


    //更换原来的结果
    state[0] += a;  
    state[1] += b;  
    state[2] += c;  
    state[3] += d;  
}


int main(int argc, char *argv[])  
{  
    MD5_CTX md5;  //定义一个MD5 text
    MD5Init(&md5);//初始化
    int i;
    //unsigned char encrypt[] ="admin";//要加密内容
    //加密结果:21232f297a57a5a743894a0e4a801fc3

    unsigned char encrypt[1000];//要加密内容
    printf("请输入要加密的字符串:");
    gets((char *)encrypt);

    unsigned char decrypt[16]; //加密结果
    MD5Update(&md5, encrypt, strlen((char *)encrypt));//进行初步分组加密

    MD5Final(&md5,decrypt);   //进行后序的补足,并加密 

    printf("加密前:%s\n加密后16位:",encrypt);  
    for(i=4;i<12;i++)  
    {  
        printf("%02x",decrypt[i]);
    }  

    printf("\n加密前:%s\n加密后32位:",encrypt);  
    for(i=0;i<16;i++)  
    {  
        printf("%02x",decrypt[i]);
    }
    printf("\n");

    return 0;  
}  

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Oliver_xpl/article/details/90214896

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法