【问题标题】:How do I sign a PDF document using a certificate from the Windows Cert Store?如何使用 Windows Cert Store 中的证书签署 PDF 文档?
【发布时间】:2013-02-06 11:21:50
【问题描述】:

我需要使用 Windows 证书存储中存在的证书签署 PDF 文档。我整天都在挖掘,试图弄清楚,我如此接近如此遥远

缺少的是:我如何获得一个 IExternalSignature 对象来签署 PDF 文件?

Rahul Singla 编写了一个漂亮的示例,说明如何使用新的 iText 5.3.0 API 签署 PDF 文档 - 只要您可以访问 PC 上某处的 .pfx 文件.

a previous question 使用来自 Windows Cert Store 的证书进行签名,但它使用的 API 版本仍然存在 SetCrypto,并且签名显然是可选的。在 iText 5.3.0 中,API 发生了变化,SetCrypto 不再是东西。

这是我到目前为止所拥有的(为后代添加了 cmets,因为这可能是关于如何在网络上执行此操作的最完整和最新的版本):

using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using BcX509 = Org.BouncyCastle.X509;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Crypto;
using DotNetUtils = Org.BouncyCastle.Security.DotNetUtilities;

...

// Set up the PDF IO
PdfReader reader = new PdfReader(@"some\dir\SomeTemplate.pdf");
PdfStamper stamper = PdfStamper.CreateSignature(reader,
    new FileStream(@"some\dir\SignedPdf.pdf", FileMode.Create), '\0');
PdfSignatureAppearance sap = stamper.SignatureAppearance;

sap.Reason = "For no apparent raisin";
sap.Location = "...";

// Acquire certificate chain
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);

X509CertificateCollection certCollection =
    certStore.Certificates.Find(X509FindType.FindBySubjectName,
    "My.Cert.Subject", true);
X509Certificate cert = certCollection[0];
// iTextSharp needs this cert as a BouncyCastle X509 object; this converts it.
BcX509.X509Certificate bcCert = DotNetUtils.FromX509Certificate(cert);
var chain = new List<BcX509.X509Certificate> { bcCert };
certStore.Close();

// Ok, that's the certificate chain done. Now how do I get the PKS?
IExternalSignature signature = null; /* ??? */

// Sign the PDF file and finish up.
MakeSignature.SignDetached(sap, signature, chain, // the important stuff
    null, null, null, 0, CryptoStandard.CMS);
stamper.Close();

如你所见:除了签名之外我什么都有,我不知道该如何获得它!

【问题讨论】:

  • 非常有用。谢谢!

标签: c# pdf certificate itextsharp signing


【解决方案1】:
X509Certificate cert = certCollection[0]; // Your code
X509Certificate2 signatureCert = new X509Certificate2(cert);

var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private;

如果你有pk,可以如上得到,你创建一个IExternalSignature如下:

IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");

您还可以找到以下使用物品:

【讨论】:

  • 这行得通(您需要先将 signature 转换为 X509Certificate2 对象)。还有一些证书权限问题需要注意 - 我稍后会添加一些相关信息。
  • @Sam 如果证书来自不允许导出私钥的 etoken 设备,var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private; 行将失败。你会得到错误:Key not valid for use in specified state.
【解决方案2】:

请在PDF and digital signatures下载本书。您将在第 3 章中找到有关如何使用 Windows 证书存储进行签名的 Java 示例。如您所见,您需要 Windows-MY 密钥存储。

现在转到我们发布 C# port of these examples 的存储库。寻找C3_11_SignWithToken.cs

X509Store x509Store = new X509Store("My");
x509Store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = x509Store.Certificates;
IList<X509Certificate> chain = new List<X509Certificate>();
X509Certificate2 pk = null;
if (certificates.Count > 0) {
    X509Certificate2Enumerator certificatesEn = certificates.GetEnumerator();
    certificatesEn.MoveNext();
    pk = certificatesEn.Current;
    X509Chain x509chain = new X509Chain();
    x509chain.Build(pk);
    foreach (X509ChainElement x509ChainElement in x509chain.ChainElements) {
        chain.Add(DotNetUtilities.FromX509Certificate(x509ChainElement.Certificate));
    }
}
x509Store.Close();

如果我理解正确chainpk 是您要查找的变量;

【讨论】:

  • 这教会了我应该如何获取证书链,谢谢。但是,这不是我想要的对象 - 在这种情况下,pk 只是一个 X509Certificate2 对象。
  • 很好的代码示例,但有点混乱。 pk 可能被解释为私钥,它不是... pk 是签名证书。
【解决方案3】:
public byte[] SignPdf(byte[] pdf)
{
    using (MemoryStream output = new MemoryStream())
    {
        //get certificate from path
        X509Certificate2 cert1 = new X509Certificate2(@"C:\temp\certtemp.pfx", "12345", X509KeyStorageFlags.Exportable);
        //get private key to sign pdf
        var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert1.PrivateKey).Private;
        // convert the type to be used at .SetCrypt(); 
        Org.BouncyCastle.X509.X509Certificate bcCert = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(cert1);
        // get the pdf u want to sign
        PdfReader pdfReader = new PdfReader(pdf);

        PdfStamper stamper = PdfStamper.CreateSignature(pdfReader, output, '\0');
        PdfSignatureAppearance pdfSignatureAppearance = stamper.SignatureAppearance;
        //.SetCrypt(); sign the pdf
        pdfSignatureAppearance.SetCrypto(pk, new Org.BouncyCastle.X509.X509Certificate[] { bcCert }, null, PdfSignatureAppearance.WINCER_SIGNED);

        pdfSignatureAppearance.Reason = "Este documento está assinado digitalmente pelo Estado Portugues";
        pdfSignatureAppearance.Location = " Lisboa, Portugal";
        pdfSignatureAppearance.SignDate = DateTime.Now;

        stamper.Close();

        return output.ToArray();
    }
} 

我使用此代码获取byte[] PDF 并再次返回已签名的byte[] PDF。

它是 iTextSharp-LGPL。

【讨论】:

  • 非常感谢您拯救了我的一天!我正在努力获取字节数组。
猜你喜欢
  • 2021-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-08
  • 2014-06-17
相关资源
最近更新 更多