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

使用IExternalSignatureContainer实现C# PAdES签名时数学摘要验证失败求助

使用IExternalSignatureContainer实现C# PAdES签名时数学摘要验证失败求助

兄弟,我看你这代码其实已经很接近正确实现了,就是几个细节没处理到位,导致签名验证时出现“数学摘要不匹配”的问题。我帮你梳理下最可能的问题点和修正方案:

1. Signed Attributes编码不一致(最常见原因)

你现在的代码里,生成签名用的是手动创建的DerSequence编码,而构建SignerInfo时又基于同一个attrVector重新生成了AttributeTable。这里的坑在于:ASN.1编码的细节(比如属性顺序、多余的空字节)可能会因为生成方式不同而出现差异,导致签名的内容和最终嵌入CMS的Signed Attributes不完全一致,验证时自然会提示内容不匹配。

修正方案:

直接用AttributeTable来获取签名所需的DER编码,确保和SignerInfo中使用的编码完全一致:

// 替换原来的derEncodedSignedAttributes生成代码
var attrTable = new Org.BouncyCastle.Asn1.Cms.AttributeTable(attrVector);
byte[] derEncodedSignedAttributes = attrTable.ToAsn1Object().GetEncoded(Asn1Encodable.Der);

然后构建SignerInfo时继续使用这个attrTable,这样签名的内容和CMS里的Signed Attributes就完全一致了。

2. SigningCertificateV2的Issuer信息处理有误

你在构建IssuerSerial时用了bcCert.IssuerDN,这个方法返回的是字符串格式的DN,再转成GeneralName时可能会丢失原始证书里的ASN.1编码细节(比如属性顺序、特殊字符的转义),导致SigningCertificateV2中的Issuer信息和证书实际的Issuer不匹配,间接影响验证。

修正方案:

直接取证书的原始Issuer ASN.1对象,而不是用字符串解析后的版本:

// 替换原来的issuerName相关代码
var issuerAsn1 = bcCert.Issuer; // 直接取证书原始的Issuer ASN.1结构
var issuerNameGeneral = new GeneralName(GeneralName.DirectoryName, issuerAsn1);

3. 确认智能卡的签名哈希算法

你指定的签名算法是"1.2.840.113549.1.1.11"(SHA256withRSA),要确保你的智能卡签名时确实用的是SHA256哈希算法。有些智能卡默认可能使用SHA1或其他哈希算法,这会导致签名的摘要和你计算的messageDigest不匹配。

检查建议:

如果你的智能卡签名库支持,可以在调用signer.sign之前明确指定哈希算法为SHA256,比如:

signer.InitSign(_pin, new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256));

4. 确认PDF签名字节流的正确性

最后,要确保传入Sign方法的data是PDF中真正需要签名的字节流——也就是去掉签名占位符后的原始内容。如果上游传入的字节流不对,即使签名逻辑正确,验证时也会出现摘要不匹配的问题。


修正后的完整代码示例

Sign方法关键修改:

public byte[] Sign(Stream data) {
    byte[] contentToBeSigned;
    using (var ms = new MemoryStream()) {
        data.Position = 0;
        data.CopyTo(ms);
        contentToBeSigned = ms.ToArray();
    }

    var bcCert = new X509CertificateParser().ReadCertificate(_certificate.getEncoded());
    Asn1EncodableVector attrVector = GetAttrributes(contentToBeSigned, bcCert);

    // 关键:用AttributeTable获取一致的Signed Attributes编码
    var attrTable = new Org.BouncyCastle.Asn1.Cms.AttributeTable(attrVector);
    byte[] derEncodedSignedAttributes = attrTable.ToAsn1Object().GetEncoded(Asn1Encodable.Der);

    // 智能卡签名
    var signer = _cardManager.GetSigner(_pin, _certificate);
    // 可选:明确指定哈希算法
    // signer.InitSign(_pin, new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256));
    byte[] realSignature = signer.sign(derEncodedSignedAttributes);
    _cardManager.Logout();

    // 用同一个attrTable构建SignerInfo
    var signatureFactory = new PrecalculatedSignatureFactory("1.2.840.113549.1.1.11", realSignature);
    var signerInfoGenBuilder = new SignerInfoGeneratorBuilder()
        .WithSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(attrTable));
    var signerInfoGen = signerInfoGenBuilder.Build(signatureFactory, bcCert);

    var cmsGen = new CmsSignedDataGenerator();
    cmsGen.AddSignerInfoGenerator(signerInfoGen);
    cmsGen.AddCertificate(bcCert);

    CmsSignedData signedData = cmsGen.Generate(new CmsProcessableByteArray(contentToBeSigned), false);
    return signedData.GetEncoded();
}

GetAttributes方法关键修改:

public Asn1EncodableVector GetAttrributes(byte[] contentToBeSigned, X509Certificate bcCert) {
    byte[] messageDigest = DigestUtilities.CalculateDigest("SHA-256", contentToBeSigned);

    byte[] certHash = DigestUtilities.CalculateDigest("SHA-256", _certificate.getEncoded());
    // 直接取证书原始Issuer ASN.1结构
    var issuerAsn1 = bcCert.Issuer;
    var serialNumber = bcCert.SerialNumber;
    var issuerNameGeneral = new GeneralName(GeneralName.DirectoryName, issuerAsn1);
    var issuerSerial = new IssuerSerial(new GeneralNames(issuerNameGeneral), new DerInteger(serialNumber));
    var signingCertV2 = new SigningCertificateV2(new[] { 
        new EssCertIDv2( 
            new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256), 
            certHash, 
            issuerSerial
        )
    });

    var attrVector = new Asn1EncodableVector();
    attrVector.Add(new Org.BouncyCastle.Asn1.Cms.Attribute(
        PkcsObjectIdentifiers.Pkcs9AtContentType, 
        new DerSet(CmsObjectIdentifiers.Data)));
    attrVector.Add(new Org.BouncyCastle.Asn1.Cms.Attribute(
        PkcsObjectIdentifiers.Pkcs9AtMessageDigest, 
        new DerSet(new DerOctetString(messageDigest))));
    attrVector.Add(new Org.BouncyCastle.Asn1.Cms.Attribute(
        new DerObjectIdentifier("1.2.840.113549.1.9.16.2.47"), 
        new DerSet(signingCertV2)));

    return attrVector;
}

先试试第一点的修改,这是最可能解决问题的地方。如果还是不行,再依次检查后面的几点。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 10:10:26