我确实使用IText通过延迟签名(SignDeferred)将签名应用于pdf文档。该过程包含以下步骤:
整个过程工作,我以一个pdf文档结束,其中签名被设置并有效。
原始pdf是pdf-A1a,但生成的pdf不再是有效的pdf-A1a。我知道有一个关于IText PDF-a支持的文档(https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java/chapter-7-creating-pdf-ua-and-pdf-a-documents),但这似乎不适用,因为我没有更改文档的内容。
我的问题:如何使用延迟签名应用签名,并在生成的文档中保留PDF-A1a?
注意:如果我直接应用签名(不使用SignDeferred),生成的pdf仍然是pdf-A1a,但我必须使用SignDeferred注意:我确实使用https://www.pdfen.com/pdf-a-validator用于检查pdf-A
以下是一个完整的代码示例示例,其中包含一个文件中所需的所有内容。它只需要引用它的文本和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)
{
}
}
}
签名pdf不再是有效pdf-A1a的原因似乎是签名的估计大小。我使用了大约120kb的值作为签名。
//doesn't work
signer.SignExternalContainer(external, 121743);
//does work
signer.SignExternalContainer(external, 65000);
iText的电子书《PDF文档的数字签名》中记录了这一概念。
似乎为了获得有效的pdf A1a,最大大小限制为65kb。
我现在必须在添加视觉表示(签名图像)时测试这是否有效,因为这就是我选择如此大尺寸的原因。
编辑:我做了更多的测试,现在我能够生成带有签名的有效pdf-A文档:pdf现在是有效的pdf-A,估计大小已更改:
添加视觉表示(图像)时,pdf-A1a和pdf-A1b不再有效。
有一个透明的软遮罩。从PDF 1.4开始,支持透明度。一些基于PDF的ISO标准禁止使用透明度。
但这是我现在试图解决的另一个问题。
我能够使用PDFBox1.8.5对PDF文档进行数字签名,多亏了PDFBOX中提供的这个出色的示例。 https://github.com/apache/pdfbox/blob/1.8/examples/src/main/java/org/apache/pdfbox/examples/signature/createSignature.java 签名此示例时,请使用本地计算机的日期/时间(第175
我刚刚创建了DocuSign开发人员帐户。我必须使用签名对PDF进行签名,并用C#将其发送给收件人。我不希望收件人使用https://developers.docusign.com/esign-rest-api/code-examples/code-example-request-a-signature-via-email。我的系统中已经有PDF可用,我需要使用DocuSign API签名并将其发
由于文件API不适用于移动平台,因此无论如何我们都可以使用带有UPE框架的标签来保护文档。
问题内容: 在我的应用程序中,我更改了XML文件的某些部分,其开始如下: 注意之前的空白行。加载,更改和保存后,结果远非令人满意: 我发现注释和文档节点之间的空白(一个换行符)根本没有在DOM中表示。下面的独立代码可靠地重现了此问题: 有谁知道如何避免这种情况?本质上,我希望输出与输入相同。(我知道将重新生成xml声明,因为它不是DOM的一部分,但这在这里不是问题。) 问题答案: 根本原因是标准D
我有一个代码是创建一个“封面”,然后将其与现有的pdf文件合并。合并后,pdf标签丢失。如何保留现有pdf的pdf标签,然后将页面标签添加到从头开始创建的pdf页面(例如“封面”)?我认为这本书的例子是关于检索和替换页面标签的。我不知道如何将一个现有的pdf和一个从头创建的pdf连接起来。我用的是itext 5.3.0。提前感谢。 根据mkl的评论进行编辑 更新 根据mkl的回答,我修改了上面的代
问题内容: 我已经开始使用推荐的广告,并从移走了。我无法重新粘合在一起的一件事是使用持久性Cookie存储。我想简单地将自定义cookie处理程序/管理器附加到我的连接中以存储cookie。Android文档并不是很有帮助,因为它将Cookie主题分为两行。 我以前使用过LoopJ ,效果很好。 关于如何在Android中设置可以附加到我的自动保存和检索cookie 的持久性cookie存储的想法