1.问题描述
MD5以512比特一块的方式处理输入的消息文本,每个块又划分为十六个32比特的子块。算法的输出由四个32比特的块组成,将它们级联成一个128比特的Hash值。
①首先填充消息使填充后的长度恰好为一个比512的倍数小64的数。填充方法是附一个“1”在消息后面,再补多个“0”。然后,在其后附上64比特的消息长度(填充前)的二进制表示。算法中使用了四个32比特的变量A、B、C、D,先把这四个变量初始化为:
A=01234567,B=89ABCDEF,C=FEDCBA98,D=76543210
称它们为链接变量。
① 接着进行算法的主循环,循环的次数是消息中512比特的块的数目。
将上面四个变量复制到另外的变量中:A到AA,B到BB,C到CC,D到DD。
主循环有四轮, 每一轮由16次操作组成。F、G、H、I函数,FF、GG、HH、II四种操作详见教材各密码学参考书。所有这些步骤进行完之后,将A、B、C、D分别加上AA、BB、CC、DD,然后用下一块数据继续进行算法。
③最后的输出是A、B、C、D的级联。
2.基本要求
以MD5(x)的形式实现,x为01串。
3. 实现提示
注意消息文本、各种变量的类型及其类型转换。
MD5.h
#include <stdio.h> #include <stdint.h> #include <string.h> #include <assert.h> #include<iostream> #define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n)))) /*MD5的结果数据长度*/ static const unsigned int MD5_HASH_SIZE = 16; /*每次处理的BLOCK的大小*/ static const unsigned int MD5_BLOCK_SIZE = 64; //================================================================================================ /*MD5的算法*/ /*md5算法的上下文,保存一些状态,中间数据,结果*/ typedef struct md5_ctx { /*处理的数据的长度*/ unsigned __int64 length; /*还没有处理的数据长度*/ unsigned __int64 unprocessed; /*取得的HASH结果(中间数据)*/ unsigned int hash[4]; } md5_ctx; static void md5_init(md5_ctx *ctx) { ctx->length = 0; ctx->unprocessed = 0; /* initialize state */ /*不要奇怪为什么初始数值与参考数值不同,这是因为我们使用的数据结构的关系,大的在低位,小的在高位,8位8位一读*/ ctx->hash[0] = 0x67452301; /*应该这样读0x01234567*/ ctx->hash[1] = 0xefcdab89; /*0x89abcdef*/ ctx->hash[2] = 0x98badcfe; /*0xfedcba98*/ ctx->hash[3] = 0x10325476; /*0x76543210*/ } #define MD5_F(x, y, z) ((((y) ^ (z)) & (x)) ^ (z)) #define MD5_G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define MD5_H(x, y, z) ((x) ^ (y) ^ (z)) #define MD5_I(x, y, z) ((y) ^ ((x) | (~z))) /* 一共4轮,每一轮使用不同函数*/ #define MD5_ROUND1(a, b, c, d, x, s, ac) { \ (a) += MD5_F((b), (c), (d)) + (x) + (ac); \ (a) = ROTL32((a), (s)); \ (a) += (b); \ } #define MD5_ROUND2(a, b, c, d, x, s, ac) { \ (a) += MD5_G((b), (c), (d)) + (x) + (ac); \ (a) = ROTL32((a), (s)); \ (a) += (b); \ } #define MD5_ROUND3(a, b, c, d, x, s, ac) { \ (a) += MD5_H((b), (c), (d)) + (x) + (ac); \ (a) = ROTL32((a), (s)); \ (a) += (b); \ } #define MD5_ROUND4(a, b, c, d, x, s, ac) { \ (a) += MD5_I((b), (c), (d)) + (x) + (ac); \ (a) = ROTL32((a), (s)); \ (a) += (b); \ } /* 每块进行一次操作,每次进行四轮 每轮m0~m15 将每一512字节细分成16个小组,每个小组64位(8个字节) yi=2^32abs(sin(i+1)))的整数部分 */ static void md5_process_block(unsigned int state[4], const unsigned int block[MD5_BLOCK_SIZE / 4]) // int 占4个字节 { register unsigned a, b, c, d; a = state[0]; b = state[1]; c = state[2]; d = state[3]; const unsigned int *x = block; MD5_ROUND1(a, b, c, d, x[ 0], 7, 0xd76aa478); MD5_ROUND1(d, a, b, c, x[ 1], 12, 0xe8c7b756); MD5_ROUND1(c, d, a, b, x[ 2], 17, 0x242070db); MD5_ROUND1(b, c, d, a, x[ 3], 22, 0xc1bdceee); MD5_ROUND1(a, b, c, d, x[ 4], 7, 0xf57c0faf); MD5_ROUND1(d, a, b, c, x[ 5], 12, 0x4787c62a); MD5_ROUND1(c, d, a, b, x[ 6], 17, 0xa8304613); MD5_ROUND1(b, c, d, a, x[ 7], 22, 0xfd469501); MD5_ROUND1(a, b, c, d, x[ 8], 7, 0x698098d8); MD5_ROUND1(d, a, b, c, x[ 9], 12, 0x8b44f7af); MD5_ROUND1(c, d, a, b, x[10], 17, 0xffff5bb1); MD5_ROUND1(b, c, d, a, x[11], 22, 0x895cd7be); MD5_ROUND1(a, b, c, d, x[12], 7, 0x6b901122); MD5_ROUND1(d, a, b, c, x[13], 12, 0xfd987193); MD5_ROUND1(c, d, a, b, x[14], 17, 0xa679438e); MD5_ROUND1(b, c, d, a, x[15], 22, 0x49b40821); MD5_ROUND2(a, b, c, d, x[ 1], 5, 0xf61e2562); MD5_ROUND2(d, a, b, c, x[ 6], 9, 0xc040b340); MD5_ROUND2(c, d, a, b, x[11], 14, 0x265e5a51); MD5_ROUND2(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); MD5_ROUND2(a, b, c, d, x[ 5], 5, 0xd62f105d); MD5_ROUND2(d, a, b, c, x[10], 9, 0x2441453); MD5_ROUND2(c, d, a, b, x[15], 14, 0xd8a1e681); MD5_ROUND2(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); MD5_ROUND2(a, b, c, d, x[ 9], 5, 0x21e1cde6); MD5_ROUND2(d, a, b, c, x[14], 9, 0xc33707d6); MD5_ROUND2(c, d, a, b, x[ 3], 14, 0xf4d50d87); MD5_ROUND2(b, c, d, a, x[ 8], 20, 0x455a14ed); MD5_ROUND2(a, b, c, d, x[13], 5, 0xa9e3e905); MD5_ROUND2(d, a, b, c, x[ 2], 9, 0xfcefa3f8); MD5_ROUND2(c, d, a, b, x[ 7], 14, 0x676f02d9); MD5_ROUND2(b, c, d, a, x[12], 20, 0x8d2a4c8a); MD5_ROUND3(a, b, c, d, x[ 5], 4, 0xfffa3942); MD5_ROUND3(d, a, b, c, x[ 8], 11, 0x8771f681); MD5_ROUND3(c, d, a, b, x[11], 16, 0x6d9d6122); MD5_ROUND3(b, c, d, a, x[14], 23, 0xfde5380c); MD5_ROUND3(a, b, c, d, x[ 1], 4, 0xa4beea44); MD5_ROUND3(d, a, b, c, x[ 4], 11, 0x4bdecfa9); MD5_ROUND3(c, d, a, b, x[ 7], 16, 0xf6bb4b60); MD5_ROUND3(b, c, d, a, x[10], 23, 0xbebfbc70); MD5_ROUND3(a, b, c, d, x[13], 4, 0x289b7ec6); MD5_ROUND3(d, a, b, c, x[ 0], 11, 0xeaa127fa); MD5_ROUND3(c, d, a, b, x[ 3], 16, 0xd4ef3085); MD5_ROUND3(b, c, d, a, x[ 6], 23, 0x4881d05); MD5_ROUND3(a, b, c, d, x[ 9], 4, 0xd9d4d039); MD5_ROUND3(d, a, b, c, x[12], 11, 0xe6db99e5); MD5_ROUND3(c, d, a, b, x[15], 16, 0x1fa27cf8); MD5_ROUND3(b, c, d, a, x[ 2], 23, 0xc4ac5665); MD5_ROUND4(a, b, c, d, x[ 0], 6, 0xf4292244); MD5_ROUND4(d, a, b, c, x[ 7], 10, 0x432aff97); MD5_ROUND4(c, d, a, b, x[14], 15, 0xab9423a7); MD5_ROUND4(b, c, d, a, x[ 5], 21, 0xfc93a039); MD5_ROUND4(a, b, c, d, x[12], 6, 0x655b59c3); MD5_ROUND4(d, a, b, c, x[ 3], 10, 0x8f0ccc92); MD5_ROUND4(c, d, a, b, x[10], 15, 0xffeff47d); MD5_ROUND4(b, c, d, a, x[ 1], 21, 0x85845dd1); MD5_ROUND4(a, b, c, d, x[ 8], 6, 0x6fa87e4f); MD5_ROUND4(d, a, b, c, x[15], 10, 0xfe2ce6e0); MD5_ROUND4(c, d, a, b, x[ 6], 15, 0xa3014314); MD5_ROUND4(b, c, d, a, x[13], 21, 0x4e0811a1); MD5_ROUND4(a, b, c, d, x[ 4], 6, 0xf7537e82); MD5_ROUND4(d, a, b, c, x[11], 10, 0xbd3af235); MD5_ROUND4(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); MD5_ROUND4(b, c, d, a, x[ 9], 21, 0xeb86d391); state[0] += a; state[1] += b; state[2] += c; state[3] += d; } /* 512位每块,分块处理 64字节 */ static void md5_update(md5_ctx *ctx, const unsigned char *buf, unsigned int size) { /*为什么不是=,因为在某些环境下,可以多次调用zen_md5_update,但这种情况,必须保证前面的调用,每次都没有unprocessed*/ ctx->length += size; /*每个处理的块都是64字节*/ while (size >= MD5_BLOCK_SIZE) { md5_process_block(ctx->hash, reinterpret_cast<const unsigned int *>(buf)); buf += MD5_BLOCK_SIZE; /*buf指针每一次向后挪动64*/ size -= MD5_BLOCK_SIZE; /*每一次处理64个字符*/ } ctx->unprocessed = size; /*未处理的字符数数目记录下来*/ } static void md5_final(md5_ctx *ctx, const unsigned char *buf, unsigned int size, unsigned char *result) { unsigned int message[MD5_BLOCK_SIZE / 4]; memset(message, 0 ,(MD5_BLOCK_SIZE / 4) * sizeof(unsigned int)); /*保存剩余的数据,我们要拼出最后1个(或者两个)要处理的块,前面的算法保证了,最后一个块肯定小于64个字节*/ if (ctx->unprocessed) { memcpy(message, buf + size - ctx->unprocessed, static_cast<unsigned int>( ctx->unprocessed)); /*================================================================================ 这里的memcpy复制很有趣,是按照字节复制比如说buf --- 0x11 0x14 0xab 0x23 0xcd | ctx>unprocessed_=5 现在copy至 message --- 0x23ab1411 0x000000cd 这样的话,下面的也很好解释了! =================================================================================*/ } /*================================================================================= 用法:static_cast < type-id > ( expression ) 该运算符把expression转换为type-id类型 ==================================================================================*/ /*得到0x80要添加在的位置(在unsigned int 数组中)*/ unsigned int index = ((unsigned int)ctx->length & 63) >> 2; /*一次性处理64个unsigned int型数据,(unsigned int)ctx->length_ & 63求出余下多少未处理的字符*/ unsigned int shift = ((unsigned int)ctx->length & 3) * 8; /*一个message里面可以放置4个字符数据,找到应该移动的位数*/ /*添加0x80进去,并且把余下的空间补充0*/ message[index++] ^= 0x80 << shift; /*^ 位异或*/ /*如果这个block还无法处理,其后面的长度无法容纳长度64bit,那么先处理这个block*/ if (index > 14) { while (index < 16) { message[index++] = 0; } md5_process_block(ctx->hash, message); index = 0; } /*补0*/ while (index < 14) { message[index++] = 0; } /*保存长度,注意是bit位的长度*/ unsigned __int64 data_len = (ctx->length) << 3; message[14] = (unsigned int) (data_len & 0x00000000FFFFFFFF); message[15] = (unsigned int) ((data_len & 0xFFFFFFFF00000000ULL) >> 32); md5_process_block(ctx->hash, message); memcpy(result, &ctx->hash, MD5_HASH_SIZE); } unsigned char* md5(const unsigned char *buf, unsigned int size, unsigned char result[MD5_HASH_SIZE]) { md5_ctx ctx; md5_init(&ctx); /*初始化*/ md5_update(&ctx, buf, size); md5_final(&ctx, buf, size, result); return result; }
main.cpp
#include "md5.h" #include <iostream> #include"stdlib.h" #include <string> using namespace std; int main() { string str; cout << "请输入数据:" << endl; cin >> str; unsigned char result[16] ={0}; //char 在C++和Java 占一个字节 md5((unsigned char*)str.c_str(), str.length(), result);//c_str()指向字符指针常量 cout << "MD5 是 "; for (int j = 0; j < 16; j++) printf ("%02X", result[j]); cout << "."<< endl; system ("pause"); return 0; }