使用iText结合Digidentity外部服务签名PDF验证失败问题排查
PDF数字签名验证失败排查请求
我通过iText创建数字签名空容器,获取文件哈希后发送至Digidentity服务进行签名,收到字符串格式的数字签名后将其嵌入PDF的签名容器中,但在欧盟DSS验证网站无法通过验证并报错。
相关实现代码
public HashInfo[] ConvertHashToBase64(string[] filePaths, JsonData identityInfo, string uniqueId) { SPWeb cWeb = SPContext.Current.Web; List<HashInfo> hashInfos = new List<HashInfo>(); foreach (string filePath in filePaths) { var signInstance = new PAdESSignerClass(); signInstance.EmptySignature(signedPath, aWeb, identityInfo, uniqueId); SPFile file = aWeb.GetFile(filePath); var fileStream = signedFile.OpenBinary(); using (SHA256 sha256Hash = SHA256.Create()) { byte[] hashBytes = sha256Hash.ComputeHash(fileStream); string base64Hash = Convert.ToBase64String(hashBytes); hashInfos.Add(new HashInfo { Hash = base64Hash, UniqueId = file.UniqueId, FileName = file.Name }); } } return hashInfos.ToArray(); } public class PAdESSignerClass { public void EmptySignature(string containerFilePath, SPWeb web, JsonData jsonData, string guidString) { SPFile file = web.GetFile(containerFilePath); var cert = jsonData.cert; string certificate = cert.certificates[0].ToString(); byte[] certificateBytes = Convert.FromBase64String(certificate); X509Certificate2 xcertificate = new X509Certificate2(certificateBytes); IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); IX509Certificate[] iTextCertificates2 = FACTORY.CreateX509CertificateParser().ReadAllCerts(xcertificate.GetRawCertData()).ToArray(); using (Stream fileStream = file.OpenBinaryStream()) { using (MemoryStream memoryStream = new MemoryStream()) { iText.Kernel.Pdf.PdfReader reader = new iText.Kernel.Pdf.PdfReader(fileStream); PdfSigner signer = new PdfSigner(reader, memoryStream, new StampingProperties()); signer.GetSignatureAppearance().SetCertificate(iTextCertificates2[0]); signer.SetFieldName(guidString); iText.Signatures.IExternalSignatureContainer external = new ExternalBlankSignatureContainer(iText.Kernel.Pdf.PdfName.Adobe_PPKLite, iText.Kernel.Pdf.PdfName.Adbe_pkcs7_detached); signer.SignExternalContainer(external, 8192); byte[] pdfBytes = memoryStream.ToArray(); web.Files.Add(containerFilePath, pdfBytes, true); } } } public void AddSignatureToPdf(string certPath, JsonData jsonData, string containerFilePath, byte[] signatureHash, SPWeb web, string uniqueId, string timestamp) { var cert = jsonData.cert; string certificate = cert.certificates[0].ToString(); byte[] certificateBytes = Convert.FromBase64String(certificate); SPFile file = web.GetFile(containerFilePath); using (Stream fileStream = file.OpenBinaryStream()) { using (MemoryStream memoryStream = new MemoryStream()) { iText.Kernel.Pdf.PdfReader pdfDoc = new iText.Kernel.Pdf.PdfReader(fileStream); PdfSigner signer = new PdfSigner(pdfDoc, memoryStream, new iText.Kernel.Pdf.StampingProperties()); X509Certificate2 xcertificate = new X509Certificate2(certificateBytes); IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); IX509Certificate[] iTextCertificates2 = FACTORY.CreateX509CertificateParser().ReadAllCerts(xcertificate.GetRawCertData()).ToArray(); IPrivateKey iTextPrivateKey = FACTORY.CreateRsa2048KeyPairGenerator().GenerateKeyPair().GetPrivateKey(); signer.GetSignatureAppearance(); iText.Signatures.IExternalSignatureContainer external = new MyExternalSignatureContainer(iTextPrivateKey, iTextCertificates2, signatureHash, certificateBytes, timestamp); PdfSigner.SignDeferred(signer.GetDocument(), uniqueId, memoryStream, external); byte[] signedBytes = memoryStream.ToArray(); web.Files.Add(containerFilePath, signedBytes, true); } } } } public class MyExternalSignatureContainer : iText.Signatures.IExternalSignatureContainer { private readonly byte[] signedBytes; protected IPrivateKey pk; protected IX509Certificate[] chain; protected byte[] signatureHash; protected byte[] certificateBytes; protected string timestamp; public MyExternalSignatureContainer(IPrivateKey pk, IX509Certificate[] chain, byte[]signatureHash, byte[] certificateBytes, string timestamp) { this.pk = pk; this.chain = chain; this.signatureHash = signatureHash; this.certificateBytes = certificateBytes; this.timestamp = timestamp; } public void ModifySigningDictionary(iText.Kernel.Pdf.PdfDictionary signDic) { } public byte[] Sign(Stream inputStream) { byte[] encodedSig = null; iText.Signatures.PrivateKeySignature signature = new iText.Signatures.PrivateKeySignature(pk, "SHA256"); string digestAlgorithmName = signature.GetDigestAlgorithmName(); iText.Signatures.PdfPKCS7 sgn = new iText.Signatures.PdfPKCS7(null, chain, digestAlgorithmName, false); byte[] hash = iText.Signatures.DigestAlgorithms.Digest(inputStream, digestAlgorithmName); byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null); byte[] extSignature = signature.Sign(sh); sgn.SetExternalSignatureValue(signatureHash, null, signature.GetSignatureAlgorithmName()); encodedSig = sgn.GetEncodedPKCS7(hash, PdfSigner.CryptoStandard.CMS, null, null, null); return encodedSig; } }
Digidentity返回的身份信息示例
{ "key": { "status": "enabled", "algo": [ "1.2.840.113549.1.1.11" ], "len": 2048 }, "cert": { "status": "valid", "certificates": [ "MIIG1TCCBL2gAwIBAgI...V2yYRGjwk/GQsq67A==" ], "issuerDN": "C=NL,O=Digidentity B.V.,organizationIdentifier=NTRNL-27322631,CN=TEST Digidentity Personal Qualified CA", "serialNumber": "7867EX1A9M6PLE3A8C4K6751Q05ADD3C", "subjectDN": "C=NL,SN=Doe,GN=John,serialNumber=4exa3441-m601-44p6-9416-ls695cid3775,CN=John Doe", "validFrom": "20220209095106Z", "validTo": "20230209095106Z" }, "authMode": "implicit", "multisign": 1, "lang": "en-US", "SCAL": "2" }
更新内容(2024年5月24日)
若无需创建空签名容器,是否也不应使用SignDeferred方法?我当前获取哈希的流程如下:
string digestAlgorithmName = signature.GetDigestAlgorithmName(); byte[] hash = iText.Signatures.DigestAlgorithms.Digest(inputStream, digestAlgorithmName); byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
我使用该sh为CSC API生成哈希:
using (SHA256 sha256Hash = SHA256.Create()) { byte[] hashBytes = sha256Hash.ComputeHash(sh); string base64Hash = Convert.ToBase64String(hashBytes); hashInfos.Add(new HashInfo { Hash = base64Hash, UniqueId = file.UniqueId, FileName = file.Name }); }
之后执行:
byte[] extSignature = signature.Sign(sh);
并设置从Digidentity获取的签名:
sgn.SetExternalSignatureValue(signatureHash, null, signature.GetSignatureAlgorithmName());
请求协助排查PDF签名验证失败的问题。
内容的提问来源于stack exchange,提问作者Newbie
相关产品推荐
相关产品推荐

