哈希算法及其MD5


       Hash,一般翻译做"散列”,也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值。也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系。        
       在信息安全领域中应用的Hash算法,还需要满足其他关键特性:
       第一当然是单向性(one-way),从预映射,能够简单迅速的得到散列值,而在计算上不可能构造一个预映射,使其散列结果等于某个特定的散列值,即构造相应的M=H-1(h)不可行。这样,散列值就能在统计上唯一的表征输入值,因此,密码学上的 Hash 又被称为"消息摘要(messagedigest)",就是要求能方便的将"消息"进行"摘要",但在"摘要"中无法得到比"摘要"本身更多的关于"消息"的信息。
       第二是抗冲突性(collision-resistant),即在统计上无法产生2个散列值相同的预映射。给定M,计算上无法找到M',满足H(M)=H(M') ,此谓弱抗冲突性;计算上也难以寻找一对任意的M和M',使满足H(M)=H(M'),此谓强抗冲突性。要求"强抗冲突性"主要是为了防范 所谓"生日攻击(birthdayattack)",在一个10人的团体中,你能找到和你生日相同的人的概率是2.4%,而在同一团体中,有2人生日相同的概率是11.7%。类似的,当预映射的空间很大的情况下,算法必须有足够的强度来保证不能轻易找到"相同生日"的人。
       第三是映射分布均匀性和差分分布均匀性,散列结果中,为 0 的 bit 和为 1 的 bit ,其总数应该大致相等;输入中一个 bit的变化,散列结果中将有一半以上的 bit 改变,这又叫做"雪崩效应(avalanche effect)";要实现使散列结果中出现 1bit的变化,则输入中至少有一半以上的 bit 必须发生变化。其实质是必须使输入中每一个 bit 的信息, 尽量均匀的反映到输出的每一个 bit上去;输出中的每一个 bit,都是输入中尽可能多 bit 的信息一起作用的结果。Damgard 和 Merkle定义了所谓“压缩函数(compression function)”,就是将一个固定长度输入,变换成较短的固定长度的输出,这对密码学实践上Hash函数的设计产生了很大的影响。
       Hash函数就是被设计为基于通过特定压缩函数的不断重复“压缩”输入的分组和前一次压缩处理的结果的过程,直到整个消息都被压缩完毕,最后的输出作为整个消息的散列值。尽管还缺乏严格的证明,但绝大多数业界的研究者都同意,如果压缩函数是安全的,那么以上述形式散列任意长度的消息也将是安全的。任意长度的消息被分拆成符合压缩函数输入要求的分组,最后一个分组可能需要在末尾添上特定的填充字节,这些分组将被顺序处理,除了第一个消息分组将与散列初始化值一起作为压缩函数的输入外,当前分组将和前一个分组的压缩函数输出一起被作为这一次压缩的输入,而其输出又将被作为下一个分组压缩函数输入的一部分,直到最后一个压缩函数的输出,将被作为整个消息散列的结果。
       MD5 和 SHA1 可以说是目前应用最广泛的Hash算法,而它们都是以MD4 为基础设计的。
       设计高效算法往往需要使用Hash链表,常数级的查找速度是任何别的算法无法比拟的,Hash链表的构造和冲突的不同实现方法对效率当然有一定的影响,然而Hash函数是Hash链表最核心的部分,Hash函数实现,可以在Hash算法的执行效率、离散性、空间利用率等方面有比较深刻的了解。
       Linux使用OpenSSL库进行哈希函数的调用示例:
依赖库安装:
sudo apt install openssl
sudo apt install libssl-dev
       代码示例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/md5.h>
 
#define HASHLEN 16
typedef unsigned char HASH[HASHLEN];
#define HASHHEXLEN 32
typedef char HASHHEX[HASHHEXLEN+1];
static const char hex_chars[] = "0123456789abcdef";
 
char int2hex(char c) 
{
	return hex_chars[(c & 0x0F)];
}
 
void CvtHex(HASH Bin, HASHHEX Hex) 
{
	unsigned short i;
 
	for (i = 0; i < HASHLEN; i++) {
		Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf);
		Hex[i*2+1] = int2hex(Bin[i] & 0xf);
	}
	Hex[HASHHEXLEN] = '\0';
}
 
/*
* 密码比较,相等返回0,不等返回-1
*/
static int password_compare(char *password, const char *pw)
{
	MD5_CTX Md5Ctx;
	char md5_pw[256];
	HASH HA1;
 
	MD5_Init(&Md5Ctx);	//初始化
	MD5_Update(&Md5Ctx, (unsigned char *)password, strlen(password)); //md5加密
	MD5_Final(HA1, &Md5Ctx); //将加密后的密文放到HA1
	CvtHex(HA1, md5_pw); //将HA1转换为字符串存储
 
	printf("md5pw = %s\n", md5_pw);
	printf("pw = %s\n", pw);
 
	if (0 == strcmp(pw, md5_pw)){
		return 0;
	}
	else {
		return -1;
	}
 
	return 0;
}
 
int main(int argc, char **argv)
{
	int result;
	char pw[256] = "5f4dcc3b5aa765d61d8327deb882cf99";
 
	if ((result = password_compare("password", pw)) < 0) {
		printf("password not equal \n");
		return -1;
	}
	else printf("password equal\n");
 
	return 0;
}
        编译命令:
gcc -o a a.c -lssl -lcrypto

猜你喜欢

转载自blog.csdn.net/essity/article/details/52910204