MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。

将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。

MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。

对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

   在MD5算法中,首先需要对信息进行填充,使其位长对512求余的结果等于448。因此,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后,在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,信息的位长=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。总体流程如下图所示,表示第i个分组,每次的运算都由前一轮的128位结果值和第i块512bit值进行运算。初始的128位值为初试链接变量,这些参数用于第一轮的运算,以大端字节序来表示,他们分别为:A=0x01234567,B=0x89ABCDEF,C=0xFEDCBA98,D=0x76543210。

C++代码实现

1. 《MD5.h》

#pragma once

/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER; /* UINT2 defines a two byte word */
typedef unsigned short int UINT2; /* UINT4 defines a four byte word */
typedef unsigned long int UINT4; class cMd5
{
private:
unsigned int m_Content[16];
unsigned int m_ContentLen;
unsigned int m_TotalLen;
static unsigned int m_T[64];
unsigned int m_A, m_B, m_C, m_D; private:
inline void FF(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s);
inline void GG(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s);
inline void HH(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s);
inline void II(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s);
inline unsigned int roateLeft(unsigned int a, unsigned int c);
void transform(); /* MD5 context. */
typedef struct _MD5_CTX{
UINT4 state[4]; /* state (ABCD) */
UINT4 count[2]; /* bit ,2^64(Low before) */
unsigned char buffer[64]; /* input buffer */
} MD5_CTX; void MD5Init (MD5_CTX *);
void MD5Update (MD5_CTX *, unsigned char *, unsigned int);
void MD5Final (unsigned char [16], MD5_CTX *);
void MD5String(char *string, long buflen, char digest[]); static void MD5Transform (UINT4 [4], unsigned char [64]);
static void Encode (unsigned char *, UINT4 *, unsigned int);
static void Decode (UINT4 *, unsigned char *, unsigned int);
static void MD5_memcpy (POINTER, POINTER, unsigned int);
static void MD5_memset (POINTER, int, unsigned int); public:
cMd5();
virtual ~cMd5();
//initialize
void beginCal();
//Show that no longer input data, will add a filling
void endCal();
//Is initialized, input data, in endCal call before, can continue to add data
void addData(unsigned char* data, unsigned int size);
//End after operation, get the code
unsigned char* getCode();
void clearMemoryHelper(char* p);
};

2. 《MD5.CPP》

#include "stdafx.h"
#include "Md5.h"
#include <string.h>
#include <stdio.h> unsigned int cMd5::m_T[64] = {
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
}; cMd5::cMd5()
{ }
cMd5::~cMd5()
{ } void cMd5::FF(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s)
{
a = b + roateLeft(a + ((b & c) | ((~b) & d)) + m_Content[k] + m_T[i], s);
}
void cMd5::GG(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s)
{
a = b + roateLeft(a + ((b & d) | (c & (~d))) + m_Content[k] + m_T[i], s);
}
void cMd5::HH(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s)
{
a = b + roateLeft(a + (b ^ c ^ d) + m_Content[k] + m_T[i], s);
}
void cMd5::II(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, int k, int i, int s)
{
a = b + roateLeft(a + (c ^ (b | (~d))) + m_Content[k] + m_T[i], s);
}
unsigned int cMd5::roateLeft(unsigned int a, unsigned int c)
{
return ( (a << c) | (a >> (32-c)) );
}
void cMd5::transform()
{
unsigned int AA, BB, CC, DD;
AA = m_A;
BB = m_B;
CC = m_C;
DD = m_D;
//1 round
FF(AA, BB, CC, DD, 0, 0, 7);
FF(DD, AA, BB, CC, 1, 1, 12);
FF(CC, DD, AA, BB, 2, 2, 17);
FF(BB, CC, DD, AA, 3, 3, 22);
FF(AA, BB, CC, DD, 4, 4, 7);
FF(DD, AA, BB, CC, 5, 5, 12);
FF(CC, DD, AA, BB, 6, 6, 17);
FF(BB, CC, DD, AA, 7, 7, 22);
FF(AA, BB, CC, DD, 8, 8, 7);
FF(DD, AA, BB, CC, 9, 9, 12);
FF(CC, DD, AA, BB, 10, 10, 17);
FF(BB, CC, DD, AA, 11, 11, 22);
FF(AA, BB, CC, DD, 12, 12, 7);
FF(DD, AA, BB, CC, 13, 13, 12);
FF(CC, DD, AA, BB, 14, 14, 17);
FF(BB, CC, DD, AA, 15, 15, 22);
//2 round
GG(AA, BB, CC, DD, 1, 16, 5);
GG(DD, AA, BB, CC, 6, 17, 9);
GG(CC, DD, AA, BB, 11, 18, 14);
GG(BB, CC, DD, AA, 0, 19, 20);
GG(AA, BB, CC, DD, 5, 20, 5);
GG(DD, AA, BB, CC, 10, 21, 9);
GG(CC, DD, AA, BB, 15, 22, 14);
GG(BB, CC, DD, AA, 4, 23, 20);
GG(AA, BB, CC, DD, 9, 24, 5);
GG(DD, AA, BB, CC, 14, 25, 9);
GG(CC, DD, AA, BB, 3, 26, 14);
GG(BB, CC, DD, AA, 8, 27, 20);
GG(AA, BB, CC, DD, 13, 28, 5);
GG(DD, AA, BB, CC, 2, 29, 9);
GG(CC, DD, AA, BB, 7, 30, 14);
GG(BB, CC, DD, AA, 12, 31, 20);
//3 round
HH(AA, BB, CC, DD, 5, 32, 4);
HH(DD, AA, BB, CC, 8, 33, 11);
HH(CC, DD, AA, BB, 11, 34, 16);
HH(BB, CC, DD, AA, 14, 35, 23);
HH(AA, BB, CC, DD, 1, 36, 4);
HH(DD, AA, BB, CC, 4, 37, 11);
HH(CC, DD, AA, BB, 7, 38, 16);
HH(BB, CC, DD, AA, 10, 39, 23);
HH(AA, BB, CC, DD, 13, 40, 4);
HH(DD, AA, BB, CC, 0, 41, 11);
HH(CC, DD, AA, BB, 3, 42, 16);
HH(BB, CC, DD, AA, 6, 43, 23);
HH(AA, BB, CC, DD, 9, 44, 4);
HH(DD, AA, BB, CC, 12, 45, 11);
HH(CC, DD, AA, BB, 15, 46, 16);
HH(BB, CC, DD, AA, 2, 47, 23);
//4 round
II(AA, BB, CC, DD, 0, 48, 6);
II(DD, AA, BB, CC, 7, 49, 10);
II(CC, DD, AA, BB, 14, 50, 15);
II(BB, CC, DD, AA, 5, 51, 21);
II(AA, BB, CC, DD, 12, 52, 6);
II(DD, AA, BB, CC, 3, 53, 10);
II(CC, DD, AA, BB, 10, 54, 15);
II(BB, CC, DD, AA, 1, 55, 21);
II(AA, BB, CC, DD, 8, 56, 6);
II(DD, AA, BB, CC, 15, 57, 10);
II(CC, DD, AA, BB, 6, 58, 15);
II(BB, CC, DD, AA, 13, 59, 21);
II(AA, BB, CC, DD, 4, 60, 6);
II(DD, AA, BB, CC, 11, 61, 10);
II(CC, DD, AA, BB, 2, 62, 15);
II(BB, CC, DD, AA, 9, 63, 21);
m_A = m_A + AA;
m_B = m_B + BB;
m_C = m_C + CC;
m_D = m_D + DD;
}
//initialize
void cMd5::beginCal()
{
memset(m_Content, 0, 64);
m_ContentLen = 0;
m_TotalLen = 0;
m_A = 0x67452301;
m_B = 0xEFCDAB89;
m_C = 0x98BADCFE;
m_D = 0x10325476;
}
void cMd5::endCal()
{
memset((char*)m_Content + m_ContentLen, 0x80, 1);
if (m_ContentLen < 56){
memset((char*)m_Content + m_ContentLen + 1, 0, 55 - m_ContentLen);
}
else{
memset((char*)m_Content + m_ContentLen + 1, 0, 63 - m_ContentLen);
transform();
memset((char*)m_Content, 0, 56);
}
unsigned int h, l;
h = 0;
l = m_TotalLen;
h |= ((l & 0x80000000) >> 31);
h <<= 1;
l <<= 1;
h |= ((l & 0x80000000) >> 31);
h <<= 1;
l <<= 1;
h |= ((l & 0x80000000) >> 31);
h <<= 1;
l <<= 1;
memcpy(m_Content + 14, &l, 4);
memcpy(m_Content + 15, &h, 4);
transform();
}
void cMd5::addData(unsigned char* data, unsigned int size)
{
unsigned int i = 0;
unsigned int remain = 64 - m_ContentLen;
if (remain > size)
remain = size;
memcpy(((char*)m_Content) + m_ContentLen, data, remain);
i += remain;
m_ContentLen += remain;
m_TotalLen += remain;
if (m_ContentLen < 64)
return;
transform();
while (i + 64 <= size)
{
memcpy(m_Content, data + i, 64);
transform();
i += 64;
m_TotalLen += 64;
}
m_ContentLen = size - i;
m_TotalLen += m_ContentLen;
memcpy(m_Content, data + i, m_ContentLen);
}
unsigned char* cMd5::getCode()
{
unsigned char * code = new unsigned char[16];
memcpy(code, &m_A, 4);
memcpy(code + 4, &m_B, 4);
memcpy(code + 8, &m_C, 4);
memcpy(code + 12, &m_D, 4);
return code;
}
void cMd5::clearMemoryHelper(char* p)
{
delete []p;
}

文/yanxin8原创,获取更多信息请访问http://yanxin8.com/278.html

算法系列9《MD5》的更多相关文章

  1. 2.Java 加解密技术系列之 MD5

    Java 加解密技术系列之 MD5 序 背景 正文 结束语 序 上一篇文章中,介绍了最基础的编码方式 — — BASE64,也简单的提了一下编码的原理.这篇文章继续加解密的系列,当然也是介绍比较基础的 ...

  2. 常用校验算法CRC、MD5、SHA_转

    1.算法概述 数据摘要算法是密码学算法中非常重要的一个分支,它通过对所有数据提取指纹信息以实现数据签名.数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密.数据摘要算法也被称为哈希(H ...

  3. 简答一波 HashMap 常见八股面试题 —— 算法系列(2)

    请点赞,你的点赞对我意义重大,满足下我的虚荣心. Hi,我是小彭.本文已收录到 GitHub · Android-NoteBook 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注 ...

  4. JAVA算法系列 冒泡排序

    java算法系列之排序 手写冒泡 冒泡算是最基础的一个排序算法,简单的可以理解为,每一趟都拿i与i+1进行比较,两个for循环,时间复杂度为 O(n^2),同时本例与选择排序进行了比较,选择排序又叫直 ...

  5. JAVA算法系列 快速排序

    java算法系列之排序 手写快排 首先说一下什么是快排,比冒泡效率要高,快排的基本思路是首先找到一个基准元素,比如数组中最左边的那个位置,作为基准元素key,之后在最左边和最右边设立两个哨兵,i 和 ...

  6. javascript实现数据结构与算法系列:栈 -- 顺序存储表示和链式表示及示例

    栈(Stack)是限定仅在表尾进行插入或删除操作的线性表.表尾为栈顶(top),表头为栈底(bottom),不含元素的空表为空栈. 栈又称为后进先出(last in first out)的线性表. 堆 ...

  7. 三白话经典算法系列 Shell排序实现

    山是包插入的精髓排序排序,这种方法,也被称为窄增量排序.因为DL.Shell至1959提出命名. 该方法的基本思想是:先将整个待排元素序列切割成若干个子序列(由相隔某个"增量"的元 ...

  8. Atitit s2018.6 s6 doc list on com pc.docx Atitit s2018.6 s6 doc list on com pc.docx  Aitit algo fix 算法系列补充.docx Atiitt 兼容性提示的艺术 attilax总结.docx Atitit 应用程序容器化总结 v2 s66.docx Atitit file cms api

    Atitit s2018.6 s6  doc list on com pc.docx Atitit s2018.6 s6  doc list on com pc.docx  Aitit algo fi ...

  9. 【C#实现漫画算法系列】-判断 2 的乘方

    微信上关注了算法爱好者这个公众号,有一个漫画算法系列的文章生动形象,感觉特别好,给大家推荐一下(没收过广告费哦),原文链接:漫画算法系列.也看到了许多同学用不同的语言来实现算法,作为一枚C#资深爱好的 ...

  10. 玩转算法系列--图论精讲 面试升职必备(Java版)

    第1章 和bobo老师一起,玩转图论算法欢迎大家来到我的新课程:<玩转图论算法>.在这个课程中,我们将一起完整学习图论领域的经典算法,培养大家的图论建模能力.通过这个课程的学习,你将能够真 ...

随机推荐

  1. 初探接口测试框架--python系列2

    点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是 ...

  2. 百度富文本编辑器UEditor安装配置全过程

    网站开发时富文本编辑器是必不可少的,他可以让用户自行编辑内容的样式然后上传到后台!下面我们来介绍如何安装使用百度富文本编辑器 一.下载并且设置百度富文本编辑器的样式     你可以去百度UEditor ...

  3. APP完整的启动流程

    0.加载+load方法 1.执行Main函数 2.执行UIApplicationMain函数. 3.创建UIApplication对象,并设置UIApplicationMain对象的代理.UIAppl ...

  4. Android开发如何去除标题栏title

    虽然是一个小问题,今天遇到了,也就写下来吧.防止自己忘掉. 取消标题栏的方式有两种,一种是在代码添加,另一种是在AndroidManifest.xml里面添加. 1.在代码中实现:在此方法setCon ...

  5. ERDAS 2013与ArcGIS10.1安装时的兼容性问题

    在Regedit中HKEY_LOCAL_MACHINE->SOFTWARE->FLEXlm License Manager下新建一个“ERDAS License Manager”,然后按照 ...

  6. Oracle笔记 三、function 、select

    Scott表下有这么几个常用的表,而且还带有数据.分别是emp.dept.salgrade: 1.查看表结构用desc desc emp; 2.空表dual,最常用的空表,如: select 2 * ...

  7. Boost C++: 网络编程1

    #include <iostream> #include <boost/asio.hpp> #include <boost/config/compiler/visualc ...

  8. 4种kill某个用户所有进程的方法

    在linux系统管理中,我们有时候需要kill掉某个用户的所有进程,初学者一般先查询出用户的所有pid,然后一条条kill掉,或者写好一个脚本,实际上方法都有现成的,这边有4种方法,我们以kill用户 ...

  9. PO、BO、VO、DTO、POJO、DAO的区别

    PO: 基本上就是Entity了 persistant object持久对象 最形象的理解就是一个PO就是数据库中的一条记录. 好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象. ---- ...

  10. 计算2的n次方的三种方法(C语言实现)

    C代码如下: #include <stdio.h> int func1(int n) { <<n; } int func2(int n) { ) { ; } )*; } int ...