C++实现常用哈希算法
一、概述
哈希算法的概念最早可以追溯到20世纪50年代,用于高效数据存储和检索。1953年,汉斯·彼得·卢恩(Hans Peter Luhn)提出了一种基于关键字的散列方法,用于快速访问信息。随后,哈希表(Hash Table)的设计逐渐成熟,成为计算机科学中基础数据结构之一。加密哈希函数的发展与密码学需求紧密相关,例如MD5(1991年)和SHA家族(1993年至今)的诞生,均是为了解决数据完整性验证和安全性问题。
之所以用来校验数据、防篡改,主要因为它的以下几个特性:
- 确定性,相同的输入永远不会产生不同的输出。假设有一个文件A,它的SHA256值(当然也可以是其他算法)是X,如果A没有被修改,那么它的SHA256就一定是X,永远不会改变。
- 唯一性(几乎),两组不同的数据基本不可能产生相同的哈希值。但这并不代表哈希值没有重复的可能性,因为数据的组合数量肯定要比哈希字符串多得多。(例子放在文章最后)
- 不可逆性,将哈希值还原成原始数据几乎是不可能的。
- 长度确定性。这使得它可以在密码学中起到很大作用,比如AES-256的密钥必须是32字节,但如果想要使用不同长度的密码(或密钥),就可以先计算输入密钥的SHA-256,再将二进制SHA-256数据作为二次密钥应用于AES-256加密,而且足够安全。
但哈希算法相对复杂,C++中通常需要依赖openssl这样的库才能实现。那么今天,作者就实现了常用的哈希算法,可以直接include在代码里使用。
废话不多说,上代码!
二、代码实现
1. 算法实现
本文的所有代码需要在C++14标准下编译(MinGW 4.9+或VS2013+ / Dev-C++ 5.11+),不然可能报错!
哈希算法原理十分复杂,这里不再多说。下面的代码可以直接放在一个.hpp里,include使用,方法和Python的hashlib相同。(作者原创代码,转载请声明出处!)
/*
MD5及SHA家族哈希算法实现
(c) Copyright 2025
By 金煜力,2025-7-19 ~ 2025-8-31
*/
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include <array>
#include <stdexcept>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <cstring>
// ================= MD5 =================
constexpr uint32_t MD5_S[64] = {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};
constexpr uint32_t MD5_K[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
};
inline uint32_t md5_left_rotate(uint32_t x, uint32_t n) {
return (x << n) | (x >> (32 - n));
}
inline uint32_t md5_to_little_endian(const uint8_t* bytes) {
return static_cast<uint32_t>(bytes[0])
| (static_cast<uint32_t>(bytes[1]) << 8)
| (static_cast<uint32_t>(bytes[2]) << 16)
| (static_cast<uint32_t>(bytes[3]) << 24);
}
class MD5 {
public:
MD5() : A(0x67452301), B(0xefcdab89), C(0x98badcfe), D(0x10325476), total_bytes(0), finished(false) {
buffer.reserve(64);
digest_result.fill(0);
}
void update(const void* data, size_t length) {
if (finished) return;
const uint8_t* ptr = static_cast<const uint8_t*>(data);
total_bytes += length;
if (!buffer.empty()) {
size_t to_copy = std::min<size_t>(64 - buffer.size(), length);
buffer.insert(buffer.end(), ptr, ptr + to_copy);
ptr += to_copy;
length -= to_copy;
if (buffer.size() == 64) {
transform(buffer.data());
buffer.clear();
}
}
while (length >= 64) {
transform(ptr);
ptr += 64;
length -= 64;
}
if (length > 0) {
buffer.insert(buffer.end(), ptr, ptr + length);
}
}
void update(const std::string& data) { update(data.data(), data.size()); }
void update(const std::vector<uint8_t>& data) { update(data.data(), data.size()); }
void finalize() {
if (!finished) do_finalize();
}
std::string digest() {
if (!finished) do_finalize();
return std::string(digest_result.begin(), digest_result.end());
}
std::string hexdigest() {
if (!finished) do_finalize();
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t c : digest_result) {
oss << std::setw(2) << static_cast<unsigned>(c);
}
return oss.str();
}
private:
uint32_t A, B, C, D;
uint64_t total_bytes;
std::vector<uint8_t> buffer;
bool finished;
std::array<uint8_t, 16> digest_result;
void transform(const uint8_t* block) {
uint32_t a = A, b = B, c = C, d = D;
uint32_t X[16];
for (int i = 0; i < 16; ++i) {
X[i] = md5_to_little_endian(block + i * 4);
}
for (int i = 0; i < 64; ++i) {
uint32_t F, g;
if (i < 16) {
F = (b & c) | ((~b) & d);
g = i;
} else if (i < 32) {
F = (d & b) | ((~d) & c);
g = (5 * i + 1) % 16;
} else if (i < 48) {
F = b ^ c ^ d;
g = (3 * i + 5) % 16;
} else {
F = c ^ (b | (~d));
g = (7 * i) % 16;
}
F = F + a + MD5_K[i] + X[g];
a = d;
d = c;
c = b;
b = b + md5_left_rotate(F, MD5_S[i]);
}
A += a;
B += b;
C += c;
D += d;
}
void do_finalize() {
if (finished) return;
uint64_t bit_length = total_bytes * 8;
buffer.push_back(0x80);
size_t orig_size = buffer.size();
size_t pad_size = (orig_size % 64 < 56) ? (56 - orig_size % 64) : (120 - orig_size % 64);
buffer.insert(buffer.end(), pad_size, 0);
for (int i = 0; i < 8; i++) {
buffer.push_back(static_cast<uint8_t>(bit_length >> (i * 8)));
}
for (size_t i = 0; i < buffer.size(); i += 64) {
transform(buffer.data() + i);
}
auto to_le_bytes = [](uint32_t n) -> std::array<uint8_t, 4> {
return {
static_cast<uint8_t>(n),
static_cast<uint8_t>(n >> 8),
static_cast<uint8_t>(n >> 16),
static_cast<uint8_t>(n >> 24)
};
};
std::array<uint8_t, 4> a_bytes = to_le_bytes(A);
std::array<uint8_t, 4> b_bytes = to_le_bytes(B);
std::array<uint8_t, 4> c_bytes = to_le_bytes(C);
std::array<uint8_t, 4> d_bytes = to_le_bytes(D);
std::copy(a_bytes.begin(), a_bytes.end(), digest_result.begin());
std::copy(b_bytes.begin(), b_bytes.end(), digest_result.begin() + 4);
std::copy(c_bytes.begin(), c_bytes.end(), digest_result.begin() + 8);
std::copy(d_bytes.begin(), d_bytes.end(), digest_result.begin() + 12);
finished = true;
}
};
// ================= SHA1 =================
class SHA1 {
public:
SHA1() : h0(0x67452301), h1(0xEFCDAB89), h2(0x98BADCFE), h3(0x10325476), h4(0xC3D2E1F0), total_bytes(0), finished(false) {}
void update(const void* data, size_t length) {
if (finished) return;
const uint8_t* ptr = static_cast<const uint8_t*>(data);
total_bytes += length;
if (!buffer.empty()) {
size_t to_copy = std::min<size_t>(64 - buffer.size(), length);
buffer.insert(buffer.end(), ptr, ptr + to_copy);
ptr += to_copy;
length -= to_copy;
if (buffer.size() == 64) {
transform(buffer.data());
buffer.clear();
}
}
while (length >= 64) {
transform(ptr);
ptr += 64;
length -= 64;
}
if (length > 0) {
buffer.insert(buffer.end(), ptr, ptr + length);
}
}
void update(const std::string& data) { update(data.data(), data.size()); }
void update(const std::vector<uint8_t>& data) { update(data.data(), data.size()); }
void finalize() {
if (!finished) do_finalize();
}
std::string digest() {
if (!finished) do_finalize();
return std::string(digest_result.begin(), digest_result.end());
}
std::string hexdigest() {
if (!finished) do_finalize();
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t c : digest_result) {
oss << std::setw(2) << static_cast<unsigned>(c);
}
return oss.str();
}
private:
uint32_t h0, h1, h2, h3, h4;
uint64_t total_bytes;
std::vector<uint8_t> buffer;
bool finished;
std::array<uint8_t, 20> digest_result;
void transform(const uint8_t* block) {
uint32_t w[80];
for (int i = 0; i < 16; ++i) {
w[i] = (static_cast<uint32_t>(block[i * 4]) << 24) |
(static_cast<uint32_t>(block[i * 4 + 1]) << 16) |
(static_cast<uint32_t>(block[i * 4 + 2]) << 8) |
static_cast<uint32_t>(block[i * 4 + 3]);
}
for (int i = 16; i < 80; ++i) {
w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
}
uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
for (int i = 0; i < 20; ++i) {
uint32_t f = (b & c) | ((~b) & d);
uint32_t temp = left_rotate(a, 5) + f + e + 0x5A827999 + w[i];
e = d; d = c; c = left_rotate(b, 30); b = a; a = temp;
}
for (int i = 20; i < 40; ++i) {
uint32_t f = b ^ c ^ d;
uint32_t temp = left_rotate(a, 5) + f + e + 0x6ED9EBA1 + w[i];
e = d; d = c; c = left_rotate(b, 30); b = a; a = temp;
}
for (int i = 40; i < 60; ++i) {
uint32_t f = (b & c) | (b & d) | (c & d);
uint32_t temp = left_rotate(a, 5) + f + e + 0x8F1BBCDC + w[i];
e = d; d = c; c = left_rotate(b, 30); b = a; a = temp;
}
for (int i = 60; i < 80; ++i) {
uint32_t f = b ^ c ^ d;
uint32_t temp = left_rotate(a, 5) + f + e + 0xCA62C1D6 + w[i];
e = d; d = c; c = left_rotate(b, 30); b = a; a = temp;
}
h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
}
void do_finalize() {
if (finished) return;
size_t n = buffer.size();
size_t k = (55 - n + 64) % 64;
std::vector<uint8_t> padding;
padding.reserve(n + 1 + k + 8);
padding = buffer;
padding.push_back(0x80);
padding.insert(padding.end(), k, 0);
uint64_t bit_length = total_bytes * 8;
for (int i = 0; i < 8; i++) {
padding.push_back(static_cast<uint8_t>((bit_length >> (56 - i * 8)) & 0xFF));
}
for (size_t i = 0; i < padding.size(); i += 64) {
transform(padding.data() + i);
}
auto to_be_bytes = [](uint32_t n, uint8_t* bytes) {
bytes[0] = static_cast<uint8_t>((n >> 24) & 0xFF);
bytes[1] = static_cast<uint8_t>((n >> 16) & 0xFF);
bytes[2] = static_cast<uint8_t>((n >> 8) & 0xFF);
bytes[3] = static_cast<uint8_t>(n & 0xFF);
};
to_be_bytes(h0, digest_result.data());
to_be_bytes(h1, digest_result.data() + 4);
to_be_bytes(h2, digest_result.data() + 8);
to_be_bytes(h3, digest_result.data() + 12);
to_be_bytes(h4, digest_result.data() + 16);
finished = true;
}
static uint32_t left_rotate(uint32_t x, uint32_t n) {
return (x << n) | (x >> (32 - n));
}
};
// ================= SHA256 =================
class SHA256 {
private:
uint64_t total_bytes;
std::vector<uint8_t> buffer;
uint32_t h[8];
bool finalized;
static uint32_t rotr(uint32_t x, uint32_t n) {
return (x >> n) | (x << (32 - n));
}
static uint32_t ch(uint32_t x, uint32_t y, uint32_t z) {
return (x & y) ^ (~x & z);
}
static uint32_t maj(uint32_t x, uint32_t y, uint32_t z) {
return (x & y) ^ (x & z) ^ (y & z);
}
static uint32_t sigma0(uint32_t x) {
return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
}
static uint32_t sigma1(uint32_t x) {
return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
}
static uint32_t gamma0(uint32_t x) {
return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
}
static uint32_t gamma1(uint32_t x) {
return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
}
void process_block(const uint8_t* block) {
static const std::array<uint32_t, 64> k = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
std::array<uint32_t, 64> w;
for (int i = 0; i < 16; ++i) {
w[i] = (static_cast<uint32_t>(block[i * 4]) << 24) |
(static_cast<uint32_t>(block[i * 4 + 1]) << 16) |
(static_cast<uint32_t>(block[i * 4 + 2]) << 8) |
(static_cast<uint32_t>(block[i * 4 + 3]));
}
for (int i = 16; i < 64; ++i) {
w[i] = gamma1(w[i - 2]) + w[i - 7] + gamma0(w[i - 15]) + w[i - 16];
}
uint32_t a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], h_val = h[7];
for (int i = 0; i < 64; ++i) {
uint32_t t1 = h_val + sigma1(e) + ch(e, f, g) + k[i] + w[i];
uint32_t t2 = sigma0(a) + maj(a, b, c);
h_val = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;
}
h[0] += a; h[1] += b; h[2] += c; h[3] += d; h[4] += e; h[5] += f; h[6] += g; h[7] += h_val;
}
public:
SHA256() { reset(); }
void reset() {
total_bytes = 0;
buffer.clear();
buffer.reserve(64);
h[0] = 0x6a09e667;
h[1] = 0xbb67ae85;
h[2] = 0x3c6ef372;
h[3] = 0xa54ff53a;
h[4] = 0x510e527f;
h[5] = 0x9b05688c;
h[6] = 0x1f83d9ab;
h[7] = 0x5be0cd19;
finalized = false;
}
void update(const void* data, size_t len) {
if (finalized) {
throw std::runtime_error("SHA256: cannot update after finalization");
}
const uint8_t* d = static_cast<const uint8_t*>(data);
size_t index = 0;
size_t remaining = len;
if (!buffer.empty()) {
size_t buffer_remaining = 64 - buffer.size();
if (remaining < buffer_remaining) {
buffer.insert(buffer.end(), d, d + remaining);
return;
}
buffer.insert(buffer.end(), d, d + buffer_remaining);
process_block(buffer.data());
total_bytes += 64;
index += buffer_remaining;
remaining -= buffer_remaining;
buffer.clear();
}
while (remaining >= 64) {
process_block(d + index);
total_bytes += 64;
index += 64;
remaining -= 64;
}
if (remaining > 0) {
buffer.insert(buffer.end(), d + index, d + index + remaining);
}
}
void update(const std::string& data) { update(data.data(), data.size()); }
void update(const std::vector<uint8_t>& data) { update(data.data(), data.size()); }
void finalize() {
if (!finalized) do_finalize();
}
std::vector<uint8_t> digest() {
if (!finalized) do_finalize();
std::vector<uint8_t> result(32);
for (int i = 0; i < 8; ++i) {
result[i * 4] = static_cast<uint8_t>(h[i] >> 24);
result[i * 4 + 1] = static_cast<uint8_t>(h[i] >> 16);
result[i * 4 + 2] = static_cast<uint8_t>(h[i] >> 8);
result[i * 4 + 3] = static_cast<uint8_t>(h[i]);
}
return result;
}
std::string hexdigest() {
std::vector<uint8_t> bin_digest = digest();
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t b : bin_digest) {
oss << std::setw(2) << static_cast<unsigned>(b);
}
return oss.str();
}
private:
void do_finalize() {
if (finalized) return;
uint64_t total_bits = (total_bytes + buffer.size()) * 8;
buffer.push_back(0x80);
size_t orig_size = buffer.size();
size_t padding_len = (orig_size % 64 < 56) ? (56 - orig_size % 64) : (120 - orig_size % 64);
buffer.resize(orig_size + padding_len, 0);
for (int i = 0; i < 8; ++i) {
buffer.push_back(static_cast<uint8_t>((total_bits >> (56 - i * 8)) & 0xFF));
}
for (size_t i = 0; i < buffer.size(); i += 64) {
process_block(&buffer[i]);
}
finalized = true;
buffer.clear();
}
};
// ================= SHA384 =================
class SHA384 {
private:
uint64_t h[8];
uint64_t length;
uint8_t buffer[128];
size_t buffer_size;
bool finalized;
// 64位循环右移
inline uint64_t rotr64(uint64_t x, uint32_t n) {
return (x >> n) | (x << (64 - n));
}
// SHA-384逻辑函数
inline uint64_t ch(uint64_t x, uint64_t y, uint64_t z) {
return (x & y) ^ (~x & z);
}
inline uint64_t maj(uint64_t x, uint64_t y, uint64_t z) {
return (x & y) ^ (x & z) ^ (y & z);
}
inline uint64_t sigma0(uint64_t x) {
return rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39);
}
inline uint64_t sigma1(uint64_t x) {
return rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41);
}
inline uint64_t gamma0(uint64_t x) {
return rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7);
}
inline uint64_t gamma1(uint64_t x) {
return rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6);
}
// 处理一个128字节块
void process_block(const uint8_t* block) {
uint64_t w[80] = {0};
// 加载前16个字(大端序)
for (int i = 0; i < 16; ++i) {
w[i] = (static_cast<uint64_t>(block[i*8]) << 56) |
(static_cast<uint64_t>(block[i*8+1]) << 48) |
(static_cast<uint64_t>(block[i*8+2]) << 40) |
(static_cast<uint64_t>(block[i*8+3]) << 32) |
(static_cast<uint64_t>(block[i*8+4]) << 24) |
(static_cast<uint64_t>(block[i*8+5]) << 16) |
(static_cast<uint64_t>(block[i*8+6]) << 8) |
(static_cast<uint64_t>(block[i*8+7]));
}
// 扩展消息调度数组(16-79)
for (int i = 16; i < 80; ++i) {
w[i] = gamma1(w[i-2]) + w[i-7] + gamma0(w[i-15]) + w[i-16];
}
// 初始化工作变量
uint64_t a = h[0];
uint64_t b = h[1];
uint64_t c = h[2];
uint64_t d = h[3];
uint64_t e = h[4];
uint64_t f = h[5];
uint64_t g = h[6];
uint64_t h_val = h[7];
// 主循环(80轮)
for (int i = 0; i < 80; ++i) {
uint64_t t1 = h_val + sigma1(e) + ch(e, f, g) + k[i] + w[i];
uint64_t t2 = sigma0(a) + maj(a, b, c);
h_val = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
// 更新哈希值
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
h[4] += e;
h[5] += f;
h[6] += g;
h[7] += h_val;
}
// SHA-384常量
static const uint64_t k[80];
public:
SHA384() {
reset();
}
// 重置哈希计算状态
void reset() {
// SHA-384初始哈希值
h[0] = 0xcbbb9d5dc1059ed8;
h[1] = 0x629a292a367cd507;
h[2] = 0x9159015a3070dd17;
h[3] = 0x152fecd8f70e5939;
h[4] = 0x67332667ffc00b31;
h[5] = 0x8eb44a8768581511;
h[6] = 0xdb0c2e0d64f98fa7;
h[7] = 0x47b5481dbefa4fa4;
length = 0;
buffer_size = 0;
finalized = false;
}
// 更新哈希计算(原始内存数据)
void update(const void* data, size_t size) {
if (finalized) return;
const uint8_t* input = static_cast<const uint8_t*>(data);
size_t offset = 0;
// 处理缓冲区中已有的数据
if (buffer_size > 0) {
size_t copy_len = std::min(128 - buffer_size, size);
memcpy(buffer + buffer_size, input, copy_len);
buffer_size += copy_len;
offset += copy_len;
if (buffer_size == 128) {
process_block(buffer);
length += 128;
buffer_size = 0;
}
}
// 处理完整的块
while (offset + 128 <= size) {
process_block(input + offset);
length += 128;
offset += 128;
}
// 将剩余数据存入缓冲区
if (offset < size) {
size_t remaining = size - offset;
memcpy(buffer, input + offset, remaining);
buffer_size = remaining;
}
}
// 更新哈希计算(字节向量)
void update(const std::vector<uint8_t>& data) {
update(data.data(), data.size());
}
// 更新哈希计算(字符串)
void update(const std::string& data) {
update(data.data(), data.size());
}
void finalize() {
if (!finalized) do_finalize();
}
// 完成哈希计算并返回十六进制摘要
std::string hexdigest() {
if (!finalized) do_finalize();
// 生成十六进制摘要(取前384位,即前6个64位字)
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (int i = 0; i < 6; ++i) {
for (int j = 7; j >= 0; --j) {
uint8_t byte = static_cast<uint8_t>((h[i] >> (j * 8)) & 0xFF);
ss << std::setw(2) << static_cast<unsigned>(byte);
}
}
// 重置状态以便后续使用
reset();
return ss.str();
}
// 完成哈希计算并返回二进制摘要
std::vector<uint8_t> digest() {
if (!finalized) do_finalize();
// 获取十六进制摘要
std::string hex = hexdigest();
// 转换为二进制数据
std::vector<uint8_t> bin;
bin.reserve(48); // SHA-384产生48字节摘要
for (size_t i = 0; i < hex.length(); i += 2) {
unsigned int byte;
std::stringstream ss;
ss << std::hex << hex.substr(i, 2);
ss >> byte;
bin.push_back(static_cast<uint8_t>(byte));
}
return bin;
}
private:
void do_finalize() {
if (finalized) return;
// 保存原始长度
uint64_t original_length = length + buffer_size;
uint64_t original_bits = original_length * 8;
// 添加填充
size_t padding_len = (buffer_size < 112) ? (112 - buffer_size) : (240 - buffer_size);
std::vector<uint8_t> padding(padding_len + 16);
padding[0] = 0x80;
// 添加长度信息(大端序)
for (int i = 0; i < 8; ++i) {
padding[padding_len + 8 + i] = static_cast<uint8_t>(original_bits >> (56 - i * 8));
}
// 处理填充数据
update(padding.data(), padding.size());
finalized = true;
}
};
// 定义SHA-384常量
const uint64_t SHA384::k[80] = {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
// ================= SHA512 =================
class SHA512 {
private:
uint64_t total_bytes[2];
std::vector<uint8_t> buffer;
uint64_t h[8];
bool finalized;
static const uint64_t K[80];
static inline uint64_t ROTR(uint64_t x, uint64_t n) {
return (x >> n) | (x << (64 - n));
}
static inline uint64_t Ch(uint64_t x, uint64_t y, uint64_t z) {
return (x & y) ^ (~x & z);
}
static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z) {
return (x & y) ^ (x & z) ^ (y & z);
}
static inline uint64_t Sigma0(uint64_t x) {
return ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39);
}
static inline uint64_t Sigma1(uint64_t x) {
return ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41);
}
static inline uint64_t sigma0(uint64_t x) {
return ROTR(x, 1) ^ ROTR(x, 8) ^ (x >> 7);
}
static inline uint64_t sigma1(uint64_t x) {
return ROTR(x, 19) ^ ROTR(x, 61) ^ (x >> 6);
}
void process_block(const uint8_t* block) {
uint64_t w[80] = { 0 };
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 8; j++) {
w[i] = (w[i] << 8) | static_cast<uint64_t>(block[i * 8 + j]);
}
}
for (int i = 16; i < 80; i++) {
w[i] = sigma1(w[i - 2]) + w[i - 7] + sigma0(w[i - 15]) + w[i - 16];
}
uint64_t a = h[0], b = h[1], c = h[2], d = h[3];
uint64_t e = h[4], f = h[5], g = h[6], h_val = h[7];
for (int i = 0; i < 80; i++) {
uint64_t T1 = h_val + Sigma1(e) + Ch(e, f, g) + K[i] + w[i];
uint64_t T2 = Sigma0(a) + Maj(a, b, c);
h_val = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
}
h[0] += a; h[1] += b; h[2] += c; h[3] += d; h[4] += e; h[5] += f; h[6] += g; h[7] += h_val;
}
public:
SHA512() { reset(); }
void reset() {
h[0] = 0x6a09e667f3bcc908;
h[1] = 0xbb67ae8584caa73b;
h[2] = 0x3c6ef372fe94f82b;
h[3] = 0xa54ff53a5f1d36f1;
h[4] = 0x510e527fade682d1;
h[5] = 0x9b05688c2b3e6c1f;
h[6] = 0x1f83d9abfb41bd6b;
h[7] = 0x5be0cd19137e2179;
total_bytes[0] = 0;
total_bytes[1] = 0;
buffer.clear();
finalized = false;
}
void update(const void* data, size_t len) {
if (finalized) {
throw std::runtime_error("SHA512: cannot update after finalization");
}
const uint8_t* d = static_cast<const uint8_t*>(data);
size_t index = 0;
uint64_t prev_total = total_bytes[1];
total_bytes[1] += static_cast<uint64_t>(len);
if (total_bytes[1] < prev_total) {
total_bytes[0]++;
}
if (!buffer.empty()) {
size_t remaining = 128 - buffer.size();
if (len < remaining) {
buffer.insert(buffer.end(), d, d + len);
return;
}
buffer.insert(buffer.end(), d, d + remaining);
process_block(buffer.data());
index = remaining;
len -= remaining;
buffer.clear();
}
while (len - index >= 128) {
process_block(d + index);
index += 128;
}
if (index < len) {
buffer.insert(buffer.end(), d + index, d + len);
}
}
void update(const std::string& data) { update(data.data(), data.size()); }
void update(const std::vector<uint8_t>& data) { update(data.data(), data.size()); }
void finalize() {
if (!finalized) do_finalize();
}
std::vector<uint8_t> digest() {
if (!finalized) do_finalize();
std::vector<uint8_t> result(64);
for (int i = 0; i < 8; i++) {
for (int j = 7; j >= 0; j--) {
result[i * 8 + (7 - j)] = static_cast<uint8_t>((h[i] >> (j * 8)) & 0xFF);
}
}
return result;
}
std::string hexdigest() {
std::vector<uint8_t> bin_digest = digest();
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t b : bin_digest) {
oss << std::setw(2) << static_cast<unsigned>(b);
}
return oss.str();
}
private:
void do_finalize() {
if (finalized) return;
uint64_t total_bits_low = total_bytes[1] << 3;
uint64_t total_bits_high = (total_bytes[0] << 3) | (total_bytes[1] >> 61);
buffer.push_back(0x80);
size_t orig_size = buffer.size();
size_t padding_len = (orig_size % 128 < 112) ? (112 - orig_size % 128) : (240 - orig_size % 128);
buffer.insert(buffer.end(), padding_len, 0);
for (int i = 7; i >= 0; --i) {
buffer.push_back(static_cast<uint8_t>((total_bits_high >> (i * 8)) & 0xFF));
}
for (int i = 7; i >= 0; --i) {
buffer.push_back(static_cast<uint8_t>((total_bits_low >> (i * 8)) & 0xFF));
}
size_t i = 0;
while (i + 128 <= buffer.size()) {
process_block(&buffer[i]);
i += 128;
}
finalized = true;
buffer.clear();
}
};
const uint64_t SHA512::K[80] = {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
2. 使用示例
这里给一个测试代码和使用demo,测试代码:
#include "hash.hpp"
#include <iostream>
#include <string>
#include <cassert>
#include <vector>
using namespace std;
// 测试 MD5 算法
void test_md5() {
cout << "Testing MD5..." << endl;
// 测试空字符串
MD5 md5_empty;
md5_empty.update("");
assert(md5_empty.hexdigest() == "d41d8cd98f00b204e9800998ecf8427e");
cout << "Empty string: PASS" << endl;
// 测试 "abc"
MD5 md5_abc;
md5_abc.update("abc");
assert(md5_abc.hexdigest() == "900150983cd24fb0d6963f7d28e17f72");
cout << "\"abc\": PASS" << endl;
// 测试长字符串
MD5 md5_long;
md5_long.update("The quick brown fox jumps over the lazy dog");
assert(md5_long.hexdigest() == "9e107d9d372bb6826bd81d3542a419d6");
cout << "Long string: PASS" << endl;
// 测试分块更新
MD5 md5_chunk;
md5_chunk.update("The quick brown fox ");
md5_chunk.update("jumps over the lazy dog");
assert(md5_chunk.hexdigest() == "9e107d9d372bb6826bd81d3542a419d6");
cout << "Chunked update: PASS" << endl;
cout << "MD5 tests completed successfully!" << endl << endl;
}
// 测试 SHA1 算法
void test_sha1() {
cout << "Testing SHA1..." << endl;
// 测试空字符串
SHA1 sha1_empty;
sha1_empty.update("");
assert(sha1_empty.hexdigest() == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
cout << "Empty string: PASS" << endl;
// 测试 "abc"
SHA1 sha1_abc;
sha1_abc.update("abc");
assert(sha1_abc.hexdigest() == "a9993e364706816aba3e25717850c26c9cd0d89d");
cout << "\"abc\": PASS" << endl;
// 测试长字符串
SHA1 sha1_long;
sha1_long.update("The quick brown fox jumps over the lazy dog");
assert(sha1_long.hexdigest() == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
cout << "Long string: PASS" << endl;
// 测试分块更新
SHA1 sha1_chunk;
sha1_chunk.update("The quick brown fox ");
sha1_chunk.update("jumps over the lazy dog");
assert(sha1_chunk.hexdigest() == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
cout << "Chunked update: PASS" << endl;
cout << "SHA1 tests completed successfully!" << endl << endl;
}
// 测试 SHA256 算法
void test_sha256() {
cout << "Testing SHA256..." << endl;
// 测试空字符串
SHA256 sha256_empty;
sha256_empty.update("");
assert(sha256_empty.hexdigest() == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
cout << "Empty string: PASS" << endl;
// 测试 "abc"
SHA256 sha256_abc;
sha256_abc.update("abc");
assert(sha256_abc.hexdigest() == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
cout << "\"abc\": PASS" << endl;
// 测试长字符串
SHA256 sha256_long;
sha256_long.update("The quick brown fox jumps over the lazy dog");
assert(sha256_long.hexdigest() == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592");
cout << "Long string: PASS" << endl;
// 测试分块更新
SHA256 sha256_chunk;
sha256_chunk.update("The quick brown fox ");
sha256_chunk.update("jumps over the lazy dog");
assert(sha256_chunk.hexdigest() == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592");
cout << "Chunked update: PASS" << endl;
cout << "SHA256 tests completed successfully!" << endl << endl;
}
// 测试 SHA384 算法
void test_sha384() {
cout << "Testing SHA384..." << endl;
// 测试空字符串
SHA384 sha384_empty;
sha384_empty.update("");
assert(sha384_empty.hexdigest() == "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b");
cout << "Empty string: PASS" << endl;
// 测试 "abc"
SHA384 sha384_abc;
sha384_abc.update("abc");
assert(sha384_abc.hexdigest() == "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7");
cout << "\"abc\": PASS" << endl;
// 测试长字符串
SHA384 sha384_long;
sha384_long.update("The quick brown fox jumps over the lazy dog");
assert(sha384_long.hexdigest() == "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1");
cout << "Long string: PASS" << endl;
// 测试分块更新
SHA384 sha384_chunk;
sha384_chunk.update("The quick brown fox ");
sha384_chunk.update("jumps over the lazy dog");
assert(sha384_chunk.hexdigest() == "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1");
cout << "Chunked update: PASS" << endl;
cout << "SHA384 tests completed successfully!" << endl << endl;
}
// 测试 SHA512 算法
void test_sha512() {
cout << "Testing SHA512..." << endl;
// 测试空字符串
SHA512 sha512_empty;
sha512_empty.update("");
assert(sha512_empty.hexdigest() == "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
cout << "Empty string: PASS" << endl;
// 测试 "abc"
SHA512 sha512_abc;
sha512_abc.update("abc");
assert(sha512_abc.hexdigest() == "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
cout << "\"abc\": PASS" << endl;
// 测试长字符串
SHA512 sha512_long;
sha512_long.update("The quick brown fox jumps over the lazy dog");
assert(sha512_long.hexdigest() == "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6");
cout << "Long string: PASS" << endl;
// 测试分块更新
SHA512 sha512_chunk;
sha512_chunk.update("The quick brown fox ");
sha512_chunk.update("jumps over the lazy dog");
assert(sha512_chunk.hexdigest() == "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6");
cout << "Chunked update: PASS" << endl;
cout << "SHA512 tests completed successfully!" << endl << endl;
}
int main() {
try {
test_md5();
test_sha1();
test_sha256();
test_sha384();
test_sha512();
cout << "All tests passed!" << endl;
system("pause");
return 0;
} catch (const exception& e) {
cerr << "Test failed: " << e.what() << endl;
system("pause");
return 1;
}
}
正常情况下输出像这样(本人使用MinGW 4.9.2,C++14标准):

使用demo:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include "hash.hpp"
// 函数声明
void print_usage(const char* program_name);
std::vector<uint8_t> read_file(const std::string& filename);
void calculate_hash(const std::string& algorithm, const std::vector<uint8_t>& data);
int main(int argc, char* argv[]) {
// 检查参数数量
if (argc != 3) {
print_usage(argv[0]);
return 1;
}
std::string algorithm = argv[1];
std::string filename = argv[2];
// 转换为大写以便比较
for (char& c : algorithm) {
c = std::toupper(c);
}
// 读取文件
std::vector<uint8_t> file_data;
try {
file_data = read_file(filename);
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
return 1;
}
// 计算哈希值
try {
calculate_hash(algorithm, file_data);
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
return 1;
}
return 0;
}
void print_usage(const char* program_name) {
std::cout << "用法: " << program_name << " <算法> <文件名>" << std::endl;
std::cout << "支持的算法: MD5, SHA1, SHA256, SHA384, SHA512" << std::endl;
std::cout << "示例: " << program_name << " MD5 example.txt" << std::endl;
}
std::vector<uint8_t> read_file(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
throw std::runtime_error("无法打开文件: " + filename);
}
// 获取文件大小
file.seekg(0, std::ios::end);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
// 读取文件内容
std::vector<uint8_t> buffer(size);
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
throw std::runtime_error("读取文件失败: " + filename);
}
return buffer;
}
void calculate_hash(const std::string& algorithm, const std::vector<uint8_t>& data) {
if (algorithm == "MD5") {
MD5 md5;
md5.update(data);
std::cout << "MD5: " << md5.hexdigest() << std::endl;
} else if (algorithm == "SHA1") {
SHA1 sha1;
sha1.update(data);
std::cout << "SHA1: " << sha1.hexdigest() << std::endl;
} else if (algorithm == "SHA256") {
SHA256 sha256;
sha256.update(data);
std::cout << "SHA256: " << sha256.hexdigest() << std::endl;
} else if (algorithm == "SHA384") {
SHA384 sha384;
sha384.update(data);
std::cout << "SHA384: " << sha384.hexdigest() << std::endl;
} else if (algorithm == "SHA512") {
SHA512 sha512;
sha512.update(data);
std::cout << "SHA512: " << sha512.hexdigest() << std::endl;
} else {
throw std::runtime_error("不支持的算法: " + algorithm);
}
}
这是一个文件哈希计算工具,需要命令行传参,第一个参数是算法名,第二个参数是文件名。
三、哈希碰撞漏洞
前面讲了哈希值很难重复,但并不代表不可能,这就被称为碰撞漏洞。比如下面两个字符串:
d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70
和:
d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f965ab6ff72a70
就是一个经典的MD5碰撞漏洞,它们的MD5值就是一样的。
这俩字符串乍一看一摸一样,但只不过差别很小,很难看出来:

根本就不一样,而且它们的MD5值都是79054025255fb1a26e4bc422aef54eb4。
好了,本文到此结束!
更多推荐

所有评论(0)