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

手动验证TLS证书链签名失败,求C#实现问题排查

手动验证TLS证书签名失败排查

问题描述

为学习目的,尝试用C#手动验证TLS证书链(仅验证签名),梳理的验证步骤如下:

  • 计算证书的哈希值;
  • 读取证书的签名哈希值;
  • 使用父证书公钥调用RSA.Verify()方法进行验证。

编写的代码如下:

var myCert = new X509Certificate2(@"Z:\springer.com.crt");
var parentCert = new X509Certificate2(@"Z:\R3.crt");

var certHash = myCert.GetCertHash(HashAlgorithmName.SHA256);

var certSignature = new byte[] { 
    0xAA, 0xF7, 0x2A, 0x84, 0x95, 0x17, 0x2E, 0xCD, 0x58, 0x84, 0x66,
    0x9A, 0xC0, 0x0B, 0x3E, 0x13, 0x0C, 0x94, 0x34, 0x35, 0x31, 0x85, 0xA3, 0x43, 0xBC, 0xDA, 0xFC, 0x00, 0x3F, 0xB3, 0x6D, 0x9E,
    0x4A, 0xAB, 0xEE, 0xDB, 0x34, 0xCB, 0xCE, 0x8F, 0xD2, 0x04, 0x73, 0x4B, 0xE7, 0x11, 0x5E, 0x9E, 0xDC, 0x6B, 0x8A, 0xDB, 0xDD,
    0xB2, 0x6B, 0x1A, 0x7C, 0xE3, 0xFA, 0x97, 0xCC, 0xDF, 0x03, 0xAE, 0xFC, 0x41, 0xDC, 0x5D, 0x6E, 0x70, 0x71, 0x2D, 0x67, 0x96,
    0x0D, 0x94, 0xC0, 0x41, 0xD9, 0xD0, 0x4E, 0xD7, 0x88, 0x99, 0xED, 0xDB, 0x84, 0x9D, 0x35, 0x73, 0xB0, 0x4B, 0x0A, 0x28, 0x19,
    0xAE, 0x62, 0xD4, 0xEE, 0x17, 0xF9, 0x83, 0xFA, 0xDF, 0x56, 0xBE, 0xBB, 0xE9, 0x8F, 0x78, 0x85, 0x11, 0x57, 0x0C, 0xC4, 0x46,
    0xEC, 0xDA, 0xE2, 0xD3, 0x14, 0x86, 0xEA, 0x65, 0xC0, 0x19, 0xDA, 0x9B, 0x8D, 0x21, 0x7D, 0x34, 0xBA, 0x0E, 0xD4, 0xC5, 0xD2,
    0x20, 0x32, 0x15, 0xAA, 0x2C, 0x3B, 0x72, 0x95, 0x8E, 0xA7, 0x72, 0x26, 0x99, 0xA0, 0x5F, 0x43, 0xAC, 0xF9, 0x42, 0x91, 0x35,
    0x0C, 0xB0, 0x0E, 0xE9, 0xF4, 0xBC, 0x25, 0x64, 0x59, 0x98, 0x5E, 0x34, 0x65, 0x9B, 0x28, 0x28, 0x3D, 0x88, 0x23, 0x25, 0x00,
    0x97, 0x38, 0xE1, 0x76, 0xBC, 0x74, 0xB9, 0xFF, 0xBD, 0x17, 0x60, 0x1F, 0x07, 0x05, 0xF0, 0xCF, 0xB4, 0x5E, 0x13, 0x8F, 0xBE,
    0x48, 0x52, 0xF2, 0xD7, 0xBA, 0xEC, 0x44, 0xDC, 0x93, 0x7D, 0x16, 0x82, 0x8F, 0xE5, 0xC3, 0x55, 0x31, 0x99, 0x0A, 0xE1, 0xB1,
    0xC4, 0xE4, 0xCE, 0x31, 0xA5, 0x91, 0xFD, 0x71, 0xF6, 0xF7, 0x01, 0x6D, 0x30, 0x2E, 0xFA, 0x30, 0x41, 0x11, 0xBB, 0x96, 0x4B,
    0xA1, 0xF0, 0x10, 0x71, 0x60, 0x07, 0x5B, 0xD2, 0x30, 0xA0, 0xC2, 0x29, 0x0E, 0x71 };

var result = 
    parentCert.GetRSAPublicKey().VerifyHash(certHash, certSignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

但始终无法让result返回True,请问哪里存在问题?

备注:使用的TLS证书来自springer.com;由于.NET原生X509Certificate类没有提取签名的方法,且引入BouncyCastle过于冗余,故硬编码了证书签名值。


问题根源及解决方案

1. 错误的哈希计算对象

GetCertHash()返回的是整个证书的指纹哈希,而X.509证书的签名是对证书的TBS(To Be Signed)部分进行签名的,这部分是证书中除了签名算法和签名值之外的核心内容。直接用证书指纹哈希验证签名,对象完全错误,这是导致验证失败的核心原因。

2. 硬编码签名的格式/字节序问题

X.509证书的签名值是ASN.1 DER编码的,且存储时可能存在字节序反转或格式偏差,硬编码的签名值很可能没有匹配正确的验证格式,导致验证不通过。

3. 无需硬编码签名:用.NET原生API提取TBS和签名

可以通过解析证书的原始ASN.1数据,直接提取TBS部分和签名值,无需依赖第三方库。以下是修正后的代码:

using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;

var myCert = new X509Certificate2(@"Z:\springer.com.crt");
var parentCert = new X509Certificate2(@"Z:\R3.crt");

// 解析证书原始ASN.1数据,提取TBS部分和签名值
using (var asnReader = new AsnReader(myCert.RawData, AsnEncodingRules.DER))
{
    // 证书结构:SEQUENCE { tbsCertificate SEQUENCE, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING }
    var certSequence = asnReader.ReadSequence();
    
    // 读取TBS证书部分(待签名的核心内容)
    var tbsCertificate = certSequence.ReadEncodedValue();
    // 跳过签名算法字段(已明确使用SHA256,无需解析)
    certSequence.ReadEncodedValue();
    // 读取签名值:BIT STRING的第一个字节是未使用的比特数,需跳过
    var signatureBitString = certSequence.ReadBitString(out int unusedBits);
    var certSignature = signatureBitString.Skip(1).ToArray();

    // 计算TBS部分的SHA256哈希
    using (var sha256 = SHA256.Create())
    {
        var tbsHash = sha256.ComputeHash(tbsCertificate);
        
        // 使用父证书公钥验证签名
        var result = parentCert.GetRSAPublicKey()
            .VerifyHash(tbsHash, certSignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        
        Console.WriteLine(result ? "验证成功" : "验证失败");
    }
}

关键说明

  • X.509证书遵循固定的ASN.1结构,必须按顺序读取TBS、签名算法、签名值三个部分。
  • 签名值存储为BIT STRING类型,其第一个字节表示未使用的比特数,验证时必须跳过该字节才能得到正确的签名数据。
  • 只有TBS部分的哈希,才是签名对应的原始哈希值,与签名验证的逻辑完全匹配。

内容的提问来源于stack exchange,提问作者Ryan B.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 12:00:01