MD算法C++程序设计和实现

1 算法原理概述

MD5,即Message-Digest Algorithm 5 (信息-摘要算法5),是广泛使用的Hash 算法,用于确保信息传输的完整性和一致性。 MD5 使用little-endian(小端模式),输入任意不定长度信息,以 512-bit 进行分组,生成四个32-bit 数据,最后联合输出固定 128-bit 的信息摘要。 其基本过程为:填充、分块、缓冲区初始化、循环压缩、得出结果
基本流程图如下:
在这里插入图片描述

2 总体结构

  • MD5.hpp:定义MD5类,宏定义4轮循环中使用的生成函数,以及循环移位
  • MD5.cpp:实现MD5.hpp中定义的类方法
  • main.cpp:测试文件,数据来自https://www.ietf.org/rfc/rfc1321.txt
    在这里插入图片描述

3 模块分解

3.1 填充及分块模块

在这里插入图片描述

3.2 初始化模块

在这里插入图片描述

3.3 循环压缩模块

在这里插入图片描述

3.4 整数转字符串模块

在这里插入图片描述

3.5 获取MD5模块(总)

在这里插入图片描述

3.6 输入模块

在这里插入图片描述

3.7 输出模块

在这里插入图片描述

4 数据结构

4.1 MD5类

面向对象编程,在类内定义各个变量和函数,可以使架构更加清晰。

4.2 unsigned int

unsigned int为32-bit无符号整数,正好可以对应一个32-bit分组。
在这里插入图片描述

5 编译运行结果

使用了RCF 1321中给出的标准测试样例进行测试:
在这里插入图片描述

6 源代码

MD5.hpp

// MD5.hpp
#include <iostream>

#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
#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)))

typedef unsigned int byte4;
typedef unsigned long long int byte8;

using namespace std;

class MD5{
public:
	MD5(const string &input); 
	void padding();
	void init();
	void transform(byte8 blockNum);
	string toString(byte4 a);
	string getMD5();
private:
	string input; // 输入字符串消息
	byte4* M; // 填充后的用32-bit整数数组表示的32-bit分组
	byte8 L; // L个512-bit(即64-byte)分组
	byte4 A, B, C, D; // MD 缓冲区,4个32-bit 寄存器(A, B, C, D) 
};

MD5.cpp

// MD5.cpp
# include "MD5.hpp"

// 各次迭代运算采用的T值, T[i] = int((2^32)*|sin(i)|) 
const byte4 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};

// 各次迭代运算采用的左循环移位的s值
const byte4 s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
				 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
				 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
				 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21};

// 各轮循环中迭代使用的X[k]的下标
const byte4 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};

MD5::MD5(const string &input){
	this->input = input;
}

/*
 * 填充及分块
 */
void MD5::padding(){
	byte8 len = input.length(); // 输入字节数
	L = ((len + 8) / 64) + 1; // L个512-bit(即64-byte)分组
	byte8 N = L * 16;  // N个32-bit字
	M = new byte4[N];  // 填充后的结果用byte4表示,每个byte4为一个32-bit字
	for(byte4 i = 0; i < N; i++)
		M[i] = 0;  // 所有位先初始化为0
	for(byte4 i = 0; i < len; i++)
		M[i / 4] |= (input[i])<<((i % 4) * 8); // 一个32位整数可保存4个字符信息
	M[len / 4] |= 0x80<<((len%4)*8); // 尾部添加1
	// 尾部附加原始长度
	M[N - 1] |= (len * 8) >> 32;
	M[N - 2] |= len * 8;
}

/*
 * 初始化MD缓存区
 */
void MD5::init(){
	A= 0x67452301;
	B= 0xEFCDAB89;
	C= 0x98BADCFE;
	D= 0x10325476;
}


/*
 * 循环压缩
 */
void MD5::transform(byte8 blockNum){
	byte4 a = A;
	byte4 b = B;
	byte4 c = C;
	byte4 d = D;
	for (int i = 0; i < 64; i++) {
		if(i < 16){
			A = B + ROTATE_LEFT(A + F(B, C, D) + M[blockNum * 16 + k[i]] + T[i], s[i]);
		}else if(i < 32){
			A = B + ROTATE_LEFT(A + G(B, C, D) + M[blockNum * 16 + k[i]] + T[i], s[i]);
		}else if(i < 48){
			A = B + ROTATE_LEFT(A + H(B, C, D) + M[blockNum * 16 + k[i]] + T[i], s[i]);
		}else{
			A = B + ROTATE_LEFT(A + I(B, C, D) + M[blockNum * 16 + k[i]] + T[i], s[i]);
		}
		byte4 temp = D;
		D = C;
		C = B;
		B = A;
		A = temp;
	}
	A += a;
	B += b;
	C += c;
	D += d;
}

/*
 * 把整数转为字符串
 */
string MD5::toString(byte4 a){
	string result;
	char buffer[4];
	for(int i = 0; i < 4; i++){
		snprintf(buffer, sizeof(buffer), "%02x", (a >> i * 8) & 0xff);
		result += buffer;
	}
	return result;
}

/*
 * 获取MD5
 */
string MD5::getMD5(){
	// 第一步:填充及分块
	padding();
	// 第二步:初始化MD缓存区
	init();
	// 第三步:以512-bit 消息分组为单位,每一分组Yq (q= 0, 1, …, L-1) 经过4个循环的压缩
	for(byte8 i = 0; i < L; i++)
		transform(i);
	// 第四步:返回字符串结果
	return toString(A).append(toString(B)).append(toString(C)).append(toString(D));
}

main.cpp

// main.cpp
#include "MD5.hpp"

#define LEN 7

int main(){
    string input[LEN] = {
        "",
        "a",
        "abc",
        "message digest",
        "abcdefghijklmnopqrstuvwxyz",
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "12345678901234567890123456789012345678901234567890123456789012345678901234567890"};

    string expected[LEN] = {
        "d41d8cd98f00b204e9800998ecf8427e", "0cc175b9c0f1b6a831c399e269772661",
        "900150983cd24fb0d6963f7d28e17f72", "f96b697d7cb7938d525a2f31aaf161d0",
        "c3fcd3d76192e4007dfb496cca67e13b", "d174ab98d277d9f5a5611c2c9f419d9f",
        "57edf4a22be3c955ac49da2e2107b67a"};

	for (int i = 0; i < LEN; i++) {
		MD5 md5(input[i]);
		string digest = md5.getMD5();
		cout << "--------------------------------" << endl;
		cout << "Test " << i << ":" << endl;
		cout << "Original Message:  " << input[i] << endl;
		cout << "Expected Result:   " << expected[i] << endl;
		cout << "Calculated Result: " << digest << endl;
		cout << (digest == expected[i] ? "PASS TEST" : "FAILED") << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Runner1st/article/details/88073975