数字PDF文档签名 [英] Digital PDF Document Signing

查看:127
本文介绍了数字PDF文档签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在 https://1drv.ms/u/s上载了示例! Al69FgQ8jwmZbgiBMXLLM4j5sbU?e = vyGF4m

可以请您检查一下.我被困在最后一步.但是,请确认其他方法是否正确.

Can you please check. I am stuck at last step. However, please confirm if other appraoch is correct.

我已确认流程.所以我很清楚.

I have confirmed the flow. So I am clear on it.

作为该数字签名PDF文档流程的一部分,我们希望使用第三方提供PDF的签名哈希. 步骤如下:

As part of that digital signing PDF document flow , We want to use third party to provide Signed Hash of PDF. Here are steps:

  1. 有第三方内部系统,可以从单词生成PDF文档.
  2. 该PDF将被发送到另一个服务,该服务将生成该PDF的哈希值
  3. 该哈希值将被发送到外部服务,以使用私钥唱歌哈希.
  4. 外部系统将发送签名的哈希和公钥证书,内部服务将使用这些证书和公钥证书在PDF文档中添加签名.

我有以下问题.

  1. 内部服务上方的第1点是创建PDF和签名块.是否需要创建签名块?因为这是延迟签名?
  2. 如果是,则第2点中的服务如何获取PDF文档的原始内容以生成哈希.

我们使用了具有签名的现有PDF,并使用iText 7获取原始内容. 这种方法正确吗? FormB.PDF具有签名,并且通过删除signaure1字段,我们得到的是原始内容.这个过程会行得通吗?

we have used existing PDF which has signature and using iText 7 to get original content. Is this method correct? FormB.PDF has signature and by removing signaure1 field we are getting original content. Will this process work and advistable?

我们也尝试使用pdfsigner.getRangeStream()方法,但是在文档中并不清楚.请帮助

We also tried to use pdfsigner.getRangeStream() method, but its not that clear in documentation and not yet clear. Please help

package com.abc.sd;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.List;

import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.signatures.SignatureUtil;

public class ItextPdf7 {

    public static void main(String [] args) throws IOException, NoSuchAlgorithmException {
        String filePath ="C:\\\\abc\\\\test\\\\FormB.pdf";
        PdfReader reader = new PdfReader(filePath);
        PdfDocument pdfDoc = new PdfDocument(reader);
        PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, false);
        SignatureUtil signUtil = new SignatureUtil(pdfDoc);
        List<String> names = signUtil.getSignatureNames();
        System.out.println("Signature Name>>>"+names);
      //  System.out.println("Singature Data>>"+signUtil.readSignatureData("Signature1"));


        PdfReader reader1 = new PdfReader(filePath);
        PdfDocument pdfDoc1 = new PdfDocument(reader1, new PdfWriter("C:\\\\\\\\abc\\\\\\\\test\\\\\\\\unsigned_latest_iext7.pdf"));
        PdfAcroForm form1 = PdfAcroForm.getAcroForm(pdfDoc1, true);
        form1.flattenFields();
        pdfDoc1.close();


    }

}

**********************************

我们希望签署PDF文档.根据我的理解,这是一些步骤.

******************************

We are looking to sign PDF document. here are steps as per my understanding.

  1. 消费者会将PDF文档摘要发送到中央系统. PDF的摘要将排除签名部分

  1.      Consumer will send a digest of PDF document to Central System. The digest of the PDF will exclude the signature section

中央系统会将摘要(使用消费者的私钥/公钥签名(不确定)签名)发送给消费者

      Central System will send the digest (signed using consumer’s private key/public key ? not sure) to consumer

消费者系统将在PDF文档的签名部分中添加摘要(可能与公钥一起使用)

      consumer system will add digest within in the signature section the PDF document (may be along with public key ??)

能为您提供帮助吗?

  1. 以上流程对我的理解是否正确?任何小的参考指南/链接都会对您有所帮助或提供任何流程图.

  1. If my understanding is correct with above flow? Any small reference guide / link will help or any flow diagram.

使用.Net和Java,可以实现此工作的库是什么?都是开源的,还是付费的. iTextSharp在这里有用吗?

With .Net and Java what are libraries which can do this work ?Both open source and paid. Will iTextSharp is relevant here?

如果客户打开PDF,如何进行验证?是否需要采取任何具体措施来签署文件?

How validation will happen if customer opens the PDF? If there any specific action required document signing?

请帮助.

推荐答案

这里有很多方面和子问题,无论是在问题文本中还是在其下的注释中.这个答案很有启发性,但是其中一些在首先介绍了一些背景之后就出现了.

集成的PDF签名意味着PDF中存在许多结构:

An integrated PDF signature implies the presence of a number of structures in a PDF:

  • 签名AcroForm表单字段.该表单字段可以具有小部件注释(可视化内容可以包含您希望放入的任何信息),但不必具有该注释.

  • A signature AcroForm form field. This form field can have a widget annotation (a visualization which can contain any information you care to put into it) but it does not need to have one.

此签名表单字段中的值.与其他表单字段不同,签名字段的值不是单纯的字符串,而是键值对的字典.内容根据签名的确切类型而有所不同.但是,在可互操作类型的情况下,总是有一个 Contents 条目,其值是一个二进制字符串,其中包含实际的PKCS1/PKCS7/CMS/RFC3161签名或时间戳,该时间戳或时间戳覆盖了整个文件,但该二进制文件除外字符串.

A value in this signature form field. Unlike other form fields, the value of a signature field is not a mere string but a dictionary of key-value pairs. The contents differ depending on the exact type of signature. In case of the interoperable types, though, there always is a Contents entry whose value is a binary string containing the actual PKCS1/PKCS7/CMS/RFC3161 signature or time stamp which covers the whole file except this binary string.

(该草图有点误导:'<'和'>'十六进制字符串定界符是已签名数据的 not 部分.)

(The sketch is a bit misleading: the '<' and '>' hex string delimiters are not part of the signed data.)

对于类型为 adbe.x509.rsa_sha1 的情况,内容条目包含PKCS1签名.签名值字典还必须包含一个包含签名证书的 Cert 条目.

In case of type adbe.x509.rsa_sha1 the Contents entry contains a PKCS1 signature. The signature value dictionary additionally must contain a Cert entry containing the signing certificate.

对于类型为 ETSI.RFC3161 的情况, Contents 条目包含RFC 3161时间戳记令牌.

In case of type ETSI.RFC3161 the Contents entry contains a RFC 3161 time stamp token.

对于类型为 ETSI.CAdES.detached adbe.pkcs7.detached adbe.pkcs7.sha1 的类型内容条目包含CMS签名容器.由于签名容器可以保存签名证书,因此不需要证书条目.

In case of types ETSI.CAdES.detached, adbe.pkcs7.detached, and adbe.pkcs7.sha1 the Contents entry contains a CMS signature container. As the signature container can hold the signing certificate, there is no need for a Cert entry for the signing certificate.

CMS签名容器可以包含签名属性"的结构.如果是这样,则这些属性之一必须是已签名的PDF字节的哈希值(请参见上文, Contents 之外的所有内容)以及包装在容器中的实际签名字节对这些签名的属性进行签名.是否允许不带签名属性的变体以及另外需要哪些属性,取决于签名的确切类型.

A CMS signature container can contain a structure of "signed attributes". If it does, one of these attributes must be the hash of the signed PDF bytes (see above, everything but the Contents value) and the actual signature bytes wrapped in the container sign these signed attributes. Whether the variant without signed attributes is allowed and which attributes additionally are required, depends on the exact type of the signature.

对于 ETSI.CAdES.detached ,CMS容器必须包含签名的属性.此外,必须已签名属性之一是引用签名者证书的ESS签名证书或signing-certificate-v2属性.

In case of ETSI.CAdES.detached, the CMS container must contain signed attributes. Furthermore, one of the signed attributes must be an ESS signing-certificate or signing-certificate-v2 attribute referencing the signer certificate.

在这种情况下,LTV信息可以在以后以增量更新的方式添加到PDF中,而不必在已签名的PDF中出现.

LTV information in this case can be added later in an incremental update to the PDF, they need not be present in the signed PDF.

对于 adbe.pkcs7.detached adbe.pkcs7.sha1 ,通常不需要签名属性.但是,根据确切的签名策略(由法律或合同规定),可能仍需要签名属性,尤其是ESS签名证书的签名属性.

In case of adbe.pkcs7.detached and adbe.pkcs7.sha1 there generically do not need to be signed attributes. Depending on the exact signing policy (prescribed by law or contract), though, signed attributes and in particular the ESS signing certificate signed attribute may be required nonetheless.

这些签名类型已在ISO 32000-1中定义.如果一个人的签名策略仅基于ISO 32000-1,则LTV信息必须存储在adbe-revocationInfoArchival属性中,该属性必须是一个签名属性.

These signature types were already defined in ISO 32000-1. If one's signature policy is based on ISO 32000-1 alone, LTV information must be stored in the adbe-revocationInfoArchival attribute which must be a signed attribute.

在注释中,您引用了iText"PDF和数字签名"电子书,该电子书似乎表明它足以与签名一起检索签名证书.

In comments you reference the iText "PDF and Digital Signatures" ebook which appears to say that it suffices to retrieve the signing certificate together with the signature.

不过,根据上述背景,我们意识到

In the light of the backgrounds explained above, though, we realize that

  • 对于 adbe.x509.rsa_sha1 签名,签名证书必须位于签名值字典的 Cert 条目中.由于该条目不在 Contents (内容)条目中,因此该证书是已签名数据的一部分.因此,必须在签名前 知道.

  • In case of adbe.x509.rsa_sha1 signatures, the signing certificate must be in the value of the Cert entry of the signature value dictionary. As this entry is not in the Contents entry, this certificate is part of the signed data. Thus, it must be known before signing.

对于 ETSI.CAdES.detached 签名,已签名属性必须包含ESS签名证书或signing-certificate-v2属性.此属性引用签名者证书.因此,必须在签名前 知道.

In case of ETSI.CAdES.detached signatures, the signed attributes must contain an ESS signing-certificate or signing-certificate-v2 attribute. This attribute references the signer certificate. Thus, it must be known before signing.

对于 adbe.pkcs7.detached adbe.pkcs7.sha1 ,这取决于必须遵循的实际签名策略. ESS signing-certificate或signing-certificate-v2属性是必需的.因此,这取决于在签名之前是否需要知道签名证书.

In case of adbe.pkcs7.detached and adbe.pkcs7.sha1 it depends on the actual signature policy one has to adhere to whether or not an ESS signing-certificate or signing-certificate-v2 attribute is required. Thus, it depends whether or not the signing certificate needs to be known before signing.

但是,如果仅基于ISO 32000-1进行签名,则LTV信息必须存储在完全签名的属性中,并且要检索LTV信息,很显然,人们需要知道一个试图对其进行认证的证书.检索它们,特别是签名者证书.

In case of a signature policy based on ISO 32000-1 alone, though, LTV information must be stored in a signed attribute if at all, and to retrieve LTV information, one obviously needs to know the certificates for which one attempts to retrieve them, in particular the signer certificate.

因此,要回答此主题标题中的问题,请执行以下操作:仅在宽松的签名策略的上下文中,只要不需要添加LTV信息,您就可以在不知道签名者证书的情况下逃脱责任.

To answer the question in this topic's header, therefore: Only in context of a lax signature policy you can get away with not knowing the signer certificate before signing as long as you don't need to add LTV information.

在评论中,您提到您需要使用PAdES和LTV .这是否意味着您在签名之前需要签名者证书?

In a comment you mention you need to use PAdES and LTV. Does that mean you need the signer certificate before signing?

嗯,这取决于.

如果使用PAdES 意味着使用PAdES基线配置文件或扩展的PAdES配置文件(BES/EPES),则必须创建 ETSI.CAdES.detached 签名.因此,您在签名之前确实需要签名者证书.

If using PAdES means using PAdES baseline profiles or extended PAdES profiles (BES/EPES), you have to create ETSI.CAdES.detached signatures. Thus, you do need the signer certificate before signing.

但是,如果只需要PAdES配置文件来进行PDF中的CMS数字签名(本质上是ISO 32000-1兼容性配置文件),则不需要签名者证书.

But if it only requires the PAdES profile for CMS digital signatures in PDF (essentially the ISO 32000-1 compatibility profile), you don't need the signer certificate before signing.

但是此配置文件特别暗示:如果存在,则任何吊销信息都应是PDF签名的签名属性.因此,对于"PAdES和LTV",您再次确实需要 em>签名之前的签名者证书.

But this profile implies in particular: If present, any revocation information shall be a signed attribute of the PDF Signature. Thus, for "PAdES and LTV" you again do need the signer certificate before signing.

因此,在某些设置中,您在计算实际签名之前不需要签署者证书.通常,尽管如此,安全API仍然需要尽早提供证书.

So there are setups in which you shouldn't need the signer certificate before calculating the actual signature. Usually, though, security APIs require the certificate early nonetheless.

使用Bouncy Castle低级API,您可以按照以下步骤进行操作. (我假设您正在使用SHA256withRSA.)

Using Bouncy Castle low-level APIs you can do that as follows. (I assume you are using SHA256withRSA.)

首先准备PDF并确定哈希值

First prepare the PDF and determine the hash value

byte[] Hash = null;

using (PdfReader reader = new PdfReader("original.pdf"))
using (FileStream fout = new FileStream("prepared.pdf", FileMode.Create))
{
    StampingProperties sp = new StampingProperties();
    sp.UseAppendMode();

    PdfSigner pdfSigner = new PdfSigner(reader, fout, sp);
    pdfSigner.SetFieldName("Signature");

    PdfSignatureAppearance appearance = pdfSigner.GetSignatureAppearance();
    appearance.SetPageNumber(1);

    int estimatedSize = 12000;
    ExternalHashingSignatureContainer container = new ExternalHashingSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
    pdfSigner.SignExternalContainer(container, estimatedSize);
    Hash = container.Hash;
}

现在要签名的PDF字节的哈希在Hash中.

Now the hash of the PDF bytes to sign is in Hash.

此处使用的ExternalHashingSignatureContainer类是以下帮助程序类:

The ExternalHashingSignatureContainer class used here is the following helper class:

public class ExternalHashingSignatureContainer : ExternalBlankSignatureContainer
{
    public ExternalHashingSignatureContainer(PdfName filter, PdfName subFilter) : base(filter, subFilter)
    { }

    public override byte[] Sign(Stream data)
    {
        SHA256 sha = new SHA256CryptoServiceProvider();
        Hash = sha.ComputeHash(data);
        return new byte[0];
    }

    public byte[] Hash { get; private set; }
}

对于上面在Hash变量中计算出的哈希,您现在可以请求PKCS#1签名和签名者证书.然后,您可以按以下方式构造CMS容器:

For the hash calculated above in the Hash variable you can now request a PKCS#1 signature and the signer certificate. Then you can construct the CMS container as follows:

byte[] signatureBytes = THE_RETRIEVED_SIGNATURE_BYTES;
byte[] certificateBytes = THE_RETRIEVED_CERTIFICATE_BYTES;

X509Certificate x509Certificate = new X509CertificateParser().ReadCertificate(certificateBytes);

SignerIdentifier sid = new SignerIdentifier(new IssuerAndSerialNumber(x509Certificate.IssuerDN, x509Certificate.SerialNumber));
AlgorithmIdentifier digAlgorithm = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256);
Attributes authenticatedAttributes = null;
AlgorithmIdentifier digEncryptionAlgorithm = new AlgorithmIdentifier(Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.Sha256WithRsaEncryption);
Asn1OctetString encryptedDigest = new DerOctetString(signatureBytes);
Attributes unauthenticatedAttributes = null;
SignerInfo signerInfo = new SignerInfo(sid, digAlgorithm, authenticatedAttributes, digEncryptionAlgorithm, encryptedDigest, unauthenticatedAttributes);

Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
digestAlgs.Add(signerInfo.DigestAlgorithm);
Asn1Set digestAlgorithms = new DerSet(digestAlgs);
ContentInfo contentInfo = new ContentInfo(CmsObjectIdentifiers.Data, null);
Asn1EncodableVector certs = new Asn1EncodableVector();
certs.Add(x509Certificate.CertificateStructure.ToAsn1Object());
Asn1Set certificates = new DerSet(certs);
Asn1EncodableVector signerInfs = new Asn1EncodableVector();
signerInfs.Add(signerInfo);
Asn1Set signerInfos = new DerSet(signerInfs);
SignedData signedData = new SignedData(digestAlgorithms, contentInfo, certificates, null, signerInfos);

contentInfo = new ContentInfo(CmsObjectIdentifiers.SignedData, signedData);

byte[] Signature = contentInfo.GetDerEncoded();

现在CMS签名容器字节在Signature中.

Now the CMS signature container bytes are in Signature.

对于上述内容,请使用这些BouncyCastle using s

For the above please use these BouncyCastle usings

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;

您现在可以将签名容器字节嵌入到PDF中,如下所示:

You can now embed the signature container bytes into the PDF like this:

using (PdfReader reader = new PdfReader("prepared.pdf"))
using (PdfDocument document = new PdfDocument(reader))
using (FileStream fout = new FileStream("signed.pdf", FileMode.Create))
{
    PdfSigner.SignDeferred(document, "Signature", fout, new ExternalPrecalculatedSignatureContainer(Signature));
}

此处使用的ExternalPrecalculatedSignatureContainer类是以下帮助程序类:

The ExternalPrecalculatedSignatureContainer class used here is the following helper class:

public class ExternalPrecalculatedSignatureContainer : ExternalBlankSignatureContainer
{
    public ExternalPrecalculatedSignatureContainer(byte[] cms) : base(new PdfDictionary())
    {
        Cms = cms;
    }

    public override byte[] Sign(Stream data)
    {
        return Cms;
    }

    public byte[] Cms { get; private set; }
}

但是,如上所述,此签名容器不是CAdES容器.因此,您的PDF签名将不是真正的PAdES签名(基准或扩展配置文件),但最多只能是ISO 32000-1兼容性的PAdES签名.

As mentioned above, though, this signature container is not a CAdES container. Thus, your PDF signatures won't be true PAdES signature (baseline or extended profiles) but at best ISO 32000-1 compatibility PAdES signatures.

您的Client方法createSignedData看起来像这样:

Your Client method createSignedData looks like this:

public byte[] createSignedData(byte[] sh)
{
    string dire = Directory.GetParent(Directory.GetParent(Directory.GetCurrentDirectory()).ToString()).ToString();
    string PROPERTIES = dire + "\\resources\\signkey.properties";
    Properties properties = new Properties();
    properties.Load(new FileStream(PROPERTIES, FileMode.Open, FileAccess.Read));
    String path = properties.GetProperty("PRIVATE");
    char[] pass = properties.GetProperty("PASSWORD").ToCharArray();
    string alias = null;
    Pkcs12Store pk12;
    pk12 = new Pkcs12Store(new FileStream(path, FileMode.Open, FileAccess.Read), pass);
    foreach (var a in pk12.Aliases)
    {
        alias = ((string)a);
        if (pk12.IsKeyEntry(alias))
            break;
    }

    ICipherParameters pk = pk12.GetKey(alias).Key;
    IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256);
    byte[] data = pks.Sign(sh);
    return data;

}

不幸的是,PrivateKeySignature.Sign希望消息登录sh参数,尤其是首先对其进行哈希处理.另一方面,在您的用例中,sh已经是要签名的消息的哈希.因此,您可以有效地对应该进行哈希处理的位置进行两次哈希处理,而只需一次.

Unfortunately PrivateKeySignature.Sign expects the message to sign in the sh parameter and in particular first hashes it. In your use case on the other hand sh already is the hash of the message to sign. Thus, you effectively hash twice where you should hash but once.

您可以通过替换来解决此问题

You can fix this by replacing

IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256);
byte[] data = pks.Sign(sh);

在上面的代码中

StaticDigest digest = new StaticDigest();
digest.AlgorithmName = "SHA-256";
digest.Digest = sh;
RsaDigestSigner signer = new RsaDigestSigner(digest);
signer.Init(true, pk);
byte[] data = signer.GenerateSignature();

此处StaticDigest是以下帮助程序类:

Here StaticDigest is the following helper class:

public class StaticDigest : IDigest
{
    public string AlgorithmName { get; set; }
    public byte[] Digest { get; set; }

    public void BlockUpdate(byte[] input, int inOff, int length)
    { }

    public int DoFinal(byte[] output, int outOff)
    {
        Array.Copy(Digest, 0, output, outOff, Digest.Length);
        return Digest.Length;
    }

    public int GetByteLength()
    {
        return 64;
    }

    public int GetDigestSize()
    {
        return Digest.Length;
    }

    public void Reset()
    { }

    public void Update(byte input)
    { }
}

此更改之后,您的测试项目将返回数学上有效的签名.

After this change your test project returns mathematically valid signatures.

这篇关于数字PDF文档签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆