【OpenSSL】单向散列函数

2023-09-21 21:33:13

什么是单向散列函数

  1. 任意长度数据生成固定长度是散列
  2. 快速计算
  3. 消息变化散列变化
  4. 单向不可逆,抗碰撞

应用场景

  1. 文件完整性
  2. 口令加密
  3. 消息认证
  4. 伪随机数
  5. 配合非对称加密做数字签名
  6. 比特币工作量证明

单向hash抗碰撞

弱抗碰撞

给定Xhash值的情况下,找到另外个数,hash值相同。

强抗碰撞

  • 找到散列值相同的两个字符串
  • MD5,SHA-1已经被攻破可以找到相同散列值的不同消息

常用的Hash算法

  • MD5
  • SHA1
  • SHA2(SHA-256 SHA-384 SHA-512)
  • SHA3 Keccak256 选举产生
  • 国密SM3

MD5算法

  • 消息摘要(Message Digest)
  • 产生128比特(16字节) 散列值(RFC1321)
  • 强抗碰撞已经被破, 2004年王小云攻破
  • 已经不安全, 如果应用加salt
  • 历史系统最广泛,效率高

原理

  1. 补结尾处的字节, 取余448,补1后再补0,补足到448位
  2. 剩余64位做存储原始数据的长度
  3. 初始化MD缓冲, A: 01 23 45 67,B:89 1b cd ef …
  4. 4个函数处理消息块

Open SSL实现MD5

#include <iostream>
#include <openssl/md5.h>

using namespace std;

//将二进制转换成16进制的文本字符
string char2Hex(unsigned char *out,  int len)
{
	const unsigned char hex_chars[] = "0123456789ABCDEF";
	string result;
	for (int i = 0; i < len; i++) 
	{
		unsigned char ch = out[i];
		unsigned int hc = (ch >> 4) & 0xf;
		unsigned int lc = ch & 0x0f;
		//采用大端存储的方式拼接
		result += hex_chars[hc];
		result += hex_chars[lc];
	}
	return result;
}

string MD5Hash(unsigned char *data, size_t len)
{
	//初始化MD5环境
	MD5_CTX ctx;
	MD5_Init(&ctx);

	// 计算MD5hash数据
	MD5_Update(&ctx, data, len);

	//读取数据
	unsigned char out[16] = { 0 }; // 只有16个字节
	//把数据读取到out中
	MD5_Final(out, &ctx);
	return char2Hex(out, 16);
}

//采用简化模式实现MD5
string MD5HashSimple(unsigned char* data, size_t len)
{
	//读取数据
	unsigned char out[16] = { 0 }; // 只有16个字节
	//把数据读取到out中
	MD5(data, len, out);
	return char2Hex(out, 16);
}

int main(int argc, char* argv[])
{
	unsigned char data[] = "测试md5数据";
	int len = sizeof(data);
	string result = MD5Hash(data, len);
	cout <<"MD5:"<<result<< endl;
	data[0] = '9';
	result = MD5HashSimple(data, len);
	cout << "MD5:" << result << endl;
}

计算文件的MD5值

string GetFileListHash(string filepath) 
{
	string hash;
	/ 以二进制方式
	ifstream ifs(filepath, ios::binary);
	if (!ifs)
		return hash;

	int block_size = 128;
	// 文件读取buf
	unsigned char buf[1024] = { 0 };
	//hash输出
	unsigned char out[1024] = { 0 };
	while (!ifs.eof())
	{
		ifs.read((char*)buf, block_size);
		int read_size = ifs.gcount();
		if (read_size < 0)
		{ 
			break; 
		}
		MD5(buf, read_size, out);
		hash.insert(hash.end(), out, out + 16);
	}
	ifs.close();
	MD5((unsigned char*)hash.data(), hash.size(), out);
	return char2Hex(out, 16);
}

SHA-1算法

  • 安全散列算法(Secure Hash Algorithm)
  • 消息摘要(Message Digest)
  • 产生160比特(20字节) 散列值H0 H1 H2 H3 H4
  • 强抗碰撞已经攻破, 在2005年王小云攻破。
  • MD5一样都是由MD4导出。

代码演示

代码实现与MD5类似, 分为三步.

  1. 初始化SHA上下文
  2. 对数据进行hash计算
  3. 读取hash计算结果
    代码如下:
string SHA1Hash(unsigned char* data, size_t len)
{
	// 首先也是初始化SHA1的上下文
	SHA_CTX ctx;
	SHA1_Init(&ctx);

	// 编码数据
	SHA1_Update(&ctx, data, len);
	//读取数据
	unsigned char out[20];
	SHA1_Final(out, &ctx);
	//将字节内容转换为字符
	return char2Hex(out, 20);
}

同样,为了方便调用,open ssl对上述过程进行了封装。仅用一个SHA1函数即可实现以上3步。测试代码如下

//简化函数实现SHA1
string SHA1HashSimple(unsigned char* data, size_t len)
{
	unsigned char out[20];
	SHA1(data, len, out);
	return char2Hex(out, 20);
}

测试:

int main(int argc, char* argv[])
{
	// --- 测试MD5
	unsigned char data[] = "测试md5数据";
	int len = sizeof(data);
	// 测试SHA1
	string hash = SHA1Hash(data, len);
	cout << "SHA1:\t" << hash << endl;

	//简介函数测试
	hash = SHA1HashSimple(data, len);
	cout << "SHA1:\t" << hash << endl;
	return 0;
}

运行结果:
在这里插入图片描述
使用sha1测试Merkle Tree算法


//文件可信树
string GetFileMerkleHash(string filepath)
{
	string hash;
	// 存放hash列表, 后面所有结果都存在其中
	vector<string> hashs;
	ifstream ifs(filepath, ios::binary);
	if (!ifs)
	{
		cout << "文件" << filepath << "不存在" << endl;
		return hash;
	}

	unsigned char buf[1024] = { 0 };
	unsigned char out[1024] = { 0 };
	int block_size = 128;
	while (!ifs.eof())
	{
		ifs.read((char*)buf, block_size);
		int read_size = ifs.gcount();
		if (read_size <= 0)
		{
			break;
		}
		SHA1(buf, read_size, out);
		hashs.push_back(string(out, out + 20));
	}
	while (hashs.size() > 1) // ==1表示已经计算到root节点
	{
		// 不是二的倍数补节点(二叉树)
		if (hashs.size() & 1)
		{
			hashs.push_back(hashs.back());
		}
		// 把hash结果的hash结果还吸入到hashs中
		for (int i = 0; i < hashs.size() / 2; i++)
		{
			// 两个节点拼起来, i表示的父节点
			string tmp_hash = hashs[i * 2]; // 左节点
			tmp_hash += hashs[i * 2 + 1]; // 右节点
			SHA1((const unsigned char*)tmp_hash.data(), tmp_hash.size(), out);
			// 写入结果
			hashs[i] = string(out, out + 20);
		}
		// hash列表删除上一次多余的hash值
		hashs.resize(hashs.size() / 2);
	}

	if (hashs.size() == 0) return hash;
	return char2Hex((unsigned char*)hashs[0].data(), 20);
}

SHA-2算法

类别SHA-1SHA-224SHA-256SHA-384SHA-512
消息摘要长度160224256384512
消息长度小于 2 64 2^{64} 264小于 2 64 2^{64} 264小于 2 64 2^{64} 264小于 2 128 2^{128} 2128小于 2 128 2^{128} 2128
分组长度51251251210241024
计算字长度3232326464
计算步骤数8064648080
  • 消息填充摸512与448同余补充消息长度。
  • 初始化链接变量 缓冲区用8个32位寄存器(SHA256
  • 取自前8个素数(2、3、5、7、11、13、17、19)的平方根的小数部分其二进制表示的前32位 8 ∗ 32 = 256 8*32=256 832=256.
  • SHA512是用64位寄存器
  • 以512位(64)分组位单位处理, 进行64步循环, SHA512以1024(128)位为以个分组
  • SHA-384SHA-512也都有6个迭代函数
string SHA256Hash(const unsigned char* data, size_t len) 
{
	// 初始化SHA256的上下文
	SHA256_CTX ctx;
	SHA256_Init(&ctx);
	
	// 开始写入数据
	SHA256_Update(&ctx, data, len);

	// 读取数据
	unsigned char out[32] = {0};
	SHA256_Final(out, &ctx);
	return char2Hex(out, 32);
}
string SHA256HashSimple(const unsigned char* data, size_t len) 
{
	unsigned char out[32];
	SHA256(data, len, out);
	return char2Hex(out, 32);
}

SHA-3算法

海绵结构, 把数据压到海绵里面。

  1. 填充,
  2. 分组4组
  3. 与初始值异或
    在这里插入图片描述
更多推荐

Ampere ARM Server 内核版本更新

本篇记录AmpereARMServer服务器上,升级内核版本或部分驱动的方法。安装编译依赖库sudoapt-getinstallbuild-essentialkernel-packagelibncurses5-devlibncurses-devsudoapt-getinstallgccmakebisonflexlibs

(高阶)Redis 7 第13讲 数据双写一致性 canal篇

面试题问题答案如何保证mysql改动后,立即同步到Rediscanal简介https://github.com/alibaba/canal/wikihttps://github.com/alibaba/canal/wiki基于MySQL数据库增量日志解析,提供增量数据订阅和消费业务数据库镜像数据库实时备份多级索引(卖家

mysq 主从同步错误之 Error_code 1032 handler error HA_ERR_KEY_NOT_FOUND

错误说明:MySQL主从同步的1032错误,一般是指要更改的数据不存在,SQL_THREAD提取的日志无法应用故报错,造成同步失败(Update、Delete、Insert一条已经delete的数据)。1032的错误本身对数据一致性没什么影响,影响最大的是造成了同步失败、同步停止。如果主主(主从)有同步失败,要第一时间

第八天:gec6818arm开发板和Ubuntu中安装并且编译移植mysql驱动连接QT执行程序

一、Ubuntu18.04中安装并且编译移植mysql驱动程序连接qt执行程序1、安装Mysqlsudoapt-getinstallmysql-serverapt-getisntallmysql-clientsudoapt-getinstalllibmysqlclient-d2、查看是否安装成功,即查看MySQL版本m

软件测试(功能、工具、接口、性能、自动化、测开)详解

一、软件测试功能测试测试用例编写是软件测试的基本技能;也有很多人认为测试用例是软件测试的核心;软件测试中最重要的是设计和生成有效的测试用例;测试用例是测试工作的指导,是软件测试的必须遵守的准则。黑盒测试常见测试用例编写方法1、等价类选取少数有代表性的数据,这一类数据等价于这一类的其它值;找出最小的子集,可以发现最多的错

时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测

时序预测|MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测目录时序预测|MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料预测效果基本介绍MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时

分布式系统的 38 个知识点

天天说分布式分布式,那么我们是否知道什么是分布式,分布式会遇到什么问题,有哪些理论支撑,有哪些经典的应对方案,业界是如何设计并保证分布式系统的高可用呢?1.架构设计这一节将从一些经典的开源系统架构设计出发,来看一下,如何设计一个高质量的分布式系统;而一般的设计出发点,无外乎冗余:简单理解为找个备胎,现任挂掉之后,备胎顶

你对lambda表达式的使用方法以及底层原理了解吗?

你对lambda表达式的使用方法以及底层原理了解吗?lambda表达式是什么?Lambda表达式是Java编程语言中引入的一种函数式编程的特性,它可以用更简洁的方式来表示匿名函数。Lambda表达式可以作为一种行内函数,用于替代传统的匿名内部类。Lambda表达式的基本语法如下:(parameters)->expres

Spring学习 (一): IoC容器

前言参考廖雪峰Spring教程一、什么是IoC容器容器的意思可以理解为一个提供供程序正常运行,提供各种依赖的组件的包的环境。IoC,控制反转,实际上就是将原本由代码编写者控制的各个对象(组件)的生命周期托管给底层的容器,应用层不需要一个个定义好什么时候初始化,什么时候析构释放,所有组件不再由应用程序自己创建和配置,而是

蓝牙核心规范(V5.4)10.10-BLE 入门笔记之SMP和安全性

蓝牙篇之蓝牙核心规范(V5.4)深入详解汇总1.概述SMP是安全管理器协议,用于蓝牙低功耗系统的安全管理。SMP协议定义了配对和Key的分发过程的实现,以及用于实现这些方法的协议和工具。SMP的内容主要是配对和Key的分发,然后用Key对链路或数据进行加密。安全管理器协议(SMP)是协议栈的安全管理器组件的一部分。它支

【Android】SVGAPlayer的使用介绍

背景客户需要通过SVG来做直播场景的炫酷动画。故调用得到如下的工具库:GIthub-SVAGPlayer组装通过xml组装到项目中,然后调用提供的api实现自己的需求即可。<?xmlversion="1.0"encoding="utf-8"?><RelativeLayoutxmlns:android="http://s

热文推荐