iTextSharp中的Pades LTV验证会引发公钥,而不是根CA证书的证书签名 [英] Pades LTV verification in iTextSharp throws Public key presented not for certificate signature for root CA certificate

查看:321
本文介绍了iTextSharp中的Pades LTV验证会引发公钥,而不是根CA证书的证书签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在验证带有pdf的pdf时收到 Org.BouncyCastle.Security.InvalidKeyException ,并显示错误消息公开密钥未提供证书签名 LtvVerifier

I'm getting an Org.BouncyCastle.Security.InvalidKeyException with error message Public key presented not for certificate signature when validating a pdf with LtvVerifier.

绕过CRL LDAP URI的问题。用于执行验证的代码与上一篇文章相同:

This problem has arisen after circumventing an issue with CRL LDAP URIs. The code used to perform the verification is the same as the previous post:

   public static bool Validate(byte[] pdfIn, X509Certificate2 cert)
    {
        using (var reader = new PdfReader(pdfIn))
        {
            var fields = reader.AcroFields;
            var signames = fields.GetSignatureNames();

            if (!signames.Any(n => fields.SignatureCoversWholeDocument(n)))
                throw new Exception("None signature covers all document");

            var verifications = signames.Select(n => fields.VerifySignature(n));

            var invalidSignature = verifications.Where(v => !v.Verify());
            var invalidTimeStamp = verifications.Where(v => !v.VerifyTimestampImprint());

            if (invalidSignature.Any())
                throw new Exception("Invalid signature found");
        }

        using (var reader = new PdfReader(pdfIn))
        {
            var ltvVerifier = new LtvVerifier(reader)
            {
                OnlineCheckingAllowed = false,
                CertificateOption = LtvVerification.CertificateOption.WHOLE_CHAIN,
                Certificates = GetChain(cert).ToList(),
                VerifyRootCertificate = false,
                Verifier = new MyVerifier(null)
            };

            var ltvResult = new List<VerificationOK> { };
            ltvVerifier.Verify(ltvResult);

            if (!ltvResult.Any())
                throw new Exception("Ltv verification failed");
        }
        return true;
   }

从证书链构建X509Certificates列表的辅助功能:

Auxiliary function that builds a List of X509Certificates from the certificate chain:

    private static X509.X509Certificate[] GetChain(X509Certificate2 myCert)
    {
        var x509Chain = new X509Chain();
        x509Chain.Build(myCert);

        var chain = new List<X509.X509Certificate>();
        foreach(var cert in x509Chain.ChainElements)
        {
            chain.Add(
                DotNetUtilities.FromX509Certificate(cert.Certificate)
                );
        }

        return chain.ToArray();
    }

自定义验证程序,仅从样本中复制:

A custom verifier, just copied from sample:

 class MyVerifier : CertificateVerifier
{
    public MyVerifier(CertificateVerifier verifier) : base(verifier) { }

    override public List<VerificationOK> Verify(
        X509.X509Certificate signCert, X509.X509Certificate issuerCert, DateTime signDate)
    {
        Console.WriteLine(signCert.SubjectDN + ": ALL VERIFICATIONS DONE");
        return new List<VerificationOK>();
    }
}

我已重新实施 LtvVerifier CrlVerifier ,如上一个问题所述。 CRL验证完成。

I have re-implemented LtvVerifier and CrlVerifier as explained in the previous question. The CRL validation is done ok.

证书链包括用于签署PDF和CA根证书的证书。函数 CrlVerifier.Verify 在调用下一行时抛出上述异常:

The certificate chain includes the certificate that was used to sign the PDF and the CA root certificate. The function CrlVerifier.Verify is throwing the mentioned exception when calling the next line:

 if (verifier != null)
                result.AddRange(verifier.Verify(signCert, issuerCert, signDate));
            // verify using the previous verifier in the chain (if any)
            return result;

这是的相关堆栈跟踪Org.BouncyCastle.Security.InvalidKeyException

   in Org.BouncyCastle.X509.X509Certificate.CheckSignature(AsymmetricKeyParameter publicKey, ISigner signature)
   in Org.BouncyCastle.X509.X509Certificate.Verify(AsymmetricKeyParameter key)
   in iTextSharp.text.pdf.security.CertificateVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
   in iTextSharp.text.pdf.security.RootStoreVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
   in PdfCommon.CrlVerifierSkippingLdap.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) in c:\Projects\digit\Fuentes\PdfCommon\CrlVerifierSkippingLdap.cs:line 76
   in iTextSharp.text.pdf.security.OcspVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate)
   in PdfCommon.LtvVerifierSkippingLdap.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime sigDate) in c:\Projects\digit\Fuentes\PdfCommon\LtvVerifierSkippingLdap.cs:line 221
   in PdfCommon.LtvVerifierSkippingLdap.VerifySignature() in c:\Projects\digit\Fuentes\PdfCommon\LtvVerifierSkippingLdap.cs:line 148
   in PdfCommon.LtvVerifierSkippingLdap.Verify(List`1 result) in c:\Projects\digit\Fuentes\PdfCommon\LtvVerifierSkippingLdap.cs:line 112

并且指向我尝试验证的pdf的链接

推荐答案

经过一些调试后发现

iText(夏普)5.5。 10 LtvVerifier 在验证证书链未结尾的证书时以观察到的方式失败精灵签名证书。

iText(Sharp) 5.5.10 LtvVerifier fails in the observed manner when verifying certificates with certificate chains not ending in a self-signed certificate.

原因很简单: LtvVerifier 建立一系列 Verifier 个实例( OcspVerifier CrlVerifier RootStoreVerifier CertificateVerifier ;最后一个通过基类调用链接的)。然后它请求相关签名的签名证书的证书链,并且对于链的每个证书,调用由该证书及其颁发者组成的证书对的 Verifier 序列。 ;如果链中的最终证书, null 将作为发行人证书转发。

The reason is pretty simple: LtvVerifier establishes a sequence of Verifier instances (OcspVerifier, CrlVerifier, RootStoreVerifier, CertificateVerifier; the last one chained via base class calls). Then it requests the certificate chain of the signing certificate of the signature in question and for each certificate of the chain calls the Verifier sequence for the certificate couple consisting of this certificate and its issuer; in case of the final certificate in the chain, null is forwarded as issuer certificate.

不幸的是最终的 Verifier CertificateVerifier ,假设 null 颁发者证书要验证的证书是自签名的:

Unfortunately the final Verifier, the CertificateVerifier, assumes in case of a null issuer certificate that the certificate to verify is self-signed:

// Check if the signature is valid
if (issuerCert != null) {
    signCert.Verify(issuerCert.GetPublicKey());
}
// Also in case, the certificate is self-signed
else {
    signCert.Verify(signCert.GetPublicKey());
} 

(来自 CertificateVerifier 方法验证

如果证书链是 LtvVerifier最初请求的不以自签名证书结束,最终测试正确导致观察到的

If the certificate chain the LtvVerifier initially requested does not end in a self-signed certificate, that final test correctly results in the observed


Org.BouncyCastle.Security.InvalidKeyException ,错误消息公开密钥不是证书签名



OP的例子



在手头的情况下,我们有一个由

The OP's example

In the case at hand we have a document timestamp issued by

cn = AUTORIDAD DE SELLADO DE TIEMPO FNMT-RCM,ou = CERES,o = FNMT-RCM,c = ES

cn=AUTORIDAD DE SELLADO DE TIEMPO FNMT-RCM, ou=CERES, o=FNMT-RCM, c=ES

cn =ACAdministraciónPública,serialNumber = Q2826004J,ou = CERES,o = FNMT-RCM,c = ES

cn=AC Administración Pública, serialNumber=Q2826004J, ou=CERES, o=FNMT-RCM, c=ES

ou = AC RAIZ FNMT-RCM,o = FNMT-RCM,c = ES

ou=AC RAIZ FNMT-RCM, o=FNMT-RCM, c=ES

这是自我-signed。

which is self-signed.

在这种情况下已经是i ntermediary证书,ACAdministraciónPública,在欧洲可信赖名单上(参见西班牙的TL经理,信任服务提供商,Fábrica Nacional de Moneda y Timbre - Real Casa de la Moneda(FNMT-RCM),Trust Service,Certificados reconocidos para su uso enelámbitode...,Digital Identity。

In this case already the intermediary certificate, AC Administración Pública, is on the European trusted list (cf. the TL manager for Spain, "Trust Service Provider", "Fábrica Nacional de Moneda y Timbre - Real Casa de la Moneda (FNMT-RCM)", "Trust Service", "Certificados reconocidos para su uso en el ámbito de... ", "Digital Identity").

因此,建立信任不需要超过前两个证书,不需要自签名根证书。因此,只有前两个证书嵌入时间戳并作为证书链返回到 LtvVerifier ,而不是自签名根。

Thus, one does not need more than the first two certificates to establish trust, the self signed root certificate is not needed. As a consequence only these first two certificates are embedded in the time stamp and returned as certificate chain to the LtvVerifier, not the self signed root.

结果是 LtvVerifier 中观察到的错误。

The result is the observed error in the LtvVerifier.

好吧,因为我们已经开始在上一个问题,更改它们应该是一个选项。

Well, as we already started creating our own copies of the involved classes in the previous question, changing them a bit more should be an option.

在这种情况下,还应该更改 RootStoreVerifier 。其验证方法如下所示:

In this case one should additionally change the RootStoreVerifier. Its Verify method looks like this:

override public List<VerificationOK> Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) {
    LOGGER.Info("Root store verification: " + signCert.SubjectDN);
    // verify using the CertificateVerifier if root store is missing
    if (certificates == null)
        return base.Verify(signCert, issuerCert, signDate);
    try {
        List<VerificationOK> result = new List<VerificationOK>();
        // loop over the trusted anchors in the root store
        foreach (X509Certificate anchor in certificates) {
            try {
                signCert.Verify(anchor.GetPublicKey());
                LOGGER.Info("Certificate verified against root store");
                result.Add(new VerificationOK(signCert, this, "Certificate verified against root store."));
                result.AddRange(base.Verify(signCert, issuerCert, signDate));
                return result;
            } catch (GeneralSecurityException) {}
        }
        result.AddRange(base.Verify(signCert, issuerCert, signDate));
        return result;
    } catch (GeneralSecurityException) {
        return base.Verify(signCert, issuerCert, signDate);
    }
}

我们只需删除标记的行

                signCert.Verify(anchor.GetPublicKey());
                LOGGER.Info("Certificate verified against root store");
                result.Add(new VerificationOK(signCert, this, "Certificate verified against root store."));
                // vvv remove
                result.AddRange(base.Verify(signCert, issuerCert, signDate));
                // ^^^ remove
                return result;

。由于我们刚刚确定证书 signCert 由信任锚签名,因此不需要 base.Verify 无论如何。

in the inner try block. As we here have just established that the certificate signCert is signed by a trust anchor, there is no need for the base.Verify anyways.

这篇关于iTextSharp中的Pades LTV验证会引发公钥,而不是根CA证书的证书签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆