iText 8.0.X版本.NET环境PDF数字签名代码适配问题求助
适配iText8的.NET PDF数字签名代码修正方案
问题背景
我们过去数年一直使用iText7 7.X的.NET版本进行PDF数字签名,方案运行良好且目前仍可正常使用。但iText推出版本8后,现有代码与其不兼容,且找不到适配iText8的PDF签名示例,出现了两个编译错误。
原代码
public class PdfSigner { IExternalSignature _privateSignature; X509Certificate[] _signChain; public PdfSigner(Stream privateKeyStream, string keyPassword) { var pks = new Pkcs12Store(privateKeyStream, keyPassword.ToCharArray()); string alias = null; foreach (string tAlias in pks.Aliases) { if (pks.IsKeyEntry(tAlias)) { alias = tAlias; break; } } var pk = pks.GetKey(alias).Key; var ce = pks.GetCertificateChain(alias); _signChain = new X509Certificate[ce.Length]; for (int k = 0; k < ce.Length; ++k) _signChain[k] = ce[k].Certificate; _privateSignature = new PrivateKeySignature(pk, "SHA-512"); } public void SignPDF(Stream input, Stream output, PDFSignParameters p) { PdfReader reader = new PdfReader(input); StampingProperties properties = new StampingProperties(); var signer = new iText.Signatures.PdfSigner(reader, output, properties); PdfSignatureAppearance sap = signer.GetSignatureAppearance().SetReason(p.Reason).SetLocation(p.Location); if (p.Image != null) { var img = iText.IO.Image.ImageDataFactory.Create(new BinaryReader(p.Image.Data).ReadBytes((int)p.Image.Data.Length)); sap.SetSignatureGraphic(img); sap.SetLayer2Text(string.Empty); sap.SetPageRect(new iText.Kernel.Geom.Rectangle(p.Image.X, p.Image.Y, img.GetWidth() / p.Image.WidthRatio, img.GetHeight() / p.Image.HeigthRatio)); sap.SetImage(img); } signer.SignDetached(_privateSignature, _signChain, null, null, null, 0, iText.Signatures.PdfSigner.CryptoStandard.CMS); } }
编译错误
错误1:构造函数最后一行
_privateSignature = new PrivateKeySignature(pk, "SHA-512");cannot convert from 'Org.BouncyCastle.Crypto.AsymmetricKeyParameter' to 'iText.Commons.Bouncycastle.Crypto.IPrivateKey'
错误2:SignPDF方法最后一行
signer.SignDetached(_privateSignature, _signChain, null, null, null, 0, iText.Signatures.PdfSigner.CryptoStandard.CMS);cannot convert from 'Org.BouncyCastle.X509.X509Certificate[]' to 'iText.Commons.Bouncycastle.Cert.IX509Certificate[]'
解决方案
iText8核心变化是引入了BouncyCastle抽象层,不再直接依赖Org.BouncyCastle的原生类型,需要通过iText提供的适配器类转换类型。以下是修正后的完整代码:
using iText.Commons.Bouncycastle.Cert; using iText.Commons.Bouncycastle.Crypto; using iText.Signatures; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.X509; using iText.Commons.Bouncycastle; public class PdfSigner { IExternalSignature _privateSignature; IX509Certificate[] _signChain; public PdfSigner(Stream privateKeyStream, string keyPassword) { var pks = new Pkcs12Store(privateKeyStream, keyPassword.ToCharArray()); string alias = null; foreach (string tAlias in pks.Aliases) { if (pks.IsKeyEntry(tAlias)) { alias = tAlias; break; } } var pk = pks.GetKey(alias).Key; var ce = pks.GetCertificateChain(alias); // 转换证书链为iText抽象层类型 _signChain = new IX509Certificate[ce.Length]; for (int k = 0; k < ce.Length; ++k) { _signChain[k] = BouncyCastleFactoryCreator.GetFactory().CreateX509Certificate(ce[k].Certificate); } // 转换私钥为iText抽象层类型并创建签名实例 var bcPrivateKey = BouncyCastleFactoryCreator.GetFactory().CreatePrivateKey(pk); _privateSignature = new PrivateKeySignature(bcPrivateKey, "SHA-512"); } public void SignPDF(Stream input, Stream output, PDFSignParameters p) { PdfReader reader = new PdfReader(input); StampingProperties properties = new StampingProperties(); var signer = new PdfSigner(reader, output, properties); PdfSignatureAppearance sap = signer.GetSignatureAppearance().SetReason(p.Reason).SetLocation(p.Location); if (p.Image != null) { var img = iText.IO.Image.ImageDataFactory.Create(new BinaryReader(p.Image.Data).ReadBytes((int)p.Image.Data.Length)); sap.SetSignatureGraphic(img); sap.SetLayer2Text(string.Empty); sap.SetPageRect(new iText.Kernel.Geom.Rectangle(p.Image.X, p.Image.Y, img.GetWidth() / p.Image.WidthRatio, img.GetHeight() / p.Image.HeigthRatio)); sap.SetImage(img); } // 使用转换后的抽象层类型调用SignDetached signer.SignDetached(_privateSignature, _signChain, null, null, null, 0, PdfSigner.CryptoStandard.CMS); } }
关键修正点
- 类型转换工具类:使用
BouncyCastleFactoryCreator.GetFactory()获取iText的BouncyCastle抽象工厂,通过它将原生BouncyCastle类型转换为iText对应的抽象接口:- 私钥:
CreatePrivateKey()将Org.BouncyCastle.Crypto.AsymmetricKeyParameter转为IPrivateKey - 证书:
CreateX509Certificate()将Org.BouncyCastle.X509.X509Certificate转为IX509Certificate
- 私钥:
- 字段类型更新:把
X509Certificate[] _signChain改为IX509Certificate[] _signChain,匹配iText8的参数要求 - 命名空间简化:
iText.Signatures.PdfSigner可以直接简化为PdfSigner(已引入对应命名空间)
内容的提问来源于stack exchange,提问作者Hagay Goshen
相关产品推荐
相关产品推荐

