C# 如何在使用IText SignDeferred签署文档时保留PDF-A

C# 如何在使用IText SignDeferred签署文档时保留PDF-A,c#,.net,itext,pdf-generation,C#,.net,Itext,Pdf Generation,我确实使用IText通过延迟签名(SignDeferred)将签名应用于pdf文档。 该过程包含以下步骤: 准备用于签名的pdf文档 在pdf文档中为签名保留空间 创建pdf文档的哈希值 基于哈希值创建签名 使用自签名证书 将签名应用于pdf文档 整个过程都是有效的,我以一个pdf文档结束,其中签名已设置且有效 原始pdf为pdf-A1a,但生成的pdf不再是有效的pdf-A1a。 我知道有一个关于IText PDF-a support()的文档,但这似乎不适用,因为我没有更改

我确实使用IText通过延迟签名(SignDeferred)将签名应用于pdf文档。 该过程包含以下步骤:

  • 准备用于签名的pdf文档
    • 在pdf文档中为签名保留空间
  • 创建pdf文档的哈希值
  • 基于哈希值创建签名
    • 使用自签名证书
  • 将签名应用于pdf文档
整个过程都是有效的,我以一个pdf文档结束,其中签名已设置且有效

原始pdf为pdf-A1a,但生成的pdf不再是有效的pdf-A1a。 我知道有一个关于IText PDF-a support()的文档,但这似乎不适用,因为我没有更改文档的内容

我的问题: 如何使用延迟签名应用签名并将PDF-A1a保留在生成的文档中

注意:如果我直接应用签名(没有SignDeferred),生成的pdf仍然是pdf-A1a,但我必须使用SignDeferred 注意:我确实用于检查pdf-A

代码示例
  • 用于签名的组件:
    • itext.sign 7.1.5.0
    • itext.kernel 7.1.5.0
  • 用于创建哈希的组件
    • BouncyCastle.Crypto 1.8.1.0
下面是一个完整的代码示例,包含一个文件中所需的所有内容。 它只需要对itext和BouncyCastle的引用以及自签名证书的路径

using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.IO;

namespace DeferredSigningTestConsole
{
    class Program
    {
        static string SignatureAttributeName = "DeferredSignature";
        static string CertificatePath = @"C:\temp\PDFA\PdfATestCert.2pfx.pfx";
        static string CertificatePassword = "test";

        static void Main(string[] args)
        {
            var signedPdf = SignPdf(System.IO.File.ReadAllBytes(@"C:\temp\PDFA\PDF_A1a.pdf"));
            System.IO.File.WriteAllBytes(@"C:\temp\PDFA\signed.pdf", signedPdf);
        }

        public static byte[] SignPdf(byte[] pdfToSign)
        {
            byte[] hash = null;
            byte[] tmpPdf = null;
            //Step #1 >> prepare pdf for signing (Allocate space for the signature and calculate hash)
            using (MemoryStream input = new MemoryStream(pdfToSign))
            {
                using (var reader = new PdfReader(input))
                {
                    StampingProperties sp = new StampingProperties();
                    sp.UseAppendMode();
                    using (MemoryStream baos = new MemoryStream())
                    {
                        var signer = new PdfSigner(reader, baos, sp);
                        signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);

                        signer.SetFieldName(SignatureAttributeName);
                        DigestCalcBlankSigner external = new DigestCalcBlankSigner(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);

                        signer.SignExternalContainer(external, 121743);
                        hash = external.GetDocBytesHash();
                        tmpPdf = baos.ToArray();
                    }
                }

                //Step #2 >> Create the signature based on the document hash
                byte[] signature = GetSignatureFromHash(hash);

                //Step #3 >> Apply the signature to the document
                ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signature);
                using (MemoryStream preparedPdfStream = new MemoryStream(tmpPdf))
                {
                    using (var pdfReader = new PdfReader(preparedPdfStream))
                    {
                        using (PdfDocument docToSign = new PdfDocument(pdfReader))
                        {
                            using (MemoryStream outStream = new MemoryStream())
                            {
                                PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
                                return outStream.ToArray();
                            }
                        }
                    }
                }

            }
        }

        public static byte[] GetSignatureFromHash(byte[] hash)
        {
            FileStream fs = new FileStream(CertificatePath, FileMode.Open);
            Pkcs12Store store = new Pkcs12Store(fs, CertificatePassword.ToCharArray());
            String alias = "";
            foreach (string al in store.Aliases)
                if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
                {
                    alias = al;
                    break;
                }
            AsymmetricKeyEntry pk = store.GetKey(alias);
            X509CertificateEntry[] chain = store.GetCertificateChain(alias);

            List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
            foreach (X509CertificateEntry en in chain)
            {
                c.Add(en.Certificate);
            }
            PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA256");
            String hashAlgorithm = signature.GetHashAlgorithm();
            PdfPKCS7 sgn = new PdfPKCS7(null, c.ToArray(), hashAlgorithm, false);
            DateTime signingTime = DateTime.Now;
            byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, null, null, PdfSigner.CryptoStandard.CMS);
            byte[] extSignature = signature.Sign(sh);
            sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
            return sgn.GetEncodedPKCS7(hash, null, null, null, PdfSigner.CryptoStandard.CMS);

        }
    }

    internal class DigestCalcBlankSigner : IExternalSignatureContainer
    {
        private readonly PdfName _filter;

        private readonly PdfName _subFilter;

        private byte[] _docBytesHash;

        internal DigestCalcBlankSigner(PdfName filter, PdfName subFilter)
        {
            _filter = filter;
            _subFilter = subFilter;
        }

        internal virtual byte[] GetDocBytesHash()
        {
            return _docBytesHash;
        }

        public virtual byte[] Sign(Stream docBytes)
        {

            _docBytesHash = CalcDocBytesHash(docBytes);
            //If we retun the signature bytes, GetAuthenticatedAttributeBytes will throw an exception
            //Not clear how this should be done
            return new byte[0];
        }

        public virtual void ModifySigningDictionary(PdfDictionary signDic)
        {
            signDic.Put(PdfName.Filter, _filter);
            signDic.Put(PdfName.SubFilter, _subFilter);
        }

        internal static byte[] CalcDocBytesHash(Stream docBytes)
        {
            byte[] docBytesHash = null;
            docBytesHash = DigestAlgorithms.Digest(docBytes, DigestUtilities.GetDigest(DigestAlgorithms.SHA256));
            return docBytesHash;
        }
    }


    internal class ReadySignatureSigner : IExternalSignatureContainer
    {
        private byte[] cmsSignatureContents;

        internal ReadySignatureSigner(byte[] cmsSignatureContents)
        {
            this.cmsSignatureContents = cmsSignatureContents;
        }

        public virtual byte[] Sign(Stream docBytes)
        {
            return cmsSignatureContents;
        }

        public virtual void ModifySigningDictionary(PdfDictionary signDic)
        {
        }
    }
}

使用iText.Kernel.Pdf;
使用iText.Signatures;
使用Org.BouncyCastle.Pkcs;
使用Org.BouncyCastle.Security;
使用制度;
使用System.Collections.Generic;
使用System.IO;
命名空间延迟签名测试控制台
{
班级计划
{
静态字符串SignatureAttributeName=“DeferredSignature”;
静态字符串CertificatePath=@“C:\temp\PDFA\PdfATestCert.2pfx.pfx”;
静态字符串证书password=“test”;
静态void Main(字符串[]参数)
{
var signedPdf=SignPdf(System.IO.File.ReadAllBytes(@“C:\temp\PDFA\PDF_A1a.PDF”);
System.IO.File.writealBytes(@“C:\temp\PDFA\signed.pdf”,signedPdf);
}
公共静态字节[]SignPdf(字节[]pdfToSign)
{
字节[]散列=null;
字节[]tmpPdf=null;
//步骤#1>>准备用于签名的pdf(为签名分配空间并计算哈希)
使用(MemoryStream输入=新的MemoryStream(pdfToSign))
{
使用(变量读取器=新PdfReader(输入))
{
冲压属性sp=新冲压属性();
sp.UseAppendMode();
使用(MemoryStream bas=new MemoryStream())
{
var签名者=新的PdfSigner(读卡器、BAS、sp);
签名人设置认证级别(PdfSigner.未认证);
signer.SetFieldName(SignatureAttributeName);
DigestCalcLanksigner external=新的DigestCalcLanksigner(PdfName.Adobe_PPKLite,PdfName.Adbe_pkcs7_);
signer.SignExternalContainer(外部,121743);
hash=external.GetDocBytesHash();
tmpPdf=baos.ToArray();
}
}
//步骤2>基于文档哈希创建签名
字节[]签名=GetSignatureFromHash(哈希);
//步骤#3>>将签名应用于文档
ReadySignatureSigner extSigContainer=新的ReadySignatureSigner(签名);
使用(MemoryStream preparedPdfStream=新的MemoryStream(tmpPdf))
{
使用(var pdfReader=新pdfReader(preparedPdfStream))
{
使用(PdfDocument docToSign=新PdfDocument(pdfReader))
{
使用(MemoryStream outStream=新MemoryStream())
{
PdfSigner.SignDeferred(docToSign、SignatureAttributeName、outStream、extSigContainer);
返回外流ToArray();
}
}
}
}
}
}
公共静态字节[]GetSignatureFromHash(字节[]哈希)
{
FileStream fs=newfilestream(CertificatePath,FileMode.Open);
Pkcs12Store=新的Pkcs12Store(fs,CertificatePassword.ToCharArray());
字符串别名=”;
foreach(存储中的字符串al.alias)
if(store.IsKeyEntry(al)和&store.GetKey(al.Key.IsPrivate)
{
别名=al;
打破
}
AsymmetricKeyEntry pk=store.GetKey(别名);
X509CertificateEntry[]chain=store.GetCertificateChain(别名);
列表c=新列表();
foreach(X509CertificateEntry-en链中)
{
c、 添加(英语证书);
}
PrivateKeySignature签名=新的PrivateKeySignature(pk.Key,“SHA256”);
String hashAlgorithm=signature.GetHashAlgorithm();
PdfPKCS7 sgn=新的PdfPKCS7(null,c.ToArray(),hashAlgorithm,false);
DateTime signingTime=DateTime.Now;
字节[]sh=sgn.GetAuthenticatedAttributeBytes(散列,null,null,PdfSigner.CryptoStandard.CMS);
字节[]extSignature=signature.Sign(sh);
sgn.SetExternalDigest(extSignature,null,signature.GetEncryptionAlgorithm());
返回sgn.GetEncodedPKCS7(散列,null,null,null,PdfSigner.CryptoStandard.CMS);
}
}
内部类DigestCalcBlankSigner:IExternalSignatureContainer
{
私有的
//doesn't work
signer.SignExternalContainer(external, 121743);

//does work
signer.SignExternalContainer(external, 65000);