赞
踩
目录
安全散列算法是一种加密散列函数,生成固定长度的散列值(或摘要),用于确保数据完整性和验证数据的真实性。
SHA-1是SHA系列中的一种,产生160比特(20字节)的散列值。其散列值通常表示为40个十六进制字符。SHA-1的散列值格式为`H0 H1 H2 H3 H4`,每个部分代表散列的不同部分。
尽管SHA-1曾被广泛使用,但它已经被证明存在弱点,尤其是对碰撞攻击的脆弱性。在2005年,研究人员王小云成功攻破了SHA-1,展示了其产生相同散列值的可能性,这就是所谓的“碰撞”。消息摘要是通过散列函数生成的数据摘要,它是输入数据的唯一表示。消息摘要用于确保数据在传输或存储过程中未被篡改。
MD4是早期的散列算法,而MD5是由MD4算法衍生而来。MD5生成128比特的散列值,广泛用于数据完整性校验,但同样也被发现存在安全漏洞。SHA系列,特别是SHA-1,确实是受到MD4的影响。尽管SHA-1和MD5具有不同的结构和输出长度,但它们在设计理念上有相似之处。
Merkle树是一种哈希树,叶子节点代表数据块(如交易)的哈希值,而非叶子节点的哈希值则是其子节点哈希值的组合。
Merkle树的底部是交易的哈希(即叶子节点),而每对叶子节点的哈希值会组合成其父节点的哈希,直到最终形成根哈希(Merkle根)。这个根哈希代表了整个树的所有交易摘要。
在比特币中,交易哈希使用双SHA-256哈希算法进行计算。即每一笔交易在生成哈希时,首先计算SHA-256哈希,然后再对该哈希值计算一次SHA-256,从而增强安全性。
- #include <iostream> // 引入输入输出流库,以便使用 cout 和 cerr
- #include <openssl/md5.h> // 引入 OpenSSL 的 MD5 相关函数
- #include <openssl/sha.h> // 引入 OpenSSL 的 SHA 相关函数
- #include <fstream> // 引入文件流库,以便读取文件
- #include <iomanip> // 引入用于格式化输入输出的库,例如设置输出宽度
- #include <thread> // 引入线程库,以便使用睡眠功能
- #include <vector> // 引入 STL 的 vector 容器
-
- using namespace std; // 使用标准命名空间,方便后续使用标准库中的元素
-
- // 计算文件的MD5哈希值
- string GetFileListHash(const string& filepath)
- {
- // 以二进制方式打开文件
- ifstream ifs(filepath, ios::binary); // 创建文件流对象以二进制模式打开指定路径的文件
- if (!ifs) { // 检查文件是否成功打开
- cerr << "Failed to open file: " << filepath << endl; // 输出错误信息到标准错误流
- return ""; // 返回空字符串表示失败
- }
-
- int block_size = 128; // 定义一次读取的块大小为 128 字节
- unsigned char buf[block_size] = { 0 }; // 创建一个缓冲区用于存放读取的数据,初始化为 0
- unsigned char out[MD5_DIGEST_LENGTH] = { 0 }; // 创建输出数组,用于存放 MD5 哈希结果
-
- MD5_CTX c; // 定义 MD5 上下文结构体
- MD5_Init(&c); // 初始化 MD5 上下文
-
- while (!ifs.eof()) { // 当文件没有到达末尾时循环
- ifs.read(reinterpret_cast<char*>(buf), block_size); // 从文件中读取数据到 buf 缓冲区
- int read_size = ifs.gcount(); // 获取实际读取的字节数
- if (read_size > 0) { // 如果读取的字节数大于 0
- MD5_Update(&c, buf, read_size); // 将读取的数据更新到 MD5 上下文中
- }
- }
-
- MD5_Final(out, &c); // 完成 MD5 计算并将结果存入 out 数组
- ifs.close(); // 关闭文件流
-
- return string(reinterpret_cast<char*>(out), MD5_DIGEST_LENGTH); // 将 MD5 结果转换为字符串并返回
- }
-
- // 文件可信树 Hash
- std::string GetFileMerkleHash(std::string filepath) {
- std::string hash; // 定义哈希字符串
- std::vector<std::string> hashes; // 定义一个字符串向量用于存放中间哈希值
- std::ifstream ifs(filepath, std::ios::binary); // 以二进制方式打开文件
-
- if (!ifs) return hash; // 如果文件未打开,返回空哈希值
-
- unsigned char buf[1024] = { 0 }; // 创建缓冲区用于读取文件数据
- unsigned char out[SHA_DIGEST_LENGTH]; // 创建输出数组,用于存放 SHA1 哈希结果
-
- int block_size = 128; // 定义一次读取的块大小为 128 字节
- while (!ifs.eof()) { // 当文件没有到达末尾时循环
- ifs.read(reinterpret_cast<char*>(buf), block_size); // 从文件中读取数据到 buf
- int read_size = ifs.gcount(); // 获取实际读取的字节数
- if (read_size <= 0) break; // 如果没有读取到数据,跳出循环
-
- SHA1(buf, read_size, out); // 计算 SHA1 哈希并存储到 out 数组中
- hashes.push_back(std::string(reinterpret_cast<char*>(out), SHA_DIGEST_LENGTH)); // 将哈希值转换为字符串并添加到 hashes 向量中
- }
-
- // 计算 Merkle 哈希
- while (hashes.size() > 1) { // 当 hashes 向量中有多个哈希值时
- if (hashes.size() & 1) { // 如果 hashes 的大小是奇数
- hashes.push_back(hashes.back()); // 将最后一个哈希值复制一份到末尾,保证成对
- }
-
- for (size_t i = 0; i < hashes.size() / 2; i++) { // 遍历哈希值的成对组合
- std::string tmp_hash = hashes[i * 2]; // 获取第一个哈希值
- tmp_hash += hashes[i * 2 + 1]; // 拼接第二个哈希值
- SHA1(reinterpret_cast<const unsigned char*>(tmp_hash.data()), tmp_hash.size(), out); // 计算拼接后字符串的 SHA1 哈希
- hashes[i] = std::string(reinterpret_cast<char*>(out), SHA_DIGEST_LENGTH); // 更新哈希值到 hashes 向量
- }
- hashes.resize(hashes.size() / 2); // 将 hashes 向量的大小减半
- }
-
- if (hashes.size() == 0) return hash; // 如果 hashes 向量为空,返回空哈希值
- return hashes[0]; // 返回 Merkle 树的根哈希值
- }
-
- // 以十六进制格式输出数据
- void PrintHex(const string& data) {
- for (unsigned char c : data) { // 遍历数据中的每一个字节
- cout << hex << setw(2) << setfill('0') << (int)c; // 将每个字节以十六进制格式输出
- }
- cout << endl; // 输出换行符
- }
-
- int main() {
- cout << "Test Hash" << endl; // 输出测试信息
-
- unsigned char data[] = "测试MD5数据"; // 定义要进行MD5哈希的数据
- unsigned char out[MD5_DIGEST_LENGTH] = { 0 }; // 创建输出数组,用于存放 MD5 哈希结果
- int len = sizeof(data) - 1; // 计算数据长度,减去 1 是因为 sizeof 包含字符串末尾的 '\0'
-
- // 对数据进行 MD5 哈希计算
- MD5(data, len, out); // 计算 MD5 哈希
- // 输出哈希结果
- for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { // 遍历哈希结果
- cout << hex << setw(2) << setfill('0') << (int)out[i]; // 输出哈希值的每一个字节
- }
- cout << endl; // 输出换行符
-
- data[1] = 9; // 修改数据
- MD5(data, len, out); // 再次计算 MD5 哈希
- for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { // 遍历哈希结果
- cout << hex << setw(2) << setfill('0') << (int)out[i]; // 输出新的哈希值
- }
- cout << endl; // 输出换行符
-
- string filepath = "/home/book/Desktop/test.txt"; // 指定要读取的文件路径
- auto hash1 = GetFileListHash(filepath); // 计算文件的 MD5 哈希值
- PrintHex(hash1); // 输出文件的哈希值
-
- for (;;) { // 无限循环
- auto hash = GetFileListHash(filepath); // 重新计算文件哈希值
- auto thash = GetFileMerkleHash(filepath); // 计算文件的 Merkle 哈希值
-
- if (hash != hash1) { // 如果文件哈希值与之前的哈希不相等
- cout << "文件被修改" << endl; // 输出文件被修改的警告
- cout << "HashList:\t"; // 输出哈希列表的提示
- PrintHex(hash); // 输出新的哈希值
- hash1 = hash; // 更新之前的哈希值
-
- cout << "MerkleTree:\t"; // 输出 Merkle 树的提示
- PrintHex(thash); // 输出 Merkle 哈希值
- }
- this_thread::sleep_for(chrono::seconds(1)); // 每秒检查一次
- }
-
- return 0; // 程序正常结束
- }
makefile
- first_openss:test_openssl.cpp
- g++ $^ -o $@ -I/usr/local/include -L/usr/local/lib -lcrypto -std=c++11
1. MD5 哈希计算 (GetFileListHash
函数)
MD5_Final
完成哈希计算,并返回哈希值。2. Merkle 哈希计算 (GetFileMerkleHash
函数)
std::vector
中。(1). MD5 哈希计算 (GetFileListHash
函数)
(2). Merkle 哈希计算 (GetFileMerkleHash
函数)
(1). MD5 哈希计算
MD5_Final
完成哈希计算,返回一个固定大小的 128 位(16 字节)MD5 哈希值。(2). Merkle 哈希计算
(1). MD5 哈希计算:
(2). Merkle 哈希计算
(1). MD5 哈希计算 更加简单,主要用于快速验证文件是否已被修改,适合单一文件检查。
(2). Merkle 哈希计算 则更复杂,适用于需要验证数据完整性和一致性的场景,尤其是当涉及多个数据块或分布式系统时。它通过构建树形结构提高了数据验证的效率和灵活性。
SHA-256、SHA-384 和 SHA-512 是三种不同的安全哈希算法,它们都是 SHA-2(安全哈希算法2)系列的一部分。它们在比特币挖矿的工作量证明(Proof of Work)机制中扮演着重要角色。
1. SHA-256:
2. SHA-384:
3. SHA-512:
1. 工作量证明机制:
2. SHA-256 在比特币挖矿中的应用:
在 SHA 系列哈希算法中,消息填充是将输入消息调整到特定长度的过程,以便满足算法处理的要求。
消息填充模 512 与 448 同余:
(1)SHA-256 和 SHA-512 都采用类似的填充机制,以确保输入消息的长度在处理之前符合特定要求。
(2)消息的长度在被填充后,需要满足以下条件:
填充后的消息长度要对 512 取模,且填充后的长度要为 448 位,即填充后的消息长度为 声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。