带有iTextSharp的外部签名PDF-更改/损坏的文档

问题描述 投票:0回答:1

目标是实现PDF签名过程,在该过程中,服务器(.NET Core服务)根据请求(Electron)向客户端提供要签名的哈希。然后,客户端使用通过PKCS#11接口从智能卡获得的私钥对给定的哈希签名。然后将签名发送回服务器,以使用iTextSharp附加到PDF文件中。

目前,使用node-webcrypto-p11使用智能卡令牌对哈希进行签名的过程非常简单(需要大量的反复试验才能到达此处。)使用的算法是RSASSA-PKCS1-v1_5。我可以成功签名哈希,然后再进行验证。

我最近在External signing PDF with iTextsharp (3)的帮助下构建了以前的实现,在其中我使用getAuthenticatedAttributeBytes获取要签名的哈希值(由[[mkl建议)。

[在Acrobat Reader中检查签名时,向我展示了与OP

pgkdev]相同的已更改/损坏的可怕文档。如上所述,客户端在签名过程中很简单,我不怀疑那里会出现任何问题(不过我愿意对此进行审查)。

pgkdev

指的是Priyanka's question,在这里我发现我可能在签署文档的两步过程中遇到了问题,其中哈希值不再相同。
如果您勾选Grazina's question,当您一步完成该过程时,我们可以看到这样的实现成功。

mkl

进一步提到了一种成功完成此步骤的方法,但需要两个步骤,但是我缺少更多关于如何准确实现此目的的解释。
注意:我(据我所知)无法一步一步完成我想做的事情,因为签名是由客户端在Electron应用程序中发起的。

enter image description hereenter image description hereenter image description here

单击证书详细信息将显示我的完整证书详细信息。

private const string SIG_FIELD_NAME = "sigField1"; private byte[] GetPDFHash(string pdfFilePath, byte[] certificateValue) { var preparedSigPdfFilePath = $"{pdfFilePath}.tempsig.pdf"; //Get certificates chain from certificate value ICollection<X509Certificate> certificatesChain = GetCertificatesChain(certificateValue); byte[] hash = CreatePDFEmptySignature(pdfFilePath, preparedSigPdfFilePath, certificatesChain); return hash; } private void SignPDFHash(string pdfFilePath, byte[] hash, byte[] signedHash, byte[] certificateValue) { var preparedSigPdfFilePath = $"{pdfFilePath}.tempsig.pdf"; var signedPdfFilePath = $"{pdfFilePath}.signed.pdf"; //Get certificates chain from certificate value ICollection<X509Certificate> certificatesChain = GetCertificatesChain(certificateValue); CreateFinalSignature(preparedSigPdfFilePath, signedPdfFilePath, hash, signedHash, certificatesChain); } private byte[] CreatePDFEmptySignature(string pdfFilePath, string preparedSigPdfFilePath, ICollection<X509Certificate> certificatesChain) { byte[] hash; using (PdfReader reader = new PdfReader(pdfFilePath)) { using (FileStream baos = System.IO.File.OpenWrite(preparedSigPdfFilePath)) { PdfStamper pdfStamper = PdfStamper.CreateSignature(reader, baos, '\0', null, true); PdfSignatureAppearance sap = pdfStamper.SignatureAppearance; sap.SetVisibleSignature(new Rectangle(36, 720, 160, 780), 1, SIG_FIELD_NAME); sap.Certificate = certificatesChain.First(); var externalEmptySigContainer = new MyExternalEmptySignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_DETACHED, preparedSigPdfFilePath, certificatesChain); MakeSignature.SignExternalContainer(sap, externalEmptySigContainer, 8192); hash = externalEmptySigContainer.PdfHash; } } return hash; } private void CreateFinalSignature(string preparedSigPdfFilePath, string signedPdfFilePath, byte[] hash, byte[] signedHash, ICollection<X509Certificate> certificatesChain) { using (PdfReader reader = new PdfReader(preparedSigPdfFilePath)) { using (FileStream baos = System.IO.File.OpenWrite(signedPdfFilePath)) { IExternalSignatureContainer externalSigContainer = new MyExternalSignatureContainer(hash, signedHash, certificatesChain); MakeSignature.SignDeferred(reader, SIG_FIELD_NAME, baos, externalSigContainer); } } } public class MyExternalEmptySignatureContainer : ExternalBlankSignatureContainer { public string PdfTempFilePath { get; set; } public byte[] PdfHash { get; private set; } public ICollection<X509Certificate> CertificatesList { get; set; } public MyExternalEmptySignatureContainer(PdfName filter, PdfName subFilter, string pdfTempFilePath, ICollection<X509Certificate> certificatesChain) : base(filter, subFilter) { PdfTempFilePath = pdfTempFilePath; CertificatesList = certificatesChain; } override public byte[] Sign(Stream data) { byte[] sigContainer = base.Sign(data); //Get the hash IDigest messageDigest = DigestUtilities.GetDigest("SHA-256"); byte[] messageHash = DigestAlgorithms.Digest(data, messageDigest); #region Log var messageHashFilePath = $"{PdfTempFilePath}.messageHash-b64.txt"; System.IO.File.WriteAllText(messageHashFilePath, Convert.ToBase64String(messageHash)); #endregion Log //Add hash prefix byte[] sha256Prefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; byte[] digestInfo = new byte[sha256Prefix.Length + messageHash.Length]; sha256Prefix.CopyTo(digestInfo, 0); messageHash.CopyTo(digestInfo, sha256Prefix.Length); #region Log var messageHashWithPrefixFilePath = $"{PdfTempFilePath}.messageHash-with-prefix-b64.txt"; System.IO.File.WriteAllText(messageHashWithPrefixFilePath, Convert.ToBase64String(digestInfo)); #endregion Log var sgn = new PdfPKCS7(null, this.CertificatesList, "SHA256", false); var authenticatedAttributeBytes = sgn.getAuthenticatedAttributeBytes(messageHash, null, null, CryptoStandard.CMS); PdfHash = authenticatedAttributeBytes; return sigContainer; } } public class MyExternalSignatureContainer : IExternalSignatureContainer { public byte[] Hash { get; set; } public byte[] SignedHash { get; set; } public ICollection<X509Certificate> CertificatesList { get; set; } public MyExternalSignatureContainer(byte[] hash, byte[] signedHash, ICollection<X509Certificate> certificatesList) { Hash = hash; SignedHash = signedHash; CertificatesList = certificatesList; } public byte[] Sign(Stream data) { PdfPKCS7 sgn = new PdfPKCS7(null, this.CertificatesList, "SHA256", false); sgn.SetExternalDigest(this.SignedHash, null, "RSA"); return sgn.GetEncodedPKCS7(this.Hash, null, null, null, CryptoStandard.CMS); } public void ModifySigningDictionary(PdfDictionary signDic) { } } private ICollection<X509Certificate> GetCertificatesChain(byte[] certByteArray) { ICollection<X509Certificate> certChain = new Collection<X509Certificate>(); X509Certificate2 cert = new X509Certificate2(certByteArray); X509Certificate regularCert = new X509CertificateParser() .ReadCertificate(cert.GetRawCertData()); certChain.Add(regularCert); return certChain; }

编辑:Signed PDF

编辑:调整了CreateFinalSignature以使用messageHash(已保存到.txt文件中)。结果是一样的。Signed PDF

private void CreateFinalSignature(string preparedSigPdfFilePath, string signedPdfFilePath, byte[] signedHash, ICollection<X509Certificate> certificatesChain) { var messageHashFilePath = $"{preparedSigPdfFilePath}.messageHash-b64.txt"; string hashString = System.IO.File.ReadAllText(messageHashFilePath); byte[] hash = Convert.FromBase64String(hashString); using (PdfReader reader = new PdfReader(preparedSigPdfFilePath)) { using (FileStream baos = System.IO.File.OpenWrite(signedPdfFilePath)) { IExternalSignatureContainer externalSigContainer = new MyExternalSignatureContainer(hash, signedHash, certificatesChain); MakeSignature.SignDeferred(reader, SIG_FIELD_NAME, baos, externalSigContainer); } } }

散列相同,如下所示。在保存之前和从文件读取之后,我放置了一些断点尝试捕获这些值。

保存前的字节数组:

[0] = {byte} 133 [1] = {byte} 170 [2] = {byte} 124 [3] = {byte} 73 [4] = {byte} 225 [5] = {byte} 104 [6] = {byte} 242 [7] = {byte} 79 [8] = {byte} 44 [9] = {byte} 52 [10] = {byte} 173 [11] = {byte} 6 [12] = {byte} 7 [13] = {byte} 250 [14] = {byte} 171 [15] = {byte} 50 [16] = {byte} 226 [17] = {byte} 132 [18] = {byte} 113 [19] = {byte} 31 [20] = {byte} 125 [21] = {byte} 174 [22] = {byte} 53 [23] = {byte} 98 [24] = {byte} 68 [25] = {byte} 117 [26] = {byte} 102 [27] = {byte} 191 [28] = {byte} 109 [29] = {byte} 180 [30] = {byte} 88 [31] = {byte} 133

从CreateFinalSignature中的.txt文件读取的字节数组:

[0] = {byte} 133 [1] = {byte} 170 [2] = {byte} 124 [3] = {byte} 73 [4] = {byte} 225 [5] = {byte} 104 [6] = {byte} 242 [7] = {byte} 79 [8] = {byte} 44 [9] = {byte} 52 [10] = {byte} 173 [11] = {byte} 6 [12] = {byte} 7 [13] = {byte} 250 [14] = {byte} 171 [15] = {byte} 50 [16] = {byte} 226 [17] = {byte} 132 [18] = {byte} 113 [19] = {byte} 31 [20] = {byte} 125 [21] = {byte} 174 [22] = {byte} 53 [23] = {byte} 98 [24] = {byte} 68 [25] = {byte} 117 [26] = {byte} 102 [27] = {byte} 191 [28] = {byte} 109 [29] = {byte} 180 [30] = {byte} 88 [31] = {byte} 133

目标是实现PDF签名过程,在该过程中,服务器(.NET Core服务)根据请求(Electron)向客户端提供要签名的哈希。然后,客户端使用...
c# pdf itext digital-signature
1个回答
0
投票
MyExternalEmptySignatureContainer.Sign中,您可以使用PDF范围流的裸哈希正确确定经过身份验证的属性:
© www.soinside.com 2019 - 2024. All rights reserved.