4.13 哈希与MAC实现:数据指纹与消息认证
哈希算法家族
SoftHSM2支持的哈希算法:
哈希算法家族:
HashAlgorithm(抽象基类)
│
├── SHA-1
│ ├── BotanSHA1 (Botan实现)
│ └── OSSLSHA1 (OpenSSL实现)
│ └── 输出长度:20字节(160位)
│
├── SHA-224
│ ├── BotanSHA224
│ └── OSSLSHA224
│ └── 输出长度:28字节(224位)
│
├── SHA-256
│ ├── BotanSHA256
│ └── OSSLSHA256
│ └── 输出长度:32字节(256位)
│
├── SHA-384
│ ├── BotanSHA384
│ └── OSSLSHA384
│ └── 输出长度:48字节(384位)
│
├── SHA-512
│ ├── BotanSHA512
│ └── OSSLSHA512
│ └── 输出长度:64字节(512位)
│
└── MD5(不推荐使用)
├── BotanMD5
└── OSSLMD5
└── 输出长度:16字节(128位)
HashAlgorithm抽象类
HashAlgorithm定义了哈希计算的通用接口:
struct HashAlgo
{
enum Type
{
Unknown,
MD5,
SHA1,
SHA224,
SHA256,
SHA384,
SHA512,
GOST
};
};
class HashAlgorithm
{
public:
HashAlgorithm();
virtual ~HashAlgorithm() { }
virtual bool hashInit();
virtual bool hashUpdate(const ByteString& data);
virtual bool hashFinal(ByteString& hashedData);
virtual int getHashSize() = 0;
protected:
enum { NONE, HASHING } currentOperation;
};
使用流程:
哈希计算流程:
1. hashInit() 初始化哈希上下文
↓
2. hashUpdate() 添加数据(可多次调用)
↓
↓ hashUpdate() 添加更多数据
↓
3. hashFinal() 计算最终哈希值
↓
输出:固定长度的哈希值
SHA-256计算过程
SHA-256是最常用的安全哈希算法:
SHA-256计算过程:
输入数据
│
│ 1. 添加填充
│ ├── 添加1位:1
│ ├── 添加0位直到长度≡448 mod 512
│ └── 添加64位原始长度
▼
填充后的数据(512位块)
│
│ 2. 初始化Hash值
│ H0 = 0x6a09e667
│ H1 = 0xbb67ae85
│ H2 = 0x3c6ef372
│ H3 = 0xa54ff53a
│ H4 = 0x510e527f
│ H5 = 0x9b05688c
│ H6 = 0x1f83d9ab
│ H7 = 0x5be0cd19
▼
初始Hash值
│
│ 3. 处理每个512位块
│ ├── 扩展为64个32位字(W0-W63)
│ ├── 64轮压缩
│ │ ├── 计算中间变量
│ │ ├── 更新Hash值
│ │ └── 使用K常量
│ └── 更新H0-H7
▼
最终Hash值
│
│ 4. 输出
│ H0-H7拼接为256位
▼
32字节SHA-256值
OSSLSHA256实现
OSSLSHA256使用OpenSSL EVP API:
class OSSLSHA256 : public OSSLEVPHashAlgorithm
{
public:
OSSLSHA256();
virtual ~OSSLSHA256();
virtual bool hashInit();
virtual bool hashUpdate(const ByteString& data);
virtual bool hashFinal(ByteString& hashedData);
virtual int getHashSize() { return 32; }
private:
EVP_MD_CTX* ctx;
};
OSSLSHA256::OSSLSHA256()
{
ctx = EVP_MD_CTX_new();
}
OSSLSHA256::~OSSLSHA256()
{
if (ctx != NULL) {
EVP_MD_CTX_free(ctx);
}
}
bool OSSLSHA256::hashInit()
{
currentOperation = HASHING;
// 初始化EVP上下文
if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1) {
return false;
}
return true;
}
bool OSSLSHA256::hashUpdate(const ByteString& data)
{
if (currentOperation != HASHING) {
return false;
}
// 更新哈希
if (EVP_DigestUpdate(ctx, data.byte_str(), data.size()) != 1) {
return false;
}
return true;
}
bool OSSLSHA256::hashFinal(ByteString& hashedData)
{
if (currentOperation != HASHING) {
return false;
}
// 完成哈希
unsigned char buffer[EVP_MAX_MD_SIZE];
unsigned int len;
if (EVP_DigestFinal_ex(ctx, buffer, &len) != 1) {
return false;
}
hashedData = ByteString(buffer, len);
currentOperation = NONE;
return true;
}
BotanSHA256实现
BotanSHA256使用Botan C++ API:
class BotanSHA256 : public BotanHashAlgorithm
{
public:
BotanSHA256();
virtual ~BotanSHA256();
virtual bool hashInit();
virtual bool hashUpdate(const ByteString& data);
virtual bool hashFinal(ByteString& hashedData);
virtual int getHashSize() { return 32; }
private:
Botan::SHA_256* hash;
};
BotanSHA256::BotanSHA256()
{
hash = new Botan::SHA_256();
}
BotanSHA256::~BotanSHA256()
{
delete hash;
}
bool BotanSHA256::hashInit()
{
currentOperation = HASHING;
hash->clear();
return true;
}
bool BotanSHA256::hashUpdate(const ByteString& data)
{
if (currentOperation != HASHING) {
return false;
}
hash->update(data.byte_str(), data.size());
return true;
}
bool BotanSHA256::hashFinal(ByteString& hashedData)
{
if (currentOperation != HASHING) {
return false;
}
Botan::SecureVector<Botan::byte> result = hash->final();
hashedData = ByteString(result.begin(), result.size());
currentOperation = NONE;
return true;
}
MAC算法家族
MAC(Message Authentication Code)用于消息认证:
MAC算法家族:
MacAlgorithm(抽象基类)
│
├── HMAC(基于哈希的MAC)
│ ├── HMAC-SHA1
│ ├── HMAC-SHA256
│ ├── HMAC-SHA384
│ ├── HMAC-SHA512
│ └── HMAC-MD5(不推荐)
│
├── CMAC(基于块密码的MAC)
│ ├── AES-CMAC
│ └── DES-CMAC
│
└── GMAC(基于GCM的MAC)
├── AES-GMAC
HMAC原理
HMAC使用密钥和哈希计算MAC:
HMAC计算原理:
HMAC(K, M) = H((K⊕opad) || H((K⊕ipad) || M))
其中:
├── H:哈希函数(SHA-256等)
├── K:密钥
├── M:消息
├── opad:0x5c重复块大小
├── ipad:0x36重复块大小
├── ||:拼接
├── ⊕:异或
计算流程:
┌─────────────────────────────────────────────┐
│ │
│ 密钥K │
│ │ │
│ ├── ⊕ ipad (0x36...) │
│ │ │
│ │ K⊕ipad │
│ │ │ │
│ │ ├── || 消息M │
│ │ │ │
│ │ │ (K⊕ipad)||M │
│ │ │ │ │
│ │ │ ├── H() │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ 内层Hash │
│ │ │ │ │
│ ├── ⊕ opad (0x5c...) │
│ │ │ │
│ │ K⊕opad │
│ │ │ │
│ │ ├── || 内层Hash │
│ │ │ │
│ │ │ (K⊕opad)||内层Hash │
│ │ │ │ │
│ │ │ ├── H() │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ 最终MAC值 │
│ │ │
└─────────────────────────────────────────────┘
MacAlgorithm抽象类
MacAlgorithm定义了MAC计算的通用接口:
struct MacAlgo
{
enum Type
{
Unknown,
HMAC_MD5,
HMAC_SHA1,
HMAC_SHA224,
HMAC_SHA256,
HMAC_SHA384,
HMAC_SHA512,
AES_CMAC,
DES_CMAC,
AES_GMAC
};
};
class MacAlgorithm
{
public:
MacAlgorithm();
virtual ~MacAlgorithm() { }
virtual bool macInit(const SymmetricKey* key);
virtual bool macUpdate(const ByteString& data);
virtual bool macFinal(ByteString& macData);
virtual size_t getMacSize() = 0;
protected:
const SymmetricKey* currentKey;
enum { NONE, MACING } currentOperation;
};
OSSLHMACSHA256实现
OSSLHMAC使用OpenSSL HMAC API:
class OSSLHMACSHA256 : public OSSLEVPMacAlgorithm
{
protected:
virtual const EVP_MD* getEVPHash() const;
virtual size_t getMacSize() const;
};
const EVP_MD* OSSLHMACSHA256::getEVPHash() const
{
return EVP_sha256();
}
size_t OSSLHMACSHA256::getMacSize() const
{
return 32; // SHA-256输出32字节
}
class OSSLEVPMacAlgorithm : public MacAlgorithm
{
public:
virtual bool macInit(const SymmetricKey* key);
virtual bool macUpdate(const ByteString& data);
virtual bool macFinal(ByteString& macData);
private:
EVP_MD_CTX* ctx;
};
bool OSSLEVPMacAlgorithm::macInit(const SymmetricKey* key)
{
currentKey = key;
currentOperation = MACING;
ctx = EVP_MD_CTX_new();
// 初始化HMAC(教学简化:使用EVP_PKEY_new_raw_private_key构造密钥对象)
EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
key->getKeyBits().byte_str(),
key->getKeyBits().size());
if (!pkey) {
EVP_MD_CTX_free(ctx);
return false;
}
if (EVP_DigestSignInit(ctx, NULL, getEVPHash(), NULL, pkey) != 1) {
EVP_PKEY_free(pkey);
EVP_MD_CTX_free(ctx);
return false;
}
EVP_PKEY_free(pkey);
return true;
}
bool OSSLEVPMacAlgorithm::macUpdate(const ByteString& data)
{
if (currentOperation != MACING) {
return false;
}
if (EVP_DigestSignUpdate(ctx, data.byte_str(), data.size()) != 1) {
return false;
}
return true;
}
bool OSSLEVPMacAlgorithm::macFinal(ByteString& macData)
{
if (currentOperation != MACING) {
return false;
}
size_t len = getMacSize();
unsigned char buffer[len];
if (EVP_DigestSignFinal(ctx, buffer, &len) != 1) {
EVP_MD_CTX_free(ctx);
return false;
}
macData = ByteString(buffer, len);
EVP_MD_CTX_free(ctx);
currentOperation = NONE;
return true;
}
CMAC实现
CMAC基于块密码计算MAC:
CMAC计算原理:
CMAC使用AES或DES计算MAC:
输入:
├── 密钥K
├── 消息M
│
输出:
├── MAC值(块大小)
计算流程:
┌─────────────────────────────────────────────┐
│ │
│ 密钥K │
│ │ │
│ ├── AES加密全零 → K1 │
│ ├── K1左移 → K2 │
│ │ │
│ 消息M │
│ │ │
│ ├── 分块 │
│ ├── 填充 │
│ │ │
│ CBC-MAC计算: │
│ ├── M1 ⊕ K1 → AES → C1 │
│ ├── C1 ⊕ M2 → AES → C2 │
│ ├── ... │
│ ├── 最后块 ⊕ K2 → AES → MAC │
│ │
└─────────────────────────────────────────────┘
class OSSLCMAC : public OSSLEVPMacAlgorithm
{
public:
OSSLCMAC() : cmac_ctx(NULL) {}
~OSSLCMAC() { if (cmac_ctx) CMAC_CTX_free(cmac_ctx); }
protected:
virtual const EVP_CIPHER* getEVPCipher() const;
virtual size_t getMacSize() const { return 16; }
private:
CMAC_CTX* cmac_ctx;
};
bool OSSLCMAC::macInit(const SymmetricKey* key)
{
if (cmac_ctx) { CMAC_CTX_free(cmac_ctx); cmac_ctx = NULL; }
// 初始化CMAC(教学简化:OpenSSL 3.x使用EVP_MAC,此处展示CMAC_CTX方式)
cmac_ctx = CMAC_CTX_new();
if (!cmac_ctx) return false;
if (!CMAC_Init(cmac_ctx, key->getKeyBits().byte_str(),
key->getKeyBits().size(),
getEVPCipher(), NULL)) {
CMAC_CTX_free(cmac_ctx);
cmac_ctx = NULL;
return false;
}
return true;
}
哈希与MAC完整流程
哈希计算完整流程:
C_DigestInit
│
│ CryptoFactory::i()->getHashAlgorithm(HashAlgo::SHA256)
│ hash->hashInit()
▼
哈希初始化
│
│ C_DigestUpdate
│ hash->hashUpdate(data)
▼
添加数据
│
│ C_DigestFinal
│ hash->hashFinal(hashValue)
▼
输出哈希值
│
│ CryptoFactory::i()->recycleHashAlgorithm(hash)
▼
回收算法实例
MAC计算完整流程:
C_SignInit(HMAC机制)
│
│ CryptoFactory::i()->getMacAlgorithm(MacAlgo::HMAC_SHA256)
│ mac->macInit(key)
▼
MAC初始化
│
│ C_SignUpdate
│ mac->macUpdate(data)
▼
添加数据
│
│ C_SignFinal
│ mac->macFinal(macValue)
▼
输出MAC值
│
│ CryptoFactory::i()->recycleMacAlgorithm(mac)
▼
回收算法实例
一个类比:指纹与签名
指纹与签名类比:
哈希(指纹)
│
├── 特点
│ ├── 固定长度输出
│ ├── 单向计算(不可逆)
│ ├── 相同输入产生相同输出
│ ├── 不同输入产生不同输出(碰撞概率低)
│
├── 用途
│ ├── 数据完整性校验
│ ├── 数字签名预处理
│ ├── 密码存储
│
└── 类比
├── 每个人有固定指纹
├── 指纹唯一标识身份
├── 不能从指纹恢复人
├── 相同人指纹相同
MAC(签名)
│
├── 特点
│ ├── 需要密钥
│ ├── 固定长度输出
│ ├── 可验证(有密钥)
│ ├── 防伪造(无密钥不能伪造)
│
├── 用途
│ ├── 消息认证
│ ├── 数据完整性+认证
│ ├── 安全通信
│
└── 类比
├── 签名需要个人印章(密钥)
├── 签名可以验证身份
├── 无印章无法伪造签名
├── 签名证明消息来源
本篇小结
今天我们分析了SoftHSM2的哈希与MAC实现。
HashAlgorithm抽象类:
- hashInit:初始化
- hashUpdate:添加数据
- hashFinal:输出哈希
哈希算法:
- SHA-1:160位(不推荐)
- SHA-256:256位(推荐)
- SHA-512:512位
- MD5:128位(不推荐)
SHA-256计算:
- 填充数据
- 初始化Hash值
- 64轮压缩
- 输出256位
MacAlgorithm抽象类:
- macInit:初始化(需要密钥)
- macUpdate:添加数据
- macFinal:输出MAC
HMAC原理:
- 内层:K⊕ipad || M
- 外层:K⊕opad || 内层Hash
- 输出:与哈希相同长度
CMAC原理:
- 基于AES
- CBC-MAC变种
- 最后块特殊处理
OpenSSL实现:
- EVP_DigestInit/Update/Final
- EVP_DigestSign API
- HMAC API
Botan实现:
- Botan::SHA_256类
- Botan::HMAC类
- C++原生API
下一节,我们将分析RNG与密钥派生——随机数生成器和HKDF实现。
【下集预告】
随机数如何生成?
RNG如何保证安全?
HKDF如何派生密钥?
密钥派生函数详解?
下一节,RNG与密钥派生。