MD5 加密算法 - C++ 实现

写在前头: 还在学习中! 整个文档写的很匆忙, 肯定还有很多不周到的地方. 欢迎在评论中提出你的宝贵意见!!

算法背景 Background

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16个字符(BYTES))的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 中被加以规范。

将数据(如一段文字)运算变为另一固定长度值,是散列算法的基础原理。

1996年后被证实存在弱点,可以被加以破解,对于需要高度安全性的资料,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞攻击,因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

— 摘自 维基百科 MD5 - 维基百科,自由的百科全书 (wikipedia.org)

说明 在 MD5 算法中, 数据的存储均使用 小端序. 即低位存放在内存地址小的位置. (英:little-endian)

基本结构 Structure

主要有一下几个构件组成.

MD5 算法

输入: 任意长度的明文

输出: 128-bit 的摘要 (digest)

HMD5H_{MD5}HMD5​ (有点像DES)

输入: 512-bit 的明文分组, CV 链接向量(类似Key)

输出: 128-bit 结果

Logic_Functioni()Logic\_Function_i()Logic_Functioni​() 逻辑函数

输入: 三个 32-bit 字

输出: 一个 32-bit 字

以 类似CBC 的模式链接 (即每一组的加密会受到前一组的影响),

算法流程 Process

MD5 算法可以大致分为三个步骤: 消息预处理, 初始化缓冲区, 循环哈希.

  1. 消息预处理

    该步骤可以分为两个小点: ① 填充消息; ② 附加信息.

    • 填充消息

      填充输入的原消息, 使消息的长度 $Length(M’) \equiv 448 \pmod{512} $

      必须对输入消息进行填充, 即使消息原本就已经满足如上要求, 也要进行填充. 这是为了第二步操作预留 64-bit 空间.

      填充内容: 第一位为1, 其余位为零.

    • 附加信息

      在第一步预留出的空间里附加上明文消息的长度.

      原因: 增加攻击者伪造明文的难度. 伪造信息的长度必需要与原文长度相等(其实是同余)

      将消息长度 Length(M)(mod264)Length(M) \pmod{2^{64}}Length(M)(mod264) 以小端序的形式附加到第一步预留的 64-bit 空间中.

    按以上步骤处理完消息之后, 每一组消息可以按 32-bit 一组, 被分为 16组字(Word) (512 = 32 * 16)

    在本文中被记作 Mi[j]M_i[j]Mi​[j] j表示字的组数.

  2. 初始化缓冲区

    算法使用 128-bit 的缓冲区存放中间结果和最终哈希值, 128-bit 可以看做 4 组 32-bit 字所占的比特位(128 = 4 * 32)

    被记作 BufferA,BufferB,BufferC,BufferDBuffer_A, Buffer_B,Buffer_C, Buffer_DBufferA​,BufferB​,BufferC​,BufferD​. 每个缓冲区都以小端的方式存储数据. 将 4 块 Buffer 组合起来记为链接向量 CViCV_iCVi​

    CVi=CVi−1CV_i = CV_{i - 1}CVi​=CVi−1​

    CV0CV_0CV0​ 被规定为常量, 如下表格所示.

    字节序 存储内容
    小端序 Buffer[4] = {0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210};
    大端序 Buffer[4] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};
  3. 循环哈希

    调用 HMD5(Mi,CVi)H_{MD5}(M_i, CV_i)HMD5​(Mi​,CVi​) 函数对每组消息分组进行哈希计算.

    每一次 HMD5()H_{MD5}()HMD5​() 中有 4 轮结构相同的逻辑函数, 不同轮次间的唯一区别是参与计算的逻辑函数不同, 分别表示为 FGHI.

    此外, HMD5()H_{MD5}()HMD5​() 还有一个常数表 T, 常数表 T 为定义为 KaTeX parse error: Undefined control sequence: \abs at position 22: … 2^{32} \times \̲a̲b̲s̲{\sin{i}} (也就是 KaTeX parse error: Undefined control sequence: \abs at position 1: \̲a̲b̲s̲{\sin i} 的前 32-bit 小数.) (i 是弧度)

    在本文中把 HMD5H_{MD5}HMD5​ 的轮函数记为 R(Mi,Buffer,T[],functioni())R(M_i, Buffer, T[], function_i())R(Mi​,Buffer,T[],functioni​()). 其中, 轮函数每次调用是都会读取缓存区中的数据. 而且不同轮次之间所调用的逻辑函数也是不一样的. 此外, 每一次调用轮函数会用到不同 16 组 T 元素(对应轮函数内部的中 16 次迭代).

    HMD5()H_{MD5}()HMD5​() 完成第四轮轮函数处理之后, 将得到的结果和输入的 $CV_i $按字(每 32-bit) 分组按位加. 得到最终输出结果 CVi+1CV_{i+1}CVi+1​.

函数定义 Function define

轮函数(压缩函数)的具体实现

轮函数每次调用内部有 16 次迭代计算, 将轮函数当前迭代的次数记作 i

读取缓冲区中的数据, 将缓冲区按字 32-bit 分为 4 组, 记作 ABCD

  • 第一步

    BCD 暂时不动, A 有以下 x 层计算:

    • A+Logic_Function轮数(B,C,D)A + Logic\_Function_{轮数}(B, C, D)A+Logic_Function轮数​(B,C,D)

      F(b,c,d)=(b∧c)∨(bˉ∧d)G(b,c,d)=(b∧d)∨(c∧dˉ)H(b,c,d)=b⊕c⊕dI(b,c,d)=c⊕(b∨dˉ)\begin{array}{l} F(b, c, d) &=& (b \wedge c) \vee(\bar{b} \wedge d) \\ G(b, c, d) &=& (b \wedge d) \vee(c \wedge \bar{d}) \\ H(b, c, d) &=& b \oplus c \oplus d \\ I(b, c, d) &=& c \oplus(b \vee \bar{d}) \end{array}F(b,c,d)G(b,c,d)H(b,c,d)I(b,c,d)​====​(b∧c)∨(bˉ∧d)(b∧d)∨(c∧dˉ)b⊕c⊕dc⊕(b∨dˉ)​

    • A+Mi[k]A + M_i[k]A+Mi​[k] (k 受到当前轮数以及迭代次数 i 的影响)

    • A+T[i]A + T[i]A+T[i] (受到输入影响, 与当前轮数有关)

    • A 循环左移 s 位 (s 由一个常量表给出)

      ρ1(i)=iρ2(i)=(1+5i)mod16ρ3(i)=(5+3i)mod16ρ4(i)=7imod16\begin{array}{l} \rho_{1}(i) = i\\ \rho_{2}(i)=(1+5 i) \bmod 16 \\ \rho_{3}(i)=(5+3 i) \bmod 16 \\ \rho_{4}(i)=7 i \bmod 16 \\ \end{array}ρ1​(i)=iρ2​(i)=(1+5i)mod16ρ3​(i)=(5+3i)mod16ρ4​(i)=7imod16​

    • A + B

  • 第二步

    对缓冲区中的四个字按字右循环位移 1 个字 即新的缓冲区 Buffer′=D′∣∣A′∣∣B′∣∣C′Buffer' = D' || A' || B' || C'Buffer′=D′∣∣A′∣∣B′∣∣C′

也就是说, 一组消息的压缩要经过这样的过程:

轮次 1

/* [abcd k s i] a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
[ABCD  0 7  1][DABC  1 12  2][CDAB  2 17  3][BCDA  3 22  4]
[ABCD  4 7  5][DABC  5 12  6][CDAB  6 17  7][BCDA  7 22  8]
[ABCD  8 7  9][DABC  9 12 10][CDAB 10 17 11][BCDA 11 22 12]
[ABCD 12 7 13][DABC 13 12 14][CDAB 14 17 15][BCDA 15 22 16]

轮次 2

/* [abcd k s i] a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
[ABCD  1 5 17][DABC  6 9 18][CDAB 11 14 19][BCDA  0 20 20]
[ABCD  5 5 21][DABC 10 9 22][CDAB 15 14 23][BCDA  4 20 24]
[ABCD  9 5 25][DABC 14 9 26][CDAB  3 14 27][BCDA  8 20 28]
[ABCD 13 5 29][DABC  2 9 30][CDAB  7 14 31][BCDA 12 20 32]

轮次 3

/* [abcd k s i] a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
[ABCD  5 4 33][DABC  8 11 34][CDAB 11 16 35][BCDA 14 23 36]
[ABCD  1 4 37][DABC  4 11 38][CDAB  7 16 39][BCDA 10 23 40]
[ABCD 13 4 41][DABC  0 11 42][CDAB  3 16 43][BCDA  6 23 44]
[ABCD  9 4 45][DABC 12 11 46][CDAB 15 16 47][BCDA  2 23 48]

轮次 4

/* [abcd k s i] a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
[ABCD  0 6 49][DABC  7 10 50][CDAB 14 15 51][BCDA  5 21 52]
[ABCD 12 6 53][DABC  3 10 54][CDAB 10 15 55][BCDA  1 21 56]
[ABCD  8 6 57][DABC 15 10 58][CDAB  6 15 59][BCDA 13 21 60]
[ABCD  4 6 61][DABC 11 10 62][CDAB  2 15 63][BCDA  9 21 64]

代码实现 Implement

#include <iostream>
#include <string>
#include <stdint.h> // for uint* type
#include <limits.h> // for CHAR_BIT
using namespace std;const uint32_t T[64] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};const unsigned int SHIFT[4][4]{{7, 12, 17, 22},{5, 9, 14, 20},{4, 11, 16, 23},{6, 10, 15, 21}};const uint8_t 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};inline uint32_t Left_Rotate32(uint32_t x, unsigned int num)
{num &= 31;return (x << num) | (x >> (-num & 31));
}inline uint32_t Logic_Function(int Round_i, uint32_t b, uint32_t c, uint32_t d)
{switch (Round_i){case 3:return c ^ (b | ~d);case 2:return b ^ c ^ d;case 1:return (b & d) | (c & ~d);case 0:return (b & c) | (~b & d);}return 0;
}inline unsigned int Substituion(int Round_i, int i)
{switch (Round_i){case 0:return i;case 1:return (1 + 5 * i) % 16;case 2:return (5 + 3 * i) % 16;case 3:return (7 * i) % 16;}return 0;
}
void Round_Function(int Round_i, uint32_t buffer[4], const uint32_t message_block[16])
{// Input:// Logic_Function             - provided by Round_i// T Table[Round_i*16 +(0-16)]    - provided by Round_i// Message Block// Buffer// Output:// void, but UPDATE buffer// Elements:// i = Round_i * 16 + i;// k = substituion(Round_i, i)for (int i = 0; i < 16; i++){// 1. Calculationbuffer[0] += Logic_Function(Round_i, buffer[1], buffer[2], buffer[3]);buffer[0] += message_block[Substituion(Round_i, i)];buffer[0] += T[Round_i * 16 + i];buffer[0] = Left_Rotate32(buffer[0], SHIFT[Round_i][i % 4]);buffer[0] += buffer[1];// 2. Rotate Buffer// Buffer right rotate by 1WORD(32-bit)uint32_t bufferCache = buffer[3];buffer[3] = buffer[2];buffer[2] = buffer[1];buffer[1] = buffer[0];buffer[0] = bufferCache;}return;
}void Hash_MD5(uint32_t chain_vector[4], const uint32_t message_block[16])
{// traverse message_block, MD5 iterationuint32_t buffer[4];memcpy(buffer, chain_vector, 128 / CHAR_BIT);// For loop, i control Round functionfor (int i = 0; i < 4; i++)Round_Function(i, buffer, message_block);// Update chain_vectorfor (int i = 0; i < 4; i++)chain_vector[i] += buffer[i];
}__uint128_t MD5(string _message)
{// 1. Pre-process message// padding & append length info// padding cached array// append messageBITcount, naturally store by litter-endian in C++uint64_t messageLength = _message.length();uint64_t messageBitCount = messageLength * CHAR_BIT;int blockCount = (messageBitCount + 64 - 1) / 512 + 1;uint8_t message[64 * blockCount];memcpy(message, _message.c_str(), messageLength);for (int i = messageLength, j = 0; i < (64 * blockCount - 8); i++)message[i] = PADDING[j++];memcpy(message + (64 * blockCount - 8), &messageBitCount, 64 / CHAR_BIT);uint32_t *messageBuffer = new uint32_t[16];// 2. Init Chain_vector_0 as a const// PAY ATTENTION TO THE IV ORDER!!// WRONG!! uint32_t res[4] = {0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210};uint32_t res[4] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};for (int i = 0; i < blockCount; i++){// Update Message_Blockmemcpy(messageBuffer, message + 64 * i, 64);Hash_MD5(res, messageBuffer);}// Output__uint128_t md5 = 0;for (int i = 0; i < 4; i++)md5 += (__uint128_t)res[i] << (i * 32);delete[] messageBuffer;return md5;
}void MD5_Print(__uint128_t in)
{unsigned char *ptr = (unsigned char *)&in;for (int i = 0; i < 16; i++)printf("%02x", ptr[i]);
}int main(void)
{#ifdef LOCAL_COMPILE// freopen("in", "r", stdin);// freopen("out", "w", stdout);
#endif// user interfacecout << "----------------- MD5 -----------------\n";cout << "INFO: Input a line of text, ended with an enter. (to end program input CTRL + C)\n";cout << "-----------------------\n";string str;while (1){cout << "text: ";getline(cin, str);__uint128_t md5 = MD5(str);cout << "result: ";MD5_Print(md5);cout << "\n-----------------------\n";}return 0;
}

代码测试 Test

参考 Rivest 在 RFC 1321 规范中给出的结果, 对上述代码进行测试.

----------------- MD5 -----------------
INFO: Input a line of text, ended with an enter. (to end program input CTRL + C)
-----------------------
text:
result: d41d8cd98f00b204e9800998ecf8427e
-----------------------
text: a
result: 0cc175b9c0f1b6a831c399e269772661
-----------------------
text: abc
result: 900150983cd24fb0d6963f7d28e17f72
-----------------------
text: message digest
result: f96b697d7cb7938d525a2f31aaf161d0
-----------------------
text: abcdefghijklmnopqrstuvwxyz
result: c3fcd3d76192e4007dfb496cca67e13b
-----------------------
text: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
result: d174ab98d277d9f5a5611c2c9f419d9f
-----------------------
text: 12345678901234567890123456789012345678901234567890123456789012345678901234567890
result: 57edf4a22be3c955ac49da2e2107b67a
-----------------------

与规范中给出的结果一致, 测试通过!

MD5 test suite:
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
d174ab98d277d9f5a5611c2c9f419d9f
MD5 ("123456789012345678901234567890123456789012345678901234567890123456
78901234567890") = 57edf4a22be3c955ac49da2e2107b67a

参考资料 Reference

字节顺序 - 维基百科,自由的百科全书 (wikipedia.org)

MD5 - 维基百科,自由的百科全书 (wikipedia.org)

RFC 1321: The MD5 Message-Digest Algorithm (rfc-editor.org)

MD5 加密算法 - C++ 实现相关推荐

  1. md5加密算法原理及其GO语言实现

    md5加密算法原理及其GO语言实现 MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值 ...

  2. c++Builder XE6 MD5 加密算法 BASE64 URL 编码

    xe6,xe7 BASE64XE6 MD5 加密算法Delphifunction MD5(const texto: string): string; varidmd5: TIdHashMessageD ...

  3. 一种增强的md5加密算法

    一种增强的md5加密算法 2008-06-04 16:26:48 标签:加密算法 md5 一种 增强的 [推送到技术圈] 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一种增强的md5加密算法 ...

  4. python md5加密解密_Python使用MD5加密算法对字符串进行加密操作示例

    本文实例讲述了Python使用MD5加密算法对字符串进行加密操作.分享给大家供大家参考,具体如下: # encoding: utf-8 from __future__ import division ...

  5. Md5加密算法的原理及应用

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后 ...

  6. MD5加密算法与SHA加密算法

    2.MD5加密 2.1 概述 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.该算法的文件号为R ...

  7. kso经验记录 --- c# 之MD5加密算法

    MD5加密算法,是比较常用的,也是比较好用的,不论进行小项目还是大项目都要进行MD5加密,因为这是不可逆的. 下面来记录一下: 直接上算法 using System; using System.Sec ...

  8. MD5加密算法(转)

    一起谈谈MD5加密算法 MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明 ...

  9. 关于Md5加密算法的原理及应用

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后 ...

  10. MD5加密算法的原理和应用

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后 ...

最新文章

  1. Java开发环境的搭建以及使用eclipse从头一步步创建java项目
  2. [读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性
  3. 快速上手seajs——简单易用Seajs
  4. DCMTK:测试衍生图像FG类
  5. SharePoint designer 文件--新建中没有工作流
  6. CodeForces - 1323D Present(思维+数学)
  7. 从今开始,好好学习一下算法!
  8. asp.net core 发布到 docker 容器时文件体积过大及服务端口的配置疑问
  9. python 数据流中的移动平均值_剑指Offer-41-数据流中的中位数
  10. php 中class,PHP5中的类(class) (转)
  11. 为什么hbase里没有表会显示表已经存在_0712-6.2.0-HBase快照异常
  12. 网址路由Routing组件如何在mvc中生成网址
  13. (二)Java中的HashMap与ConcurrentHashMap的区别
  14. 简单易懂实例说明如何设计子网掩码
  15. Android常用的简单代码
  16. 2014年上半年系统集成项目管理工程师真题解析(上午+下午)
  17. vscode调试用的launch.json
  18. STM32开发 --- W25Q128读写、SPI通信
  19. 数据分析新人如何面对繁杂且突然的数据需求
  20. leetcode174.地下城游戏

热门文章

  1. python简史_移动恶意软件简史
  2. 校园网无法拨号的一些解决方案
  3. matlab中sum(,3)求和函数
  4. LightGBM特征重要性画图
  5. 数组-奇数位上都是奇数或者偶数位上都是偶数(牛客网)
  6. 云计算到底算啥?李彦宏、马化腾PK马云
  7. 【python库】:Pillow、PyTorch、Colorama超详细
  8. 常用的数据库备份类型有哪些?
  9. mtk modem log分析
  10. 恶补通信基础知识——OFDM篇