如何解决如何使用带有 PKCS1 签名的 iText 签署 pdf
我需要使用将返回 PKCS1 签名的外部服务签署 pdf 文档。这意味着我必须在 IExternalSignatureContainer 实例中添加公钥。我在整个签名过程中使用 iText 7。
上有一个很好的例子来说明这种方法我确实实现了这个示例,但我没有使用服务来签署哈希值,而是使用 X509 证书来完成。问题是签名在某种程度上无效(它说签名后文档被更改了)。证书似乎是正确集成的。
我在一个文件中创建了整个歌唱过程的示例。 所需参考资料:
- BouncyCastle.Crypto
- itext.kernel
- itext.sign
可以在此处下载(自签名)公钥、私钥和已签名 pdf 文档的示例:http://www.filedropper.com/pdfsignfiles
知道我在这里做错了什么吗?
示例代码(在一个文件中为控制台应用程序准备了两个类):
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.X509;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
namespace SignExternalTestManuel
{
class Program
{
public static void Main(String[] args)
{
string filePath = @"c:\temp\pdfsign\";
string pdfToSign = Path.Combine(filePath,@"test.pdf");
string destinationFile = Path.Combine(filePath,"test_signed.pdf");
string userCertificatePublicKey = Path.Combine(filePath,"BITSignTestManuel5Base64.cer");
string caCertificatePublicKey = Path.Combine(filePath,"BITRoot5Base64.cer");
string privateKeyFile = Path.Combine(filePath,"BITSignTestManuel5.pfx");
string privateKeyPassword = "test";
PdfReader reader = new PdfReader(pdfToSign);
using (FileStream os = new FileStream(destinationFile,FileMode.OpenOrCreate))
{
StampingProperties stampingProperties = new StampingProperties();
//For any signature in the Pdf but the first one,you need to use appendMode
// stampingProperties.useAppendMode();
stampingProperties.UseAppendMode();
PdfSigner pdfSigner = new PdfSigner(reader,os,stampingProperties);
pdfSigner.SetCertificationLevel(PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED);
IExternalSignatureContainer external = new GsSignatureContainer(
PdfName.Adobe_PPKLite,PdfName.Adbe_pkcs7_detached,userCertificatePublicKey,caCertificatePublicKey,privateKeyFile,privateKeyPassword);
pdfSigner.SignExternalContainer(external,32000);
}
}
}
public class GsSignatureContainer : IExternalSignatureContainer
{
private PdfDictionary sigDic;
private string userCertificatePublicKey;
private string caCertificatePublicKey;
private string privateKeyFile;
private string privateKeyPassword;
public GsSignatureContainer(PdfName filter,PdfName subFilter,string userCertificatePublicKey,string caCertificatePublicKey,string privateKeyFile,string privateKeyPassword)
{
sigDic = new PdfDictionary();
sigDic.Put(PdfName.Filter,filter);
sigDic.Put(PdfName.SubFilter,subFilter);
this.userCertificatePublicKey = userCertificatePublicKey;
this.caCertificatePublicKey = caCertificatePublicKey;
this.privateKeyFile = privateKeyFile;
this.privateKeyPassword = privateKeyPassword;
}
/// <summary>
/// Implementation based on https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7#HowtouseaDigitalSigningService(DSS)suchasGlobalSign,withiText7-Examplecode
/// </summary>
/// <param name="pdfStream"></param>
/// <returns></returns>
public byte[] Sign(Stream pdfStream)
{
//Create the certificate chaing since the signature is just a PKCS1,the certificates must be added to the signature
string cert = System.IO.File.ReadAllText(userCertificatePublicKey);
string ca = System.IO.File.ReadAllText(caCertificatePublicKey);
X509Certificate[] chain = CreateChain(cert,ca);
String hashAlgorithm = DigestAlgorithms.SHA256;
PdfPKCS7 pkcs7Signature = new PdfPKCS7(null,chain,hashAlgorithm,false);
//Create the hash of of the pdf document
byte[] hash = DigestAlgorithms.Digest(pdfStream,DigestAlgorithms.GetMessageDigest(hashAlgorithm));
//TODO: Unclear whether i need to use this. It is from the example,but maybe only required for
//byte[] sh = pkcs7Signature.GetAuthenticatedAttributeBytes(hash,null,PdfSigner.CryptoStandard.CMS);
//using (SHA256 sha256 = SHA256.Create())
//{
// sh = sha256.ComputeHash(hash);
//}
//Create the actual signautre (This will be done via Service)
byte[] signature = CreateSignature(hash,privateKeyPassword);
pkcs7Signature.SetExternalDigest(signature,"RSA");
return pkcs7Signature.GetEncodedPKCS7(hash,PdfSigner.CryptoStandard.CMS);
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.PutAll(sigDic);
}
private static X509Certificate[] CreateChain(String cert,String ca)
{
//Note: The root certificate could be omitted and it would still work
X509Certificate[] chainy = new X509Certificate[2];
X509CertificateParser parser = new X509CertificateParser();
chainy[0] = new X509Certificate(parser.ReadCertificate(Encoding.UTF8.GetBytes(cert))
.CertificateStructure);
chainy[1] = new X509Certificate(parser.ReadCertificate(Encoding.UTF8.GetBytes(ca))
.CertificateStructure);
return chainy;
}
#region "Create signature,will be done by an actual service"
private byte[] CreateSignature(byte[] hash,string privateKeyPassword)
{
X509Certificate2 rootCertificateWithPrivateKey = new X509Certificate2();
byte[] rawData = System.IO.File.ReadAllBytes(privateKeyFile);
rootCertificateWithPrivateKey.Import(rawData,privateKeyPassword,X509KeyStorageFlags.Exportable);
using (var key = rootCertificateWithPrivateKey.GetRSAPrivateKey())
{
return key.SignData(hash,HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);
}
}
#endregion
}
}
解决方法
我想我在我的代码中发现了问题。 我直接对哈希值进行签名,而不使用 GetAuthenticatedAttributeBytes。一旦我添加以下代码,然后签名 sh 而不是哈希,签名就会生效:
//Create the hash based on the document hash which is suitable for pdf siging
X509Certificate2 rootCertificateWithoutPrivateKey = new X509Certificate2();
rootCertificateWithoutPrivateKey.Import(caCertificatePublicKey);
X509Certificate rootCertBouncy = DotNetUtilities.FromX509Certificate(rootCertificateWithoutPrivateKey);
List<X509Certificate> c = new List<X509Certificate>();
c.Add(rootCertBouncy);
PdfPKCS7 sgn = new PdfPKCS7(null,c.ToArray(),hashAlgorithm,false);
byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash,null,PdfSigner.CryptoStandard.CMS);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。