You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用iText结合Digidentity外部服务签名PDF验证失败问题排查

PDF数字签名验证失败排查请求

我通过iText创建数字签名空容器,获取文件哈希后发送至Digidentity服务进行签名,收到字符串格式的数字签名后将其嵌入PDF的签名容器中,但在欧盟DSS验证网站无法通过验证并报错。

相关实现代码

public HashInfo[] ConvertHashToBase64(string[] filePaths, JsonData identityInfo, string uniqueId)
{
    SPWeb cWeb = SPContext.Current.Web;
    List<HashInfo> hashInfos = new List<HashInfo>();

    foreach (string filePath in filePaths)
    {
        var signInstance = new PAdESSignerClass();
        signInstance.EmptySignature(signedPath, aWeb, identityInfo, uniqueId);
        SPFile file = aWeb.GetFile(filePath);
        var fileStream = signedFile.OpenBinary();
                
        using (SHA256 sha256Hash = SHA256.Create())
        {
            byte[] hashBytes = sha256Hash.ComputeHash(fileStream);
            string base64Hash = Convert.ToBase64String(hashBytes);

            hashInfos.Add(new HashInfo
            {
                Hash = base64Hash,
                UniqueId = file.UniqueId,
                FileName = file.Name
            });
        }
    }

    return hashInfos.ToArray();
}

public class PAdESSignerClass
{
    public void EmptySignature(string containerFilePath, SPWeb web, JsonData jsonData, string guidString)
    {
        SPFile file = web.GetFile(containerFilePath);
        var cert = jsonData.cert;
        string certificate = cert.certificates[0].ToString();
        byte[] certificateBytes = Convert.FromBase64String(certificate);
        X509Certificate2 xcertificate = new X509Certificate2(certificateBytes);
        IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory();
        IX509Certificate[] iTextCertificates2 = FACTORY.CreateX509CertificateParser().ReadAllCerts(xcertificate.GetRawCertData()).ToArray();

        using (Stream fileStream = file.OpenBinaryStream())
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                iText.Kernel.Pdf.PdfReader reader = new iText.Kernel.Pdf.PdfReader(fileStream);
                PdfSigner signer = new PdfSigner(reader, memoryStream, new StampingProperties());
                signer.GetSignatureAppearance().SetCertificate(iTextCertificates2[0]);
                signer.SetFieldName(guidString);

                iText.Signatures.IExternalSignatureContainer external = new ExternalBlankSignatureContainer(iText.Kernel.Pdf.PdfName.Adobe_PPKLite,
                iText.Kernel.Pdf.PdfName.Adbe_pkcs7_detached);

                signer.SignExternalContainer(external, 8192);

                byte[] pdfBytes = memoryStream.ToArray();
                web.Files.Add(containerFilePath, pdfBytes, true);
            }
        }
    }

    public void AddSignatureToPdf(string certPath, JsonData jsonData, string containerFilePath, byte[]  signatureHash, SPWeb web, string uniqueId, string timestamp)
    {
        var cert = jsonData.cert;
        string certificate = cert.certificates[0].ToString();
        byte[] certificateBytes = Convert.FromBase64String(certificate);

        SPFile file = web.GetFile(containerFilePath);

        using (Stream fileStream = file.OpenBinaryStream())
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                iText.Kernel.Pdf.PdfReader pdfDoc = new iText.Kernel.Pdf.PdfReader(fileStream);
                PdfSigner signer = new PdfSigner(pdfDoc, memoryStream, new iText.Kernel.Pdf.StampingProperties());

                X509Certificate2 xcertificate = new X509Certificate2(certificateBytes);
                   
                IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory();
                IX509Certificate[] iTextCertificates2 = FACTORY.CreateX509CertificateParser().ReadAllCerts(xcertificate.GetRawCertData()).ToArray();
                IPrivateKey iTextPrivateKey = FACTORY.CreateRsa2048KeyPairGenerator().GenerateKeyPair().GetPrivateKey();

                signer.GetSignatureAppearance();

                iText.Signatures.IExternalSignatureContainer external = new MyExternalSignatureContainer(iTextPrivateKey, iTextCertificates2, signatureHash, certificateBytes, timestamp);

                PdfSigner.SignDeferred(signer.GetDocument(), uniqueId, memoryStream, external);
        
                byte[] signedBytes = memoryStream.ToArray();
                web.Files.Add(containerFilePath, signedBytes, true);
            }
        }
    }
}

public class MyExternalSignatureContainer : iText.Signatures.IExternalSignatureContainer
{
    private readonly byte[] signedBytes;
    protected IPrivateKey pk;
    protected IX509Certificate[] chain;
    protected byte[] signatureHash;
    protected byte[] certificateBytes;
    protected string timestamp;

    public MyExternalSignatureContainer(IPrivateKey pk, IX509Certificate[] chain, byte[]signatureHash, byte[] certificateBytes, string timestamp)
    {
        this.pk = pk;
        this.chain = chain;
        this.signatureHash = signatureHash;
        this.certificateBytes = certificateBytes;
        this.timestamp = timestamp;
    }

    public void ModifySigningDictionary(iText.Kernel.Pdf.PdfDictionary signDic)
    {
    }

    public byte[] Sign(Stream inputStream)
    {
        byte[] encodedSig = null;
        iText.Signatures.PrivateKeySignature signature = new iText.Signatures.PrivateKeySignature(pk, "SHA256");
        string digestAlgorithmName = signature.GetDigestAlgorithmName();
      
        iText.Signatures.PdfPKCS7 sgn = new iText.Signatures.PdfPKCS7(null, chain, digestAlgorithmName, false);

        byte[] hash = iText.Signatures.DigestAlgorithms.Digest(inputStream, digestAlgorithmName);
        byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
        byte[] extSignature = signature.Sign(sh);
        sgn.SetExternalSignatureValue(signatureHash, null, signature.GetSignatureAlgorithmName());

        encodedSig = sgn.GetEncodedPKCS7(hash, PdfSigner.CryptoStandard.CMS, null, null, null);

        return encodedSig;
    }
}

Digidentity返回的身份信息示例

{
  "key": {
    "status": "enabled",
    "algo": [
      "1.2.840.113549.1.1.11"
    ],
    "len": 2048
  },
  "cert": {
    "status": "valid",
    "certificates": [
      "MIIG1TCCBL2gAwIBAgI...V2yYRGjwk/GQsq67A=="
    ],
    "issuerDN": "C=NL,O=Digidentity B.V.,organizationIdentifier=NTRNL-27322631,CN=TEST Digidentity Personal Qualified CA",
    "serialNumber": "7867EX1A9M6PLE3A8C4K6751Q05ADD3C",
    "subjectDN": "C=NL,SN=Doe,GN=John,serialNumber=4exa3441-m601-44p6-9416-ls695cid3775,CN=John Doe",
    "validFrom": "20220209095106Z",
    "validTo": "20230209095106Z"
  },
  "authMode": "implicit",
  "multisign": 1,
  "lang": "en-US",
  "SCAL": "2"
}

更新内容(2024年5月24日)

若无需创建空签名容器,是否也不应使用SignDeferred方法?我当前获取哈希的流程如下:

string digestAlgorithmName = signature.GetDigestAlgorithmName();
byte[] hash = iText.Signatures.DigestAlgorithms.Digest(inputStream, digestAlgorithmName);
byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);

我使用该sh为CSC API生成哈希:

using (SHA256 sha256Hash = SHA256.Create())
{
    byte[] hashBytes = sha256Hash.ComputeHash(sh);
    string base64Hash = Convert.ToBase64String(hashBytes);
    hashInfos.Add(new HashInfo
    {
        Hash = base64Hash,
        UniqueId = file.UniqueId,
        FileName = file.Name
    });
}

之后执行:

byte[] extSignature = signature.Sign(sh);

并设置从Digidentity获取的签名:

sgn.SetExternalSignatureValue(signatureHash, null, signature.GetSignatureAlgorithmName());

请求协助排查PDF签名验证失败的问题。


内容的提问来源于stack exchange,提问作者Newbie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 11:48:09