MD5(Message-DigestAlgorithm 5)是计算机中广泛使用的杂凑算法之一。主要可以实现将数据运算后转换为一串固定值,其前身主要有MD2、MD3和MD4算法。MD2算法在1989年由Rivest设计开发,后来由Rogier和Chauvaud发现如果忽略了校验将和MD2产生冲突。为了加强算法的安全性,Rivest在1990年又开发出MD4算法,随后由Denboer和Bosselaers以及其他人很快的发现了攻击MD4版本中第一步和第三步的漏洞,于是MD4就此被淘汰了。1991年Rivest在原有MD4算法的基础上开发出技术更成熟的MD5算法。
MD5算法是一个被广泛使用的称为信息摘要的摘要算法,其主要过程包括了对字符串的填充(Padding)、分段摘要(Digesting)和最终摘要的输出(Outputting)三个步骤。其中填充是指首先需要对信息进行填充,使其字节长度对512求余得到结果为448.填充是在信息的后面填充一个1和无数个0,直到满足上面的长度条件时停止。在填充完成后,再往后面附加一个以64位二进制表示填充前信息的长度,使现在的信息字节长度等于N*512+448+64=(N+1)*512,这样长度恰好可以被512整除满足后面处理中对信息长度可以划分为N+1个完整分组的要求。
摘要算法的两个重要评价指标是摘要强度和速度。摘要强度是指算法抵抗各种攻击的能力,而加密速度就是摘要产生的速度。通常摘要强度和摘要处理速度是一对互斥的参数,即强度越大不可避免的会增加摘要产生的时间即降低摘要产生的速度。而在摘要强度一定的情况下加密速度在实现中可以使用各种优化技术最大限度地发挥算法速度的潜力。
MD5算法的应用领域非常广泛,包括数据加密、数据校验、数字签名等。在Linux系统中,主要应用MD5对明文进行加密,在文件的上传和下载中为了校验文件是否被改动过,需要提供文件的MD5散列值,而在数字签名中先要利用MD5计算报文摘要。
MD5算法是迭代型Hash函数结构,输入一个任意长度的信息,将输出128比特的消息摘要。算法的核心为分段摘要过程,即图1中所示的由A、B、C、D到经过摘要运算处理过后的结果A、B、C、D的过程。
其算法实现过程:
(1)、补位:MD5算法中首先对信息加以填充,让其位数满足对512求余后等于448.即填充后的消息长度等于512的某倍数减掉 64,即使原来消息值得长度满足条件也需要进行填充,长度填充的方式是在信息的末尾添加一个1和无数个0,直至满足条件为止.
(2)、补明文长度:通过第一步补位后,将预留的64比特信息来表示消息在被填充前的长度,并附加在信息之后,使其总长度变成512的整数被,这样方便后面对信息的分组。如果某消息的原始长度超出2^64,则以2^64进行取模。
通过以上两步执行完之后,可以对消息进行分组,每组长度均为512比特,而每组值又可表示为16个32比特的字。
(3)、初始化:算法中采用128比特的缓冲区存放中间结果以及最终的hash值,缓冲区利用4个32比特的寄存器(A,B,C,D)充当,对MD5种四个32位的链接变量(Chaining Variable)进行初始化,分别为:state[0]=0x67452301, state[1]=0xefcdab89, state[2]=0x98badcfe,state[3]=0x10325476
(4)、输入:将4个链接变量的值复制到另外四个变量中。a=state[0], b=state[1], c=state[2], d=state[3],由这四个新变量及每个分割后的512位的子消息组作为算法的输入值,进入到算法的四轮循环,而循环次数则由消息分组后的数目决定。
(5)、运算:算法的主循环中包括4轮,每一轮都要执行16次操作。而每次操作就是对a,b,c,d四个变量中的其中3个做一次非线性的函数运算,接着将结果加上第4个变量,以及一个子分组和一个常数(常数来自常数表,该常数表共有64个元素,第i个常数即为2^32*abs(sin(i))),再将整个结果左循环移动一个不定的数,最后再加上a,b,c或d中之一,这个结果取代变量a,b,c或d,见图1和图2.
图 1 MD5 处理流程
图2 MD5分段摘要函数处理流程
其中m[i],表示子消息组的第i个分组,i为0--15;T[j]表示该步的常数。
(6)、输出:当消息的N个分组均被处理完成后,最后的压缩函数将输出一个128位的散列值,即为消息摘要。
利用Hash函数可以将任意长度的消息压缩转换为固定长度的hash值,俗称消息摘要或数字指纹,可以直接用于数据的完整性检测。
MD2, MD4, andMD5 are cryptographic hash functions with a 128 bit output.
1. MD5(): compute the MD5 messagedigest of the B<n> bytes at B<d> and place it in B<md> (whichmust have space for MD5_DIGEST_LENGTH == 16 bytes of output). If B<md> isNULL, the digest is placed in a static array.
The following functions may be used if the message isnot completely stored in memory:
2. MD5_Init(): initializes aB<MD5_CTX> structure.
3. MD5_Update(): can be calledrepeatedly with chunks of the message to be hashed (B<len> bytes atB<data>).
4. MD5_Final():places the message digest in B<md>, which must have space for MD5_DIGEST_LENGTH== 16 bytes of output, and erases the B<MD5_CTX>.以下是测试代码:
cryptotest.h:
#ifndef _CRYPTOTEST_H_
#define _CRYPTOTEST_H_
#include <string>
using namespace std;
typedef enum {
GENERAL = 0,
ECB,
CBC,
CFB,
OFB,
TRIPLE_ECB,
TRIPLE_CBC
}CRYPTO_MODE;
string DES_Encrypt(const string cleartext, const string key, CRYPTO_MODE mode);
string DES_Decrypt(const string ciphertext, const string key, CRYPTO_MODE mode);
string RC4_Encrypt(const string cleartext, const string key);
string RC4_Decrypt(const string ciphertext, const string key);
string MD5_Digest(const string cleartext);
#endif //_CRYPTOTEST_H_
md5test.cpp:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <iomanip>
#include <stdlib.h>
#include <openssl/md5.h>
#include "cryptotest.h"
using namespace std;
string MD5_Digest(const string cleartext)
{
string strDigest;
unsigned char tmp[16] = {0};
#if 0
MD5((const unsigned char*)cleartext.c_str(), cleartext.length(), tmp);
#else
MD5_CTX c;
MD5_Init(&c);
MD5_Update(&c, cleartext.c_str(), cleartext.length());
MD5_Final(tmp, &c);
#endif
char* tmp1 = new char[32 + 1];
memset(tmp1, 0, 32 + 1);
for(int i = 0; i < 16; i++)
sprintf(&(tmp1[i*2]), "%02x", tmp[i]);
//cout<<hex<<setw(2)<<setfill('0')<<(int)tmp[i];
strDigest = (char*)tmp1;
delete [] tmp1;
return strDigest;
}
main.cpp:
#include "stdafx.h"
#include "cryptotest.h"
#include "TestMemory.h"
#include <iostream>
#include <string>
using namespace std;
void test_MD5()
{
string strSrc[7] = {"", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"12345678901234567890123456789012345678901234567890123456789012345678901234567890"};
string strDigest[7] = {"d41d8cd98f00b204e9800998ecf8427e",
"0cc175b9c0f1b6a831c399e269772661",
"900150983cd24fb0d6963f7d28e17f72",
"f96b697d7cb7938d525a2f31aaf161d0",
"c3fcd3d76192e4007dfb496cca67e13b",
"d174ab98d277d9f5a5611c2c9f419d9f",
"57edf4a22be3c955ac49da2e2107b67a"};
for (int i = 0; i < 7; i++) {
string str = MD5_Digest(strSrc[i]);
cout<<str<<endl;
if (strcmp(strDigest[i].c_str(), str.c_str()) != 0)
cout<<"i = "<<i<<" MD5 error!"<<endl;
}
}
int main(int argc, char* argv[])
{
test_MD5();
cout<<"ok!!!"<<endl;
return 0;
}
MD5理论摘自:
1. 《基于简化MD5摘要技术快照差分算法的研究》
2. 《基于MD5改进算法的安全教师博客系统设计及开发》