Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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与密钥派生。