zoukankan      html  css  js  c++  java
  • MD5加密算法

    转载:https://blog.csdn.net/mp624183768/article/details/80575843

    全称:message-digest algorithm 5 
    翻译过来就是:信息 摘要 算法 5

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

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

    1.特点
    (1)长度固定:不管多长的字符串,加密后长度都是一样长 

    作用:方便平时信息的统计和管理

    (2)易计算:字符串和文件加密的过程是容易的. 

    作用: 开发者很容易理解和做出加密工具

    (3)细微性:一个文件,不管多大,小到几k,大到几G,你只要改变里面某个字符,那么都会导致MD5值改变. 

    作用:很多软件和应用在网站提供下载资源,其中包含了对文件的MD5码,用户下载后只需要用工具测一下下载好的文件,通过对比就知道该文件是否有过更改变动.

    (4)不可逆性:你明明知道密文和加密方式,你却无法反向计算出原密码. 

    作用:大大提高了数据的安全性

    2.撞库破解

    这是概率极低的破解方法,原理就是:

    (1)建立一个大型的数据库,把日常的各个语句,通过MD5加密成为密文,不断的积累大量的句子,放在一个庞大的数据库里.

    (2)比如一个人拿到了别人的密文,想去查询真实的密码,就需要那这个密文去到提供这个数据库的公司网站去查询.

    3.MD5加盐

    比如我的银行密码是”12345”

    (1)得到的MD5是:827ccb0eea8a706c4c34a16891f84e7b

    (2)一个人截取到这个密文,那么通过撞库肯定容易撞出12345.

    (3)我们要做的就是加盐,银行密码还是”12345”,然后我把银行密码加上我特定的字符串才计算MD5 

    所以密码还是那个密码,但是变成求”12345密码加密987”的MD5值,然后再得到MD5,那么这个MD5起码可以确认那个数据库不会有.

    4.算法原理

    (1)数据填充

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

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

    (2)添加消息长度

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

    在此步骤进行完毕后,最终消息长度就是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:

    #pragma once
    
    #ifndef MD5H
    #define MD5H
    #include <math.h>
    #include <Windows.h>
    
    void ROL(unsigned int &s, unsigned short cx); //32位数循环左移实现函数
    void ltob(unsigned int &i); //BL互转,接受UINT类型
    unsigned int* MD5(const char* mStr); //接口函数,并执行数据填充,计算MD5时调用此函数
    
     #endif

    MD5.cpp:

    #pragma once
    
    #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));
         }
     /*4组计算函数结束*/
    
     /*32位数循环左移实现函数*/
     void ROL(unsigned int &s, unsigned short cx)
     {
             if (cx > 32)cx %= 32;
             s = (s << cx) | (s >> (32 - cx));
             return;
         }
    
     /*BL互转,接收UINT类型*/
     void ltob(unsigned int &i)
     {
             unsigned int tmp = i;//保存副本
            byte *psour = (byte*)&tmp, *pdes = (byte*)&i;
            pdes += 3;//调整指针,准备左右调转
             for (short i = 3; i >= 0; --i)
                 {
                     CopyMemory(pdes - i, psour + i, 1);
                 }
             return;
         }
    
     /*
        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(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(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}; //种子副本数组,并作为返回值返回
           for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
                {
                    /*进入4次计算的小循环*/
                      for (unsigned short Lcount = 0; Lcount < 4;)
                        {
                AccLoop(++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;
    }
  • 相关阅读:
    C#projectPropertyGrid 显示结构(展开逗号分隔)
    WPF 数据绑定
    WPF Binding值转换器ValueConverter使用简介(二)-IMultiValueConverter
    WPF Binding值转换器ValueConverter使用简介(一)
    Sql语句知识大全
    C#端口、IP正则
    C#基础知识整理
    C#UDP异步通信
    D365 添加导入按钮继承到系统导出
    D365 FO 创建DataEntity找不到natural key
  • 原文地址:https://www.cnblogs.com/Toya/p/12374080.html
Copyright © 2011-2022 走看看