ECDsa 签名的 PDF 在 Adobe Reader 中显示“文档自签名以来已被更改或损坏”错误

问题描述 投票:0回答:1

我已使用 ECDSA (SHA-256) 证书使用 iText 8.0.2 签署了 PDF 文档。不幸的是,Adobe Reader 说签名无效,原因是“文档自签名以来已被更改或损坏”。由于数字签名主题不是我的专长,因此我无法弄清楚如何以及在哪里查找错误。有人可以指导我吗? 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); // byte tömbben a pfx file tartalmát adjuk be X509Certificate2 signatureCert = new X509Certificate2(cert); ECDsa pk = signatureCert.GetECDsaPrivateKey(); IExternalSignature signature = new EcdsaSignature(pk, DigestAlgorithms.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 _encryptionAlgorithm; private readonly string _hashAlgorithm; private readonly ECDsa _pk; public EcdsaSignature(ECDsa pk, string hashAlgorithm) { _pk = pk; _hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigest(hashAlgorithm)); _encryptionAlgorithm = "ECDSA"; } public string GetDigestAlgorithmName() { return "SHA-256"; } public virtual string GetEncryptionAlgorithm() { return _encryptionAlgorithm; } public virtual string GetHashAlgorithm() { return _hashAlgorithm; } public string GetSignatureAlgorithmName() { return "ECDSA"; } public ISignatureMechanismParams GetSignatureMechanismParameters() { return null; } public virtual byte[] Sign(byte[] message) { return _pk.SignData(message, new HashAlgorithmName(_hashAlgorithm)); } } }

我删除了所有附加功能,例如 TSA 和 LTV,预计可能是这些步骤之一导致了错误。我还尝试使用从 OpenSSL 测试证书集合中获取的测试证书以及从 w3.org 下载的测试 PDF。所有这些都
在这里

c# .net pdf itext7 ecdsa
1个回答
0
投票
ECDsa.SignData

返回 PLAIN 格式的签名值,而 iText 使用 DER 格式的 ECDSA 签名的 OID。因此,在从

EcdsaSignature.Sign
返回签名值之前,您必须将其从 PLAIN 转换为 DER 格式,例如像这样:
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Math;

[...]

public virtual byte[] Sign(byte[] message)
{
    return PlainToDer(_pk.SignData(message, new HashAlgorithmName(_hashAlgorithm)));
}

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);
}

SignWithEcdsaCertEcdsaSignature方法)


PlainToDer

辅助方法借用自 iText 知识库文章

使用 iText 进行数字签名
/第一部分 - 概述和简单案例
/
使用 .NET 进行签名的
X509Certificate2Signature
实现IExternalSignature CryptoAPI/CNG 密钥.

© www.soinside.com 2019 - 2024. All rights reserved.