使用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

