如何使用 iText7 将 .p7s 字节数组插入到 PDF 中?
How to insert .p7s byte array into PDF with iText7?
我正在尝试将 .p7s 字节数组信息插入签名字段,我按照下面的图片操作:
我的步数:
准备签名容器
原始PDF为"tmp/example.pdf",这部分输出为"results/prepared.pdf"
PdfSigner signer = new PdfSigner(new PdfReader("tmp/example.pdf"), new FileStream("results/prepared.pdf", FileMode.Create), new StampingProperties().UseAppendMode());
signer.SetFieldName("Signature1");
PdfDocument _pdfDocument = new PdfDocument(new PdfReader("tmp/example.pdf"));
PdfSignatureAppearance sigAppearance = signer.GetSignatureAppearance();
sigAppearance
.SetPageRect(new Rectangle(144, 144, 200, 100))
.SetPageNumber(1)
.SetContact("This is contact")
.SetReason("This is reason")
.SetLocation("This is location")
.SetSignatureCreator("This is signature creator");
MyExternalSignatureContainer _container = new MyExternalSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached, _chain);
IExternalSignatureContainer container = _container;
signer.SignExternalContainer(container, 8192);
byte[] _sb = _container.Signed_Bytes;
我的外部签名容器Class
public byte[] Sign(Stream data)
{
iText.Signatures.PdfPKCS7 _sgn = new iText.Signatures.PdfPKCS7(null, chain, "SHA256", false);
byte[] _hash = iText.Signatures.DigestAlgorithms.Digest(data, "SHA256");
byte[] _sh = _sgn.GetAuthenticatedAttributeBytes(_hash, PdfSigner.CryptoStandard.CMS,
null, null);
Signed_Bytes = _sh;
return new byte[0]; ;
}
直到这一部分,一切顺利,我得到了"results/prepared.pdf"
散列并发送到外部签名服务并获得 .p7s
现在我想根据上图将.p7s字节[]插入到PDF结构的签名值部分。
我尝试使用下面的代码获取 PdfDictionay
的 "results/prepared.pdf" 的 ByteRange
信息,我希望将 .p7s 注入到 "results/prepared.pdf" 的签名容器中"results/injected.pdf"
PdfDocument pdfDocument = new PdfDocument(new PdfReader("results/prepared.pdf"), new PdfWriter("results/injected.pdf").SetSmartMode(true));
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
PdfSignature signature = signatureUtil.GetSignature("Signature1");
PdfDictionary _pd = signatureUtil.GetSignatureDictionary("Signature1");
现在我得到了“_pd”的结果如下:
{<</ByteRange [0 107457 123843 2688 ] /ContactInfo This is contact /Contents
我的理解(如有错误请指正)是我应该把.p7s字节数组放在107457作为起始位置。
尝试将 .p7s 注入现有签名容器
我尝试在下面创建 paddedSig
数组并将 .p7s 复制到它:
byte[] _p7s = System.IO.File.ReadAllBytes("tmp/example.p7s");
byte[] paddedSig = new byte[8192];
System.Array.Copy(_p7s, 0, paddedSig, 0, _p7s.Length);
然后尝试将 paddedSig 放入 PdfDictionary,如下所示:
PdfDictionary _pd = signatureUtil.GetSignatureDictionary("Signature1");
pd.Put(PdfName.Contents, new PdfString(paddedSig).SetHexWriting(true));
pdfDocument.Close();
生成了一个名为 "results/injected.pdf" 的新 PDF,但是:
Signature contains incorrect, unrecognized, corrupted or suspicious data.
Support Information: SigDict /Contents illegal data
我错过了什么? ..如何将.p7s注入准备好的签名容器?
响应Mkl的post:
我不明白的是如何将返回的 PKCS#7 字节嵌入到 pdf 中..
假设 byte[] _p7s 是 API_CALL
的结果
byte[] _p7s = API_CALL;
byte[] paddedSig = new byte[8192];
System.Array.Copy(_p7s, 0, paddedSig, 0, _p7s.Length);
然后尝试将 paddedSig 放入 PdfDictionary,如下所示:
PdfDictionary _pd = signatureUtil.GetSignatureDictionary("Signature1");
pd.Put(PdfName.Contents, new PdfString(paddedSig).SetHexWriting(true));
pdfDocument.Close();
结果是:
签名包含不正确、无法识别、损坏或可疑的数据。
支持信息:SigDict /Contents 非法数据
尝试使用 .p7s 文件
我有一个 example.p7s 文件,我尝试使用下面提供的代码嵌入:
byte[] _p7s = System.IO.File.ReadAllBytes("tmp/example.p7s");
private static void Embed_P7S(Org.BouncyCastle.X509.X509Certificate[] _chain, byte[] _p7s)
{
PdfDocument document = new PdfDocument(new PdfReader("results/example-prepared.pdf"));
Stream output = new FileStream("results/example-prepared-signed.pdf", FileMode.Create);
ExternalInjectingSignatureContainer container2 = new ExternalInjectingSignatureContainer(_p7s);
PdfSigner.SignDeferred(document, "Signature1", output, container2);
}
}
internal class ExternalInjectingSignatureContainer :IExternalSignatureContainer
{
public ExternalInjectingSignatureContainer(byte[] signature)
{
Signature = signature;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
public byte[] Sign(Stream data)
{
return Signature;
}
public byte[] Signature;
}
结果:
example-prepared-signed.pdf 未显示证书(这类似于原始 pdf)
但是尺寸比原来的pdf大
original.pdf 是 105KB
示例准备的是 124KB
example-prepared-signed 是 121KB
首先,您不必像现在这样拆分签名过程。我已经看到很多开发人员想要这样做的问题,但严格来说没有必要(嗯,在引擎盖下 iText 仍然会首先创建一个准备好的 PDF,然后注入签名容器,但它可以保留在引擎盖下)。
仅当外部签名服务需要很长时间来创建签名并且您无法在这段时间将 PDF 保存在内存中时,才需要拆分流程。
我将在此处研究这两种变体。
单通签名
如果您的外部签名服务 return 足够快地生成结果(一个完整的 PKCS#7 签名容器),您应该使用这种方法。基本代码的开头与您的相似:
PdfSigner signer = new PdfSigner(new PdfReader("example.pdf"), new FileStream("example-signed.pdf", FileMode.Create), new StampingProperties().UseAppendMode());
signer.SetFieldName("Signature1");
PdfSignatureAppearance sigAppearance = signer.GetSignatureAppearance();
sigAppearance
.SetPageRect(new Rectangle(144, 144, 200, 100))
.SetPageNumber(1)
.SetContact("This is contact")
.SetReason("This is reason")
.SetLocation("This is location")
.SetSignatureCreator("This is signature creator");
ExternalServiceSignatureContainer container = new ExternalServiceSignatureContainer();
signer.SignExternalContainer(container, 8192);
您的代码的区别在于 IExternalSignatureContainer
实现:
public class ExternalServiceSignatureContainer : IExternalSignatureContainer
{
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
public byte[] Sign(Stream data)
{
// Call your external signing service to create a CMS signature container
// for the data in the InputStream and return that signature container
[... see below ...]
}
}
根据您的 API 访问外部签名服务,Sign
的实现会有所不同。在每种情况下,我都假设 API_CALL 到 return 结果 PKCS#7 签名容器作为字节数组:
您可以直接通过流调用它
return YOUR_SIGNING_API_CALL_FOR_STREAM(data);
或使用从流内容
生成的字节[]
return YOUR_SIGNING_API_CALL_FOR_ARRAY(StreamUtil.InputStreamToArray(data));
作为参数,
或者您可能首先必须自己对数据进行哈希处理(例如如下)并将您的哈希发送到服务。
byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);
return YOUR_SIGNING_API_CALL_FOR_HASH(hash)
signer
的输出已经是最终确定的签名 PDF。
这基本上就是 this answer 中已经讨论过的情况。
二传签
如果您的外部签名服务return结果(完整的 PKCS#7 签名容器)不够快(例如,在批量签名的情况下 APIs 或服务等待如有必要,需要长时间确认),或者如果您在调用签名服务之前在单独的程序中实现了该部分,之后又在单独的程序中实现了该部分(有些人确实这样做了),则可以使用这种方法。
基本代码的开头与您的相似:
PdfSigner signer = new PdfSigner(new PdfReader("example.pdf"), new FileStream("example-prepared.pdf", FileMode.Create), new StampingProperties().UseAppendMode());
signer.SetFieldName("Signature1");
PdfSignatureAppearance sigAppearance = signer.GetSignatureAppearance();
sigAppearance
.SetPageRect(new Rectangle(144, 144, 200, 100))
.SetPageNumber(1)
.SetContact("This is contact")
.SetReason("This is reason")
.SetLocation("This is location")
.SetSignatureCreator("This is signature creator");
ExternalEmptySignatureContainer container = new ExternalEmptySignatureContainer();
signer.SignExternalContainer(container, 8192);
byte[] dataToSign = container.Data;
ExternalEmptySignatureContainer
现在只提供签名服务稍后签名的数据,还没有注入签名容器
public class ExternalEmptySignatureContainer : IExternalSignatureContainer
{
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
public byte[] Sign(Stream data)
{
// Store the data to sign and return an empty array
[... see below ...]
return new byte[0];
}
public byte[] Data;
}
根据您的 API 访问外部签名服务,Sign
的实现会有所不同。
如果您的签名API需要原始数据进行签名,请使用从流内容
生成的字节[]
Data = StreamUtil.InputStreamToArray(data);
如果您的签名 API 需要原始数据的哈希值进行签名,请从流内容中这样计算它
Data = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);
signer
的输出是中介,准备好的PDF。
下一步是调用签名服务并检索 PKCS#7 签名容器:
byte[] signature = YOUR_SIGNING_API_CALL(dataToSign);
最后,您将该签名容器注入到准备好的 PDF 中:
PdfDocument document = new PdfDocument(new PdfReader("example-prepared.pdf"));
using (Stream output = new FileStream("example-prepared-signed.pdf", FileMode.Create))
{
ExternalInjectingSignatureContainer container2 = new ExternalInjectingSignatureContainer(signature);
PdfSigner.SignDeferred(document, "Signature1", output, container2);
}
IExternalSignatureContainer
实现仅注入签名字节:
public class ExternalInjectingSignatureContainer : IExternalSignatureContainer
{
public ExternalInjectingSignatureContainer(byte[] signature)
{
Signature = signature;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
public byte[] Sign(Stream data)
{
return Signature;
}
public byte[] Signature;
}
输出是最终确定的签名 PDF。
我正在尝试将 .p7s 字节数组信息插入签名字段,我按照下面的图片操作:
我的步数:
准备签名容器
原始PDF为"tmp/example.pdf",这部分输出为"results/prepared.pdf"
PdfSigner signer = new PdfSigner(new PdfReader("tmp/example.pdf"), new FileStream("results/prepared.pdf", FileMode.Create), new StampingProperties().UseAppendMode());
signer.SetFieldName("Signature1");
PdfDocument _pdfDocument = new PdfDocument(new PdfReader("tmp/example.pdf"));
PdfSignatureAppearance sigAppearance = signer.GetSignatureAppearance();
sigAppearance
.SetPageRect(new Rectangle(144, 144, 200, 100))
.SetPageNumber(1)
.SetContact("This is contact")
.SetReason("This is reason")
.SetLocation("This is location")
.SetSignatureCreator("This is signature creator");
MyExternalSignatureContainer _container = new MyExternalSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached, _chain);
IExternalSignatureContainer container = _container;
signer.SignExternalContainer(container, 8192);
byte[] _sb = _container.Signed_Bytes;
我的外部签名容器Class
public byte[] Sign(Stream data)
{
iText.Signatures.PdfPKCS7 _sgn = new iText.Signatures.PdfPKCS7(null, chain, "SHA256", false);
byte[] _hash = iText.Signatures.DigestAlgorithms.Digest(data, "SHA256");
byte[] _sh = _sgn.GetAuthenticatedAttributeBytes(_hash, PdfSigner.CryptoStandard.CMS,
null, null);
Signed_Bytes = _sh;
return new byte[0]; ;
}
直到这一部分,一切顺利,我得到了"results/prepared.pdf" 散列并发送到外部签名服务并获得 .p7s
现在我想根据上图将.p7s字节[]插入到PDF结构的签名值部分。
我尝试使用下面的代码获取 PdfDictionay
的 "results/prepared.pdf" 的 ByteRange
信息,我希望将 .p7s 注入到 "results/prepared.pdf" 的签名容器中"results/injected.pdf"
PdfDocument pdfDocument = new PdfDocument(new PdfReader("results/prepared.pdf"), new PdfWriter("results/injected.pdf").SetSmartMode(true));
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
PdfSignature signature = signatureUtil.GetSignature("Signature1");
PdfDictionary _pd = signatureUtil.GetSignatureDictionary("Signature1");
现在我得到了“_pd”的结果如下:
{<</ByteRange [0 107457 123843 2688 ] /ContactInfo This is contact /Contents
我的理解(如有错误请指正)是我应该把.p7s字节数组放在107457作为起始位置。
尝试将 .p7s 注入现有签名容器
我尝试在下面创建 paddedSig
数组并将 .p7s 复制到它:
byte[] _p7s = System.IO.File.ReadAllBytes("tmp/example.p7s");
byte[] paddedSig = new byte[8192];
System.Array.Copy(_p7s, 0, paddedSig, 0, _p7s.Length);
然后尝试将 paddedSig 放入 PdfDictionary,如下所示:
PdfDictionary _pd = signatureUtil.GetSignatureDictionary("Signature1");
pd.Put(PdfName.Contents, new PdfString(paddedSig).SetHexWriting(true));
pdfDocument.Close();
生成了一个名为 "results/injected.pdf" 的新 PDF,但是:
Signature contains incorrect, unrecognized, corrupted or suspicious data. Support Information: SigDict /Contents illegal data
我错过了什么? ..如何将.p7s注入准备好的签名容器?
响应Mkl的post:
我不明白的是如何将返回的 PKCS#7 字节嵌入到 pdf 中.. 假设 byte[] _p7s 是 API_CALL
的结果byte[] _p7s = API_CALL;
byte[] paddedSig = new byte[8192];
System.Array.Copy(_p7s, 0, paddedSig, 0, _p7s.Length);
然后尝试将 paddedSig 放入 PdfDictionary,如下所示:
PdfDictionary _pd = signatureUtil.GetSignatureDictionary("Signature1");
pd.Put(PdfName.Contents, new PdfString(paddedSig).SetHexWriting(true));
pdfDocument.Close();
结果是:
签名包含不正确、无法识别、损坏或可疑的数据。 支持信息:SigDict /Contents 非法数据
尝试使用 .p7s 文件
我有一个 example.p7s 文件,我尝试使用下面提供的代码嵌入:
byte[] _p7s = System.IO.File.ReadAllBytes("tmp/example.p7s");
private static void Embed_P7S(Org.BouncyCastle.X509.X509Certificate[] _chain, byte[] _p7s)
{
PdfDocument document = new PdfDocument(new PdfReader("results/example-prepared.pdf"));
Stream output = new FileStream("results/example-prepared-signed.pdf", FileMode.Create);
ExternalInjectingSignatureContainer container2 = new ExternalInjectingSignatureContainer(_p7s);
PdfSigner.SignDeferred(document, "Signature1", output, container2);
}
}
internal class ExternalInjectingSignatureContainer :IExternalSignatureContainer
{
public ExternalInjectingSignatureContainer(byte[] signature)
{
Signature = signature;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
public byte[] Sign(Stream data)
{
return Signature;
}
public byte[] Signature;
}
结果:
example-prepared-signed.pdf 未显示证书(这类似于原始 pdf) 但是尺寸比原来的pdf大
original.pdf 是 105KB 示例准备的是 124KB example-prepared-signed 是 121KB
首先,您不必像现在这样拆分签名过程。我已经看到很多开发人员想要这样做的问题,但严格来说没有必要(嗯,在引擎盖下 iText 仍然会首先创建一个准备好的 PDF,然后注入签名容器,但它可以保留在引擎盖下)。
仅当外部签名服务需要很长时间来创建签名并且您无法在这段时间将 PDF 保存在内存中时,才需要拆分流程。
我将在此处研究这两种变体。
单通签名
如果您的外部签名服务 return 足够快地生成结果(一个完整的 PKCS#7 签名容器),您应该使用这种方法。基本代码的开头与您的相似:
PdfSigner signer = new PdfSigner(new PdfReader("example.pdf"), new FileStream("example-signed.pdf", FileMode.Create), new StampingProperties().UseAppendMode());
signer.SetFieldName("Signature1");
PdfSignatureAppearance sigAppearance = signer.GetSignatureAppearance();
sigAppearance
.SetPageRect(new Rectangle(144, 144, 200, 100))
.SetPageNumber(1)
.SetContact("This is contact")
.SetReason("This is reason")
.SetLocation("This is location")
.SetSignatureCreator("This is signature creator");
ExternalServiceSignatureContainer container = new ExternalServiceSignatureContainer();
signer.SignExternalContainer(container, 8192);
您的代码的区别在于 IExternalSignatureContainer
实现:
public class ExternalServiceSignatureContainer : IExternalSignatureContainer
{
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
public byte[] Sign(Stream data)
{
// Call your external signing service to create a CMS signature container
// for the data in the InputStream and return that signature container
[... see below ...]
}
}
根据您的 API 访问外部签名服务,Sign
的实现会有所不同。在每种情况下,我都假设 API_CALL 到 return 结果 PKCS#7 签名容器作为字节数组:
您可以直接通过流调用它
return YOUR_SIGNING_API_CALL_FOR_STREAM(data);
或使用从流内容
生成的字节[]return YOUR_SIGNING_API_CALL_FOR_ARRAY(StreamUtil.InputStreamToArray(data));
作为参数,
或者您可能首先必须自己对数据进行哈希处理(例如如下)并将您的哈希发送到服务。
byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256); return YOUR_SIGNING_API_CALL_FOR_HASH(hash)
signer
的输出已经是最终确定的签名 PDF。
这基本上就是 this answer 中已经讨论过的情况。
二传签
如果您的外部签名服务return结果(完整的 PKCS#7 签名容器)不够快(例如,在批量签名的情况下 APIs 或服务等待如有必要,需要长时间确认),或者如果您在调用签名服务之前在单独的程序中实现了该部分,之后又在单独的程序中实现了该部分(有些人确实这样做了),则可以使用这种方法。
基本代码的开头与您的相似:
PdfSigner signer = new PdfSigner(new PdfReader("example.pdf"), new FileStream("example-prepared.pdf", FileMode.Create), new StampingProperties().UseAppendMode());
signer.SetFieldName("Signature1");
PdfSignatureAppearance sigAppearance = signer.GetSignatureAppearance();
sigAppearance
.SetPageRect(new Rectangle(144, 144, 200, 100))
.SetPageNumber(1)
.SetContact("This is contact")
.SetReason("This is reason")
.SetLocation("This is location")
.SetSignatureCreator("This is signature creator");
ExternalEmptySignatureContainer container = new ExternalEmptySignatureContainer();
signer.SignExternalContainer(container, 8192);
byte[] dataToSign = container.Data;
ExternalEmptySignatureContainer
现在只提供签名服务稍后签名的数据,还没有注入签名容器
public class ExternalEmptySignatureContainer : IExternalSignatureContainer
{
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
public byte[] Sign(Stream data)
{
// Store the data to sign and return an empty array
[... see below ...]
return new byte[0];
}
public byte[] Data;
}
根据您的 API 访问外部签名服务,Sign
的实现会有所不同。
如果您的签名API需要原始数据进行签名,请使用从流内容
生成的字节[]Data = StreamUtil.InputStreamToArray(data);
如果您的签名 API 需要原始数据的哈希值进行签名,请从流内容中这样计算它
Data = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);
signer
的输出是中介,准备好的PDF。
下一步是调用签名服务并检索 PKCS#7 签名容器:
byte[] signature = YOUR_SIGNING_API_CALL(dataToSign);
最后,您将该签名容器注入到准备好的 PDF 中:
PdfDocument document = new PdfDocument(new PdfReader("example-prepared.pdf"));
using (Stream output = new FileStream("example-prepared-signed.pdf", FileMode.Create))
{
ExternalInjectingSignatureContainer container2 = new ExternalInjectingSignatureContainer(signature);
PdfSigner.SignDeferred(document, "Signature1", output, container2);
}
IExternalSignatureContainer
实现仅注入签名字节:
public class ExternalInjectingSignatureContainer : IExternalSignatureContainer
{
public ExternalInjectingSignatureContainer(byte[] signature)
{
Signature = signature;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
public byte[] Sign(Stream data)
{
return Signature;
}
public byte[] Signature;
}
输出是最终确定的签名 PDF。