You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TLS1.3 RSA-PSS-RSAE-SHA256证书验证算法实现问题求助

TLS 1.3 Certificate Verify (RSA-PSS-RSAE-SHA256) 分步实现与调试指南

核心问题排查点

你当前的验证流程框架正确,但大概率在EMSA-PSS的字节解析细节或RSA解密后的EM格式处理上出现偏差,以下是修正后的标准步骤及可调试代码:

标准验证分步流程

1. 准备基础数据

  • 从证书中提取RSA公钥参数:模数n(大整数)、公钥指数e(通常为65537)
  • 获取CertificateVerify消息中的原始签名字节(注意:不是ASN.1编码的签名,是RSA加密后的EM字节)
  • 计算握手消息的transcript-hash:将ClientHello到Certificate的所有握手消息(含消息头的类型、长度字段)按顺序拼接,再计算SHA256哈希(32字节)

2. 构造待签名消息M(RFC8446 §4.4.3)

M的字节结构严格为:

64个0x20字节 + b"TLS 1.3, server CertificateVerify" + 1个0x00字节 + transcript-hash(32字节)

总长度为 64 + 31 + 1 + 32 = 128 字节(注:b"TLS 1.3, server CertificateVerify" 的字节长度为31)

3. RSA解密签名得到EM

  • 将签名字节转换为大端整数 sig_int
  • 用公钥解密:em_int = pow(sig_int, e, n)
  • 将em_int转换为字节数组,补前导零至模数长度(如2048位RSA对应256字节),得到编码消息EM

4. EMSA-PSS验证(RFC8017 §9.1.2)

针对SHA256的参数:hLen=32(哈希长度),sLen=32(salt长度,RSASSA-PSS-RSAE-SHA256要求salt长度等于哈希长度)

  1. 检查EM长度是否 ≥ hLen + sLen + 2,且最后一个字节为0xbc,不满足则验证失败
  2. 去掉EM最后一个字节得到EM',取EM'的最后 1 + hLen + sLen = 65 字节作为DB
  3. 检查DB的第一个字节是否为0x00,不满足则验证失败
  4. 从DB中提取:
    • H:DB[1:33](第2到第33字节,共32字节)
    • salt:DB[33:](第34到第65字节,共32字节)
  5. 计算mHash = SHA256(M)
  6. 构造M' = 8个0x00字节 + mHash + salt,计算H' = SHA256(M')
  7. 对比H与H',相等则验证成功

可分步调试的Python代码

import hashlib

def tls13_cert_verify_rsapss(n, e, signature, transcript_hash):
    # 步骤1:构造待签名消息M
    prefix_64_20 = b'\x20' * 64
    label = b"TLS 1.3, server CertificateVerify"
    null_byte = b'\x00'
    M = prefix_64_20 + label + null_byte + transcript_hash
    print(f"[Step 1] M length: {len(M)} bytes")
    print(f"[Step 1] M hex: {M.hex()}")

    # 步骤2:RSA解密签名得到EM
    sig_int = int.from_bytes(signature, byteorder='big')
    em_int = pow(sig_int, e, n)
    mod_len = (n.bit_length() + 7) // 8
    EM = em_int.to_bytes(mod_len, byteorder='big')
    print(f"\n[Step 2] EM length: {len(EM)} bytes (mod length: {mod_len})")
    print(f"[Step 2] EM hex: {EM.hex()}")

    # 步骤3:EMSA-PSS解析与校验
    hLen = 32
    sLen = hLen
    emLen = len(EM)

    if emLen < hLen + sLen + 2:
        print(f"[Step 3] Error: EM length {emLen} < required {hLen + sLen + 2}")
        return False
    if EM[-1] != 0xbc:
        print(f"[Step 3] Error: EM last byte is {hex(EM[-1])}, expected 0xbc")
        return False

    EM_prime = EM[:-1]
    db_len = 1 + hLen + sLen
    if len(EM_prime) < db_len:
        print(f"[Step 3] Error: EM' length {len(EM_prime)} < DB length {db_len}")
        return False
    DB = EM_prime[-db_len:]
    if DB[0] != 0x00:
        print(f"[Step 3] Error: DB first byte is {hex(DB[0])}, expected 0x00")
        return False

    H = DB[1:1+hLen]
    salt = DB[1+hLen:]
    print(f"\n[Step 3] Extracted H hex: {H.hex()}")
    print(f"[Step 3] Extracted salt hex: {salt.hex()}")

    # 步骤4:计算并对比哈希
    mHash = hashlib.sha256(M).digest()
    print(f"\n[Step 4] mHash hex: {mHash.hex()}")

    M_prime = b'\x00' * 8 + mHash + salt
    H_prime = hashlib.sha256(M_prime).digest()
    print(f"[Step 4] M' hex: {M_prime.hex()}")
    print(f"[Step 4] H' hex: {H_prime.hex()}")

    if H == H_prime:
        print("\n[Result] Verification SUCCESS")
        return True
    else:
        print("\n[Result] Verification FAILED: H != H'")
        return False

调试建议

  1. 重点检查EM的长度是否与模数长度一致,若解密后的字节不足,必须补前导零
  2. 确认DB的第一个字节为0x00,这是EMSA-PSS格式的强制要求,很多解析错误都出在这里
  3. 对比mHash的计算结果是否与预期一致,排除transcript-hash的拼接错误

内容的提问来源于stack exchange,提问作者FERcsI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:35:02