我已经使用ECDSA(SHA-256)证书用iText 8.0.2签署了一个PDF文档.不幸的是,Adobe Reader说签名无效,理由是"Document has been altered or corrupted since it was signed".由于数字签名主题不是我的专长,我不知道如何查找错误以及在哪里查找错误.有谁能给我带路吗?

using iText.Kernel.Pdf;
using iText.Signatures;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace PdfSignTest
{   
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("Usage: PdfSignTest.exe pdfFilePath pfxFilePath");
                Console.WriteLine(" - pdfFilePath: path of an unsigned pdf file which will be signed");
                Console.WriteLine(" - pfxFilePath: path of a pfx file containing the full certificate chain");
                Console.ReadKey();
                return;
            }

            string srcPath = args[0];
            string cerPath = args[1];
            string dstPath = srcPath.Replace(".pdf", ".signed.at." + DateTime.Now.ToString("HH-mm-ss") + ".pdf");

            var signed = SignPdfECC(srcPath, cerPath);
            File.WriteAllBytes(dstPath, signed);

            Console.WriteLine(string.Format("File {0} successfully signed and saved as {1}.", srcPath, dstPath));
            Console.ReadKey();
        }

        private static byte[] SignPdfECC(string unsignedPdfPath, string certificatePfxPath)
        {
            byte[] certificatePfx = File.ReadAllBytes(certificatePfxPath);

            using (PdfReader reader = new PdfReader(unsignedPdfPath))
            using (MemoryStream mem = new MemoryStream())
            {
                PdfSigner signer = new PdfSigner(reader, mem, new StampingProperties().UseAppendMode());
                X509Certificate cert = new X509Certificate(certificatePfx, "password", X509KeyStorageFlags.Exportable);
                X509Certificate2 signatureCert = new X509Certificate2(cert);
                ECDsa pk = signatureCert.GetECDsaPrivateKey();
                IExternalSignature signature = new EcdsaSignature(pk, Digest算法rithms.SHA256);
                iText.Bouncycastle.X509.X509CertificateBC[] chain = new iText.Bouncycastle.X509.X509CertificateBC[] { new iText.Bouncycastle.X509.X509CertificateBC(new Org.BouncyCastle.X509.X509Certificate(signatureCert.GetRawCertData())) };
                signer.SignDetached(signature, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
                return mem.ToArray();
            }
        }
    }

    public class EcdsaSignature : IExternalSignature
    {
        private readonly string _encryption算法rithm;
        private readonly string _hash算法rithm;
        private readonly ECDsa _pk;

        public EcdsaSignature(ECDsa pk, string hash算法rithm)
        {
            _pk = pk;
            _hash算法rithm = Digest算法rithms.GetDigest(Digest算法rithms.GetAllowedDigest(hash算法rithm));
            _encryption算法rithm = "ECDSA";
        }

        public string GetDigest算法rithmName()
        {
            return "SHA-256";
        }

        public virtual string GetEncryption算法rithm()
        {
            return _encryption算法rithm;
        }

        public virtual string GetHash算法rithm()
        {
            return _hash算法rithm;
        }

        public string GetSignature算法rithmName()
        {
            return "ECDSA";
        }

        public ISignatureMechanismParams GetSignatureMechanismParameters()
        {
            return null;
        }

        public virtual byte[] Sign(byte[] message)
        {
            return _pk.SignData(message, new Hash算法rithmName(_hash算法rithm));
        }
    }
}

我删除了所有其他功能,如TSA和LTV,以为可能是这些步骤中的一个导致了错误.我还try 使用从OpenSSL测试证书集合中获取的测试证书,以及从w3.org下载的测试PDF.所有这些都是here.

推荐答案

这里的问题是,ECDsa.SignData返回纯格式的签名值,而iText使用DER格式的ECDSA签名的OID.因此,在从EcdsaSignature.Sign返回签名值之前,您必须将其从纯格式转换为DER格式,例如:

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Math;

[...]

public virtual byte[] Sign(byte[] message)
{
    return PlainToDer(_pk.SignData(message, new Hash算法rithmName(_hash算法rithm)));
}

byte[] PlainToDer(byte[] plain)
{
    int valueLength = plain.Length / 2;
    BigInteger r = new BigInteger(1, plain, 0, valueLength);
    BigInteger s = new BigInteger(1, plain, valueLength, valueLength);
    return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
}

一百零二

PlainToDer帮助器方法是从iText知识库文章Digital Signing with iText/Part I - Overview and Simple Cases/Signing with .NET CryptoAPI/CNG KeysIExternalSignatureX509Certificate2Signature实现中borrow 的.

Csharp相关问答推荐

编写DataAnnotations自定义验证器的多种方法

如何注册实现同一接口的多个服务并注入到控制器构造函数中

C#DateTime.ToString在ubuntu和centos中返回不同的结果

Polly使用泛型重试和重试包装函数

如何使用C#Interop EXCEL创建度量衡

在C#中,有没有一种方法可以集中定义跨多个方法使用的XML参数描述符?

如何在C#中实现非抛出`MinBy`?

Docker Container中的HttpRequest后地址不可用

在使用Audit.NET的AuditTrail实现中,如何逐月将数据摄取到AzureTableStorage?

在C#中过滤Excel文件

为什么Docker中没有转发该端口?

用于获取字符串的最后12个字符的正则表达式(空格除外)

EF Core 7-忽略模型绑定中的虚拟属性

如何在C#中从MongoDB IPipelineStageDefinition中获取聚合命令的字段/选项?

WPF如何获取有关从一个视图模型更改另一个视图模型的信息

游戏对象走向不同的方向

ASP.NET核心MVC|如何在控制器方法之间传递值

实例化列表时的集合表达式是什么?

C#Web服务转换为 node /Express不工作

使用ImmutableList时,DynamicData未按预期工作