zoukankan      html  css  js  c++  java
  • MD5加密算法原理及实现

    MD5加密算法原理及实现

     

    MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。

    以下所描述的消息长度、填充数据都以位(Bit)为单位,字节序为小端字节。

    算法原理

    1、数据填充

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

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

    2、添加消息长度

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

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

    3、数据处理

    准备需要用到的数据:

    • 4个常数: A = 0x67452301, B = 0x0EFCDAB89, 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));

    把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。

    具体计算的实现较为复杂,建议查阅相关书籍,下面给出在C++上的实现代码。

    代码实现

    #MD5.h

    复制代码
     1 #ifndef MD5H
     2 #define MD5H
     3 #include <math.h>
     4 #include <Windows.h>
     5 
     6 void ROL(unsigned int &s, unsigned short cx); //32位数循环左移实现函数
     7 void ltob(unsigned int &i); //B\L互转,接受UINT类型
     8 unsigned int* MD5(const char* mStr); //接口函数,并执行数据填充,计算MD5时调用此函数
     9 
    10 #endif
    复制代码

    #MD5.cpp

    复制代码
      1 #include "MD5.h"
      2 
      3 /*4组计算函数*/
      4 inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
      5 {
      6     return (X & Y) | ((~X) & Z);
      7 }
      8 inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
      9 {
     10     return (X & Z) | (Y & (~Z));
     11 }
     12 inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
     13 {
     14     return X ^ Y ^ Z;
     15 }
     16 inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
     17 {
     18     return Y ^ (X | (~Z));
     19 }
     20 /*4组计算函数结束*/
     21 
     22 /*32位数循环左移实现函数*/
     23 void ROL(unsigned int &s, unsigned short cx)
     24 {
     25     if (cx > 32)cx %= 32;
     26     s = (s << cx) | (s >> (32 - cx));
     27     return;
     28 }
     29 
     30 /*B\L互转,接收UINT类型*/
     31 void ltob(unsigned int &i)
     32 {
     33     unsigned int tmp = i;//保存副本
     34     byte *psour = (byte*)&tmp, *pdes = (byte*)&i;
     35     pdes += 3;//调整指针,准备左右调转
     36     for (short i = 3; i >= 0; --i)
     37     {
     38         CopyMemory(pdes - i, psour + i, 1);
     39     }
     40     return;
     41 }
     42 
     43 /*
     44 MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
     45 种子数组排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B;
     46 */
     47 void AccLoop(unsigned short label, unsigned int *lGroup, void *M)
     48 {
     49     unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
     50     typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型
     51     const unsigned int rolarray[4][4] = {
     52         { 7, 12, 17, 22 },
     53         { 5, 9, 14, 20 },
     54         { 4, 11, 16, 23 },
     55         { 6, 10, 15, 21 }
     56     };//循环左移-位数表
     57     const unsigned short mN[4][16] = {
     58         { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
     59         { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
     60         { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
     61         { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
     62     };//数据坐标表
     63     const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
     64     TAcc = ((label - 1) * 16) + 1; //根据第几轮循环初始化T表累加器
     65     clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组
     66 
     67     /*一轮循环开始(16组->16次)*/
     68     for (short i = 0; i < 16; ++i)
     69     {
     70         /*进行指针自变换*/
     71         i1 = lGroup + ((0 + i) % 4);
     72         i2 = lGroup + ((3 + i) % 4);
     73         i3 = lGroup + ((2 + i) % 4);
     74         i4 = lGroup + ((1 + i) % 4);
     75 
     76         /*第一步计算开始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接计算T表*/
     77         tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
     78         ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:循环左移
     79         *i1 = *i2 + tmpi;//第三步:相加并赋值到种子
     80     }
     81     return;
     82 }
     83 
     84 /*接口函数,并执行数据填充*/
     85 unsigned int* MD5(const char* mStr)
     86 {
     87     unsigned int mLen = strlen(mStr); //计算字符串长度
     88     if (mLen < 0) return 0;
     89     unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
     90     unsigned int FSbyte = FillSize / 8; //以字节表示的填充数
     91     unsigned int BuffLen = mLen + 8 + FSbyte; //缓冲区长度或者说填充后的长度
     92     unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
     93     CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区
     94 
     95     /*数据填充开始*/
     96     md5Buff[mLen] = 0x80; //第一个bit填充1
     97     ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函数为FillMemory
     98     unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充
     99     CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充长度
    100     /*数据填充结束*/
    101 
    102     /*运算开始*/
    103     unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
    104     unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
    105     unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //种子副本数组,并作为返回值返回
    106     for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
    107     {
    108         /*进入4次计算的小循环*/
    109         for (unsigned short Lcount = 0; Lcount < 4;)
    110         {
    111             AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]);
    112         }
    113         /*数据相加作为下一轮的种子或者最终输出*/
    114         A = (lGroup[0] += A);
    115         B = (lGroup[3] += B);
    116         C = (lGroup[2] += C);
    117         D = (lGroup[1] += D);
    118     }
    119     /*转换内存中的布局后才能正常显示*/
    120     ltob(lGroup[0]);
    121     ltob(lGroup[1]);
    122     ltob(lGroup[2]);
    123     ltob(lGroup[3]);
    124     delete[] md5Buff; //清除内存并返回
    125     return lGroup;
    126 }
    复制代码

    再给出调用实例(以win32控制台应用程序为例):

    #main.cpp

    复制代码
     1 #include <iostream>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include "MD5.h"
     5 
     6 int main(int argc, char **argv)
     7 {
     8     char tmpstr[256], buf[4][10];
     9     std::cout << "请输入要加密的字符串:";
    10     std::cin >> tmpstr;
    11     unsigned int* tmpGroup = MD5(tmpstr);
    12     sprintf_s(buf[0], "%8X", tmpGroup[0]);
    13     sprintf_s(buf[1], "%8X", tmpGroup[3]);
    14     sprintf_s(buf[2], "%8X", tmpGroup[2]);
    15     sprintf_s(buf[3], "%8X", tmpGroup[1]);
    16     std::cout <<"MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;
    17     
    18     delete[] tmpGroup;
    19     return 0; //在此下断点才能看到输出的值
    20 }
    复制代码

    注:以上代码在VS2013上编译通过

     
    标签: MD5
  • 相关阅读:
    4.计算机启动过程的简单介绍 计算机启动流程 计算机BIOS作用 POST 开机自检 计算机启动顺序 分区表 操作系统启动
    3.操作系统简单介绍 操作系统发展历史 批处理分时系统 操作系统是什么 操作系统对文件的抽象 进程 虚拟内存是什么 操作系统作用 操作系统功能
    2.计算机组成-数字逻辑电路 门电路与半加器 异或运算半加器 全加器组成 全加器结构 反馈电路 振荡器 存储 D T 触发器 循环移位 计数器 寄存器 传输门电路 译码器 晶体管 sram rom 微处理 计算机
    1.计算机发展阶段 计算机发展历史 机械式计算机 机电式计算机 电子计算机 逻辑电路与计算机 二极管 电子管 晶体管 硅 门电路 计算机 电磁学计算机二进制
    如何解决svn清理失败 不能更新 cleanup失败 cleanup乱码 更新乱码 svn更新提示清理 清理乱码不能清理 svn故障修复SVN cleanup 陷入死循环 svn cleanup时遇到错误怎么办
    eclipse svn插件卸载 重新安装 Subclipse卸载安装 The project was not built since its build path is incomplete This client is too old to work with the working copy at
    java for循环里面执行sql语句操作,有效结果只有一次,只执行了一次sql mybatis 循环执行update生效一次 实际只执行一次
    windows资源管理器多标签打开 windows文件夹多标签浏览 浏览器tab页面一样浏览文件夹 clover win8 win10 报错 无响应问题怎么解决 clover卡死 clover怎么换皮肤
    批处理启动vm虚拟机服务 vm12启动无界面启动vm虚拟机系统 windows上如何操作服务 sc net启动关闭服务
    不能ssh连接ubuntu linux 服务器 secureCRT不能ssh连接服务器 不能远程ssh连接虚拟机的ubuntu linux
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/15748492.html
Copyright © 2011-2022 走看看