使用iText 7在2个步骤中对PDF文件进行数字签名,结果是损坏的文件。

huangapple go评论78阅读模式
英文:

Digitally sign a pdf file in 2 steps with iText 7, result is corrupted file

问题

我会为您翻译代码部分:

首先,我会翻译计算哈希和签名的部分:

public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
{
    IList<ICrlClient> crlList = new List<ICrlClient>();
    crlList.Add(new CrlClientOnline(chains));

    using (var reader = new PdfReader(source))
    {
        using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
        {
            var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());

            var signatureAppearance = signer.GetSignatureAppearance();
            signatureAppearance
                .SetReason(reason)
                .SetLocation(location)
                .SetPageNumber(pageNumber)
                .SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
                .SetCertificate(chains[0])
                .SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));

            var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
            signer.SignExternalContainer(container, 8192);

            byte[] hash = container.Hash;

            return hash;
        }
    }
}

以下是签名容器部分的翻译:

public class EmptySignatureContainer : IExternalSignatureContainer
{
    private PdfDictionary _sigDic;
    public byte[] Hash;

    public byte[] Sign(Stream data)
    {
        string hashAlgorithm = "SHA256";

        try
        {
            this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        }
        catch (IOException e)
        {
            throw new GeneralSecurityException("EmptySignatureContainer signing exception", e);
        }

        return new byte[0];
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.PutAll(_sigDic);
    }

    public EmptySignatureContainer(PdfName filter, PdfName subFilter)
    {
        _sigDic = new PdfDictionary();
        _sigDic.Put(PdfName.Filter, filter);
        _sigDic.Put(PdfName.SubFilter, subFilter);
    }
}

接下来是计算哈希的部分:

var hash = ComputeHash(inputFile, tempFile, chain, "sign-reason", "", 1, 1);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);
var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);

var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);

最后,是签名和添加签名的部分:

Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);

签名方法的翻译如下:

public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
{
    using (PdfReader reader = new PdfReader(tempFile))
    {
        using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
        {
            var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
            var signer = new PdfSigner(reader, outStream, new StampingProperties());
            signer.SignExternalContainer(signedContainer, 8192);
        }
    }
}

最后是签名容器的翻译:

public class SignedSignatureContainer : IExternalSignatureContainer
{
    public byte[] Hash { get; set; }
    public byte[] SignedHash { get; set; }
    public X509Certificate[] CertChains { get; set; }

    public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
    {
        this.Hash = hash;
        this.SignedHash = signedHash;
        this.CertChains = certCertChains;
    }

    public byte[] Sign(Stream data)
    {
        var sgn = new PdfPKCS7(null,  CertChains, "SHA256", false);
        sgn.SetExternalDigest(this.SignedHash, null, "RSA");
        return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
        signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
    }
}

希望这有助于您理解代码。如果您有其他问题,请随时提出。

英文:

I'm trying to digitally sign a PDF file in two steps: compute hash, sign hash, and then merge the signed hash to the output file. The library that I'm using is iText 7.

However, the result is a corrupted file and cannot be opened.

What is wrong?

First I'm computing the hash and sign the input file with an empty signature container:

public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
{
    IList&lt;ICrlClient&gt; crlList = new List&lt;ICrlClient&gt;();
    crlList.Add(new CrlClientOnline(chains));

    using (var reader = new PdfReader(source))
    {
        using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
        {
            var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());

            var signatureAppearance = signer.GetSignatureAppearance();
            signatureAppearance
                .SetReason(reason)
                .SetLocation(location)
                .SetPageNumber(pageNumber)
                .SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
                .SetCertificate(chains[0])
                .SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));

            var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
            signer.SignExternalContainer(container, 8192);

            byte[] hash = container.Hash;

            return hash;
        }
    }
}

Here is the container:

public class EmptySignatureContainer : IExternalSignatureContainer
{
    private PdfDictionary _sigDic;
    public byte[] Hash;

    public byte[] Sign(Stream data)
    {
        string hashAlgorithm = &quot;SHA256&quot;;

        try
        {
            this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        }
        catch (IOException e)
        {
            throw new GeneralSecurityException(&quot;EmptySignatureContainer signing exception&quot;, e);
        }

        return new byte[0];
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.PutAll(_sigDic);
    }

    public EmptySignatureContainer(PdfName filter, PdfName subFilter)
    {
        _sigDic = new PdfDictionary();
        _sigDic.Put(PdfName.Filter, filter);
        _sigDic.Put(PdfName.SubFilter, subFilter);
    }
}

After that, I get the authenticated attribute bytes and compute its hash:

var hash = ComputeHash(inputFile, tempFile, chain, &quot;sign-reason&quot;, &quot;&quot;,  1, 1);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, &quot;SHA256&quot;, false);
var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);

var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);`

The computedHash then will be sent to the hsm server to sign.
The signed hash then will be added back to the temporary file as following code:

Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);

And here is the Sign method:

public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
{
    using (PdfReader reader = new PdfReader(tempFile))
    {
        using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
        {
            var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
            var signer = new PdfSigner(reader, outStream, new StampingProperties());
            signer.SignExternalContainer(signedContainer, 8192);
        }
    }
}

The Signed container as following:

public  class SignedSignatureContainer : IExternalSignatureContainer
{
    public byte[] Hash { get; set; }
    public byte[] SignedHash { get; set; }
    public X509Certificate[] CertChains { get; set; }

    public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
    {
        this.Hash = hash;
        this.SignedHash = signedHash;
        this.CertChains = certCertChains;
    }

    public byte[] Sign(Stream data)
    {
        var sgn = new PdfPKCS7(null,  CertChains, &quot;SHA256&quot;, false);
        sgn.SetExternalDigest(this.SignedHash, null, &quot;RSA&quot;);
        return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
        signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
    }
}

答案1

得分: 0

在你的Sign方法中,你使用了PdfSigner方法的SignExternalContainer

var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);

但这不会填充原先准备好的签名!相反,它会创建另一个签名字段,并使用原始签名的签名来填充它。

请改用静态的PdfSigner方法SignDeferred

/// <summary>Signs a PDF where space was already reserved.</summary>
/// <param name="document">the original PDF</param>
/// <param name="fieldName">the field to sign. It must be the last field</param>
/// <param name="outs">the output PDF</param>
/// <param name="externalSignatureContainer">
/// the signature container doing the actual signing. Only the
/// method ExternalSignatureContainer.sign is used
/// </param>
public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
     IExternalSignatureContainer externalSignatureContainer)
英文:

In your Sign method you use the PdfSigner method SignExternalContainer:

var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);

But that does not fill in the originally prepared signature! Instead this creates another signature field and fills it with a signature for the original one.

Use the static PdfSigner method SignDeferred instead:

/// &lt;summary&gt;Signs a PDF where space was already reserved.&lt;/summary&gt;
/// &lt;param name=&quot;document&quot;&gt;the original PDF&lt;/param&gt;
/// &lt;param name=&quot;fieldName&quot;&gt;the field to sign. It must be the last field&lt;/param&gt;
/// &lt;param name=&quot;outs&quot;&gt;the output PDF&lt;/param&gt;
/// &lt;param name=&quot;externalSignatureContainer&quot;&gt;
/// the signature container doing the actual signing. Only the
/// method ExternalSignatureContainer.sign is used
/// &lt;/param&gt;
public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
     IExternalSignatureContainer externalSignatureContainer)

huangapple
  • 本文由 发表于 2023年4月19日 15:04:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76051603.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定