Itext8 .net pdf 与受信任的第三方签名

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

我今天请求您帮助使用 C# 中的 iText8 进行 PaDES Baseline-LTA Profile PDF 签名项目。

我使用受信任的第三方 API 来签名哈希,我想遵循此工作流程:

  • 计算待签名数据
  • 计算待签名数据摘要(sha256)
  • 签名哈希(可信第三方API)
  • 创建签名文档

可信第三方建议我使用 DSS 生成 DTBS (https://github.com/esig/dss),但我正在使用 C# 工作,不想使用 Java。

我尝试了多种方法,包括这个:https://github.com/itext/itext-publications-signatures-java/blob/develop/src/test/java/com/itextpdf/samples/signatures/chapter04 /C4_07_ClientServerSigning.java

老实说,我觉得我完全偏离了目标......

是否可以在没有 DSS 帮助的情况下使用 BouncyCastle 创建 DTBS?

我应该使用 iText 什么方法来实现这种类型的签名?

预先感谢您的帮助。

编辑:

生成的pdf:https://drive.google.com/file/d/1rnY7tuXPJkzSgLFasQ6R7I8BDQ1RWwM_/view?usp=sharing

我使用 itext 8.0.3

测试代码:


    using Flurl;
    using Flurl.Http;
    using iText.Bouncycastle.X509;
    using iText.Commons.Bouncycastle.Cert;
    using iText.Kernel.Geom;
    using iText.Kernel.Pdf;
    using iText.Signatures;
    using Org.BouncyCastle.X509;
    using SignatureCerteurope.Certeurope;
    using SignatureCerteurope.ComponentC.Json;
    using System.Security.Cryptography.X509Certificates;
    
    string DEST = "SIGNED.PDF";
    string SRC = "TEST.PDF";
    
    var sign = new SignatureAPI();
    var cert = await sign.GetCertificate();
    
    string base64Certificate = cert.pem
                    .Replace("-----BEGIN CERTIFICATE-----\n", "")
                    .Replace("\n-----END CERTIFICATE-----\n", "");
    
    byte[] certificateBytes = Convert.FromBase64String(base64Certificate);
    
    Org.BouncyCastle.X509.X509CertificateParser parser = new Org.BouncyCastle.X509.X509CertificateParser();
    
    Org.BouncyCastle.X509.X509Certificate x509cert = parser.ReadCertificate(certificateBytes);
    
    iText.Commons.Bouncycastle.Cert.IX509Certificate[] certificateWrappers = new IX509Certificate[1];
    
    certificateWrappers[0] = new X509CertificateBC(x509cert);
    
    new C4_07_ClientServerSigning().Sign(SRC, DEST, certificateWrappers, PdfSigner.CryptoStandard.CMS, "test reason", "test location");
    
    public class C4_07_ClientServerSigning
    {
        public void Sign(string src, string dest, IX509Certificate[] chain, PdfSigner.CryptoStandard subfilter, string reason, string location)
        {
            PdfReader reader = new PdfReader(src);
            PdfSigner signer = new PdfSigner(reader, new FileStream(dest, FileMode.Create), new StampingProperties());
    
            // Create the signature appearance
            Rectangle rect = new Rectangle(36, 648, 200, 100);
            signer.SetReason(reason)
                    .SetLocation(location)
                    .SetPageRect(rect)
                    .SetPageNumber(1)
                    .SetFieldName("sig");
    
            IExternalSignature signature = new ServerSignature();
    
            signer.SignDetached(signature, chain, null, null, null, 0, subfilter);
        }
    
        public class ServerSignature : IExternalSignature
        {
            public string GetDigestAlgorithmName()
            {
                return DigestAlgorithms.SHA256;
            }
    
            public string GetSignatureAlgorithmName()
            {
                return "RSA";
            }
    
            public ISignatureMechanismParams GetSignatureMechanismParameters()
            {
                return null;
            }
    
            public byte[] Sign(byte[] message)
            {
                HashPDF json = new HashPDF
                {
                    orderRequestId = 444928,
                    hash = new List<HashItem>()
                };
    
                HashItem unHash = new HashItem
                {
                    hash = Convert.ToBase64String(message)
                };
                json.hash.Add(unHash);
                var sign = new SignatureAPI();
                HashSignatureRequestCollectDTO jsonres = sign.SignHash("444928", json).GetAwaiter().GetResult();
                return Convert.FromBase64String(jsonres.Signatures[0].Signature);
            }
        }
    }
    public class SignatureAPI
    {
        private const int _ORDER_REQUEST_ID = 444928;
        private const string _API_BASE_URL = "SIGNURL";
        private const string _SERIAL_NUMBER_CERTIFICATE = "17E1BC";
    
        public SignatureAPI()
        {
            X509Store store = new X509Store(StoreName.My);
            store.Open(OpenFlags.ReadOnly);
    
            X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindBySerialNumber, _SERIAL_NUMBER_CERTIFICATE, false);
        
            X509Certificate2 certificate = col[0];
    
            FlurlHttp.Clients.WithDefaults(builder => builder.ConfigureInnerHandler(h => { h.ClientCertificates.Add(certificate); }));
        }
    
        public async Task<HashSignatureRequestCollectDTO> SignHash(string id, HashPDF json)
        {
            string apiUrl = "SIGNHASH";
            var response = await apiUrl.SetQueryParam("orderRequestId", id).PostJsonAsync(json);
    
            var result = await response.GetStringAsync();
            HashSignatureRequestCollectDTO jsonRes = result.ConvertJsonStringWithTypeToObject<HashSignatureRequestCollectDTO>();
    
            return jsonRes;
        }
    
        public async Task<CertificateJson> GetCertificate()
        {
           string apiUrl = "GETCERT";
           return await apiUrl.SetQueryParam("orderRequestId", _ORDER_REQUEST_ID).GetJsonAsync<CertificateJson>();
        }
    }
    
    public class SignatureResponse
    {
        public int SignatureRequestId { get; set; }
        public string Status { get; set; }
        public string Hash { get; set; }
        public string Signature { get; set; }
        public string ErrorMessage { get; set; }
    }
    
    public class HashSignatureRequestCollectDTO
    {
        public int OrderRequestId { get; set; }
        public object ExternalOrderRequestId { get; set; }
        public List<SignatureResponse> Signatures { get; set; }
    }
    
    public class HashItem
    {
        public string hash { get; set; }
    }
    
    public class HashPDF
    {
        public int orderRequestId { get; set; }
        public List<HashItem> hash { get; set; }
    }

c# .net pdf itext pades
1个回答
0
投票

解密示例 PDF 中签名容器中的签名字节,发现它们不包含包装签名属性哈希的

DigestInfo
结构,而是包含签名属性本身。

因此,当您在代码中假设远程签名服务将散列并包装您发送给它的字节时,该服务实际上不会这样做,但希望您这样做。

这种差异实际上在您的

ServerSignature.Sign
实现中的这一行中很明显:

json.hash.Add(unHash);

在这里,您将一些 un-hashed 添加到集合中,根据其名称,该集合应包含一些 hashed 项目。

要解决此问题,您应该更换

HashItem unHash = new HashItem
{
    hash = Convert.ToBase64String(message)
};
json.hash.Add(unHash);

通过类似

byte[] digestValue = <<Calculate SHA-256 hash of message>>;
byte[] digestInfo = <<Wrap digestValue in a DigestInfo object>>;
HashItem digestInfoHashItem = new HashItem
{
    hash = Convert.ToBase64String(digestInfo)
};
json.hash.Add(digestInfoHashItem);

将digestValue 包装在DigestInfo 对象中,RFC 8017 提供了一条捷径:

对于附录B.1中提到的九个哈希函数,DER DigestInfo 值的编码 T 等于以下内容:

...

SHA-256:(0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.

即您只需将字节

30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
添加到您的
digestValue
字节即可。

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