簡體   English   中英

如何使用 iText7 將.p7s 字節數組插入 PDF?

[英]How to insert .p7s byte array into PDF with iText7?

我正在嘗試將 a.p7s 字節數組信息插入簽名字段,我按照下面的圖片:

PDF 文檔結構圖像

我的步驟:

准備簽名容器

原來的 PDF 是“tmp/example.pdf”,這部分的 output 是“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”hash 並發送到外部簽名服務並得到了.p7s

現在我想根據上圖將 .p7s byte[] 插入 PDF 結構的簽名值部分。

我嘗試使用下面的代碼獲取“results/prepared.pdf”的PdfDictionayByteRange信息,我希望在“results/injected.pdf”處獲得 injection.p7s 到“results/prepared.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 作為開始 position。

嘗試將.p7s 注入現有的簽名容器

我嘗試在下面制作paddedSig數組並將其copy.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,但是:

簽名包含不正確、無法識別、損壞或可疑的數據。 支持信息:SigDict /Contents 非法數據

我錯過了什么? .. 如何將.p7s 注入准備好的簽名容器?

回復 Mkl 的帖子:

我不明白的是如何將返回的 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 示例准備簽名是 121KB

這是.p7s

首先,您不必像以前那樣拆分簽名過程。 我見過很多開發人員想要這樣做的問題,但嚴格來說沒有必要(好吧,在引擎蓋下 iText 仍然會首先創建一個准備好的 PDF 然后注入簽名容器,但它可以保留在引擎蓋下)。

僅當外部簽名服務需要很長時間才能創建簽名並且您當時無法將 PDF 保留在 memory 中時,才需要拆分該過程。

我將在這里研究這兩種變體。

單通簽名

如果您的外部簽名服務足夠快地返回結果(完整的 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 將結果 PKCS#7 簽名容器作為字節數組返回:

  • 您可以使用 stream 直接調用它

    return YOUR_SIGNING_API_CALL_FOR_STREAM(data);
  • 或使用從 stream 內容生成的 byte[]

     return YOUR_SIGNING_API_CALL_FOR_ARRAY(StreamUtil.InputStreamToArray(data));

    作為參數,

  • 或者您可能首先必須自己 hash 數據(例如如下)並將您的 hash 發送到服務。

     byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256); return YOUR_SIGNING_API_CALL_FOR_HASH(hash)

signer的 output 已經是最終確定、簽名的 PDF。

這基本上是這個答案中已經討論過的情況。

兩通簽名

如果您的外部簽名服務沒有足夠快地返回結果(一個完整的 PKCS#7 簽名容器)(例如,在批量簽名 API 的情況下,或者在服務需要長時間等待確認的情況下),或者如果您站在您這邊您在調用簽名服務之前實現該部分,然后在單獨的程序中實現該部分(有些人確實這樣做),您可以使用這種方法。

同樣,基本代碼的開頭與您的相似:

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 需要原始數據進行簽名,請使用從 stream 內容生成的 byte[]

     Data = StreamUtil.InputStreamToArray(data);
  • 如果您的簽名 API 期望原始數據的 hash 進行簽名,請從 stream 內容中這樣計算

    Data = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);

signer的output為中介,准備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;
}

output 是最終簽署的 PDF。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM