【问题标题】:Verify digital signature with ItextSharp使用 ItextSharp 验证数字签名
【发布时间】:2017-09-27 15:48:54
【问题描述】:

我正在尝试使用 iTextSharp 在 c# 中验证数字签名。

我已按照 iText 网络 (http://gitlab.itextsupport.com/itextsharp/tutorial/blob/master/signatures/chapter5/C5_03_CertificateValidation/C5_03_CertificateValidation.cs) 中的示例进行操作,但结果并非我所期望的。具体来说,当尝试通过 OCSP 或 CRL 验证签名时,结果通常是无法验证签名。我认为这不应该发生,因为 Adob​​e Reader 可以验证签名。

我用来测试验证的 pdf 可以在这个链接中找到:https://blogs.adobe.com/security/SampleSignedPDFDocument.pdf

这是我正在使用的代码(上面链接中示例的简短版本):

static void Main(String[] args)
{
    LoggerFactory.GetInstance().SetLogger(new SysoLogger());
    C5_03_CertificateValidation app = new C5_03_CertificateValidation();
    app.VerifySignatures(EXAMPLE); //Pdf file I'm using to test the verification
}

public void VerifySignatures(String path)
{
    Console.WriteLine(path);
    PdfReader reader = new PdfReader(path);
    AcroFields fields = reader.AcroFields;
    List<String> names = fields.GetSignatureNames();
    foreach (string name in names)
    {
        Console.WriteLine("===== " + name + " =====");
        VerifySignature(fields, name);
    }
    Console.WriteLine();
}

public PdfPKCS7 VerifySignature(AcroFields fields, String name)
{
    PdfPKCS7 pkcs7 = fields.VerifySignature(name);
    X509Certificate[] certs = pkcs7.SignCertificateChain;
    DateTime cal = pkcs7.SignDate;

    X509Certificate signCert = certs[0];
    X509Certificate issuerCert = (certs.Length > 1 ? certs[1] : null);
    Console.WriteLine("=== Checking validity of the document at the time of signing ===");
    CheckRevocation(pkcs7, signCert, issuerCert, cal);
    Console.WriteLine("=== Checking validity of the document today ===");
    CheckRevocation(pkcs7, signCert, issuerCert, DateTime.Now);
    return pkcs7;
}

public static void CheckRevocation(PdfPKCS7 pkcs7, X509Certificate signCert, X509Certificate issuerCert, DateTime date)
{
    List<BasicOcspResp> ocsps = new List<BasicOcspResp>();
    if (pkcs7.Ocsp != null)
        ocsps.Add(pkcs7.Ocsp);
    OcspVerifier ocspVerifier = new OcspVerifier(null, ocsps);
    List<VerificationOK> verification =
        ocspVerifier.Verify(signCert, issuerCert, date);
    if (verification.Count == 0)
    {
        List<X509Crl> crls = new List<X509Crl>();
        if (pkcs7.CRLs != null)
            foreach (X509Crl crl in pkcs7.CRLs)
                crls.Add(crl);
        CrlVerifier crlVerifier = new CrlVerifier(null, crls);
        verification.AddRange(crlVerifier.Verify(signCert, issuerCert, date));
    }
    if (verification.Count == 0)
        Console.WriteLine("The signing certificate couldn't be verified with the example");
    else
        foreach (VerificationOK v in verification)
            Console.WriteLine(v);


    //Code not in the example, added by me
    //This way, I can find out if the certificate is revoked or not (through CRL). Not sure if it's the right way though
    if (verification.Count == 0 && pkcs7.CRLs != null && pkcs7.CRLs.Count != 0)
    {
        bool revoked = false;
        foreach (X509Crl crl in pkcs7.CRLs)
        {
            revoked = crl.IsRevoked(pkcs7.SigningCertificate);
            if (revoked)
                break;
        }

        Console.WriteLine("Is certificate revoked?: " + revoked.ToString());
    }
}

这是我得到的输出:

===== Signature2 =====

=== Checking validity of the document at the time of signing ===
i.t.p.s.OcspClientBouncyCastle INFO  Getting OCSP from http://adobe-ocsp.geotrust.com/responder
iTextSharp.text.pdf.security.OcspClientBouncyCastle ERROR Error en el servidor remoto: (502) Puerta de enlace no válida.
i.t.p.s.OcspVerifier INFO  Valid OCSPs found: 0
i.t.p.s.CrlVerifier INFO  Getting CRL from http://crl.geotrust.com/crls/adobeca1.crl
i.t.p.s.CrlVerifier INFO  Valid CRLs found: 0
The signing certificate couldnt be verified with the example
Is certificate revoked?: False

=== Checking validity of the document today ===
i.t.p.s.OcspClientBouncyCastle INFO  Getting OCSP from http://adobe-ocsp.geotrust.com/responder
iTextSharp.text.pdf.security.OcspClientBouncyCastle ERROR Error en el servidor remoto: (502) Puerta de enlace no válida.
i.t.p.s.OcspVerifier INFO  Valid OCSPs found: 0
i.t.p.s.CrlVerifier INFO  Getting CRL from http://crl.geotrust.com/crls/adobeca1.crl
i.t.p.s.CrlVerifier INFO  Valid CRLs found: 0
The signing certificate couldnt be verified with the example
Is certificate revoked?: False

我不明白为什么无法验证签名,因为 Adob​​e 可以做到。任何想法都表示赞赏。

【问题讨论】:

  • 如果您仔细查看 Adob​​e Reader 的输出,您会看到“所选证书被认为是有效的,因为它没有出现在 嵌入的证书撤销列表 (CRL) 中签名。”您的代码尝试在线检索 OCSP 响应或 CRL。
  • @mkl 我查过了,你是对的。但是签名也有 OCSP url 和 CRL url(我明白阅读输出),我不明白为什么无法使用这些验证签名。可能是网址无效吗?另外,我不明白为什么 crlVerifier 没有给出答案,但 crl.IsRevoked 给出了答案。 (顺便说一句,感谢您的快速回答)
  • “但签名也有 OCSP url 和 CRL url(我明白阅读输出),我不明白为什么无法使用这些验证签名。” - A OCSP 服务 URL 不再存在;该服务似乎已停止。这就是为什么应该将验证相关信息添加到 PDF 本身的原因。 B 您当前从该 URL 检索到的 CRL 从 2015 年 1 月 22 日开始有效。签名者证书在 2009-09-04 之后被标记为有效。因此,不能从该 CRL 中获得有关证书的可用信息。 ...
  • 嵌入到签名中的 CRL 从 2009-07-16 开始有效,即签名创建日期。因此,该 CRL 确实包含有关证书的可用信息。
  • Hhmmm,我刚刚意识到(即使它大部分是真的)上面所说的并不能解释你的测试结果。稍后我会写一些更详细的内容。

标签: c# itext digital-signature


【解决方案1】:

这个问题有两个方面:

  • 为什么 iTextSharp 似乎无法验证 Adob​​e 提供的给定示例签名?
  • 应在哪个日期使用哪个 CRL 进行撤销检查?

使用 iTextSharp 验证给定的签名

使用来自C5_03_CertificateValidation.cs 的示例代码,OP 无法验证有问题的证书,尤其是签名者证书,没有被吊销,“在签名时” 和 “今天”。另一方面,我可以立即验证“在签名时”

OP 的测试和我的测试之间的主要区别在于,前者使用 OP 的时区 UTC-3 发生,而我的测试使用 UTC+2。

因此,我使用不同的系统时区运行代码,实际上:验证仅在 UTC-01 及以上时区成功,即 UTC-01、UTC、UTC+01、...

DateTime cal = pkcs7.SignDate 在测试中返回的时间原来是使用当前本地时区给出的。

因此,显然,CRL 验证代码根据本地时区使用时间,而是使用某个固定时区,大概是 UTC 本身。

因此,可以通过以下方式使示例代码通用

变化

crlVerifier.Verify(signCert, issuerCert, date)

crlVerifier.Verify(signCert, issuerCert, date.ToUniversalTime())

因为 OP 可以在测试后确认。

为撤销检查选择正确的日期和 CRL

OP 提到他更愿意使用当前时间和当前版本的 PKI 的 CRL 执行吊销检查。

虽然这种方法似乎利用了可用的最新信息,但它的用处有限:

  • 如果受审查的证书现在已超过其原始有效期(即其有效期为过去日期),但在签署时并未出现,则在签署时有关其可能撤销的信息可能不会不再在 CRL 上。事实上,根据 RFC 5280

    不得删除条目 从 CRL 直到它出现在一个定期发布的 CRL 上 超过被吊销证书的有效期。

    因此,暗示性地,它可能在有效期之后发布的任何版本中从 CRL 中删除。

    因此,使用比证书有效期结束时更新的 CRL 是没有意义的。

  • 即使受审查的证书尚未超过其原始有效期,PKI 提供商也可能已经倒闭。在这种情况下,所有撤销信息的访问点现在可能只提供在 PKI 仍处于活动状态或根本没有活动时创建的 CRL 的最终版本。

    在这种情况下,只要 CRL 足够新,就可以使用最终 CRL 或仍然可用的最新 CRL(例如,在某些 CRL 缓存中)。

因此,验证策略通常允许甚至要求使用较旧的 CRL,只要它足够新,至少 CRL 的 nextUpdate 值必须在签名时间之后,同时又足够旧,在至少 CRL 的 thisUpdate 值必须在证书有效期结束之前。

不过,要考虑的一项是在检查是否可以使用较旧的 CRL 时,需要比较的签名时间。

上面 iText 示例代码中使用的pkcs7.SignDate 可能只是 PDF 或 CMS 容器中的字段内容,可能已被伪造:有人可能已经掌握了私钥;在相关证书被吊销后,该人仍可能滥用将签名时间信息设置为吊销前日期的密钥。

因此,您可能会以不同的方式确定签署日期。例如

  • 如果签名容器中包含签名时间戳或PDF文档包含后面的文档时间戳,并且如果该时间戳可以信任,则可以使用该时间戳的时间;

  • 如果签名的文档已经在本地存储了一段时间(例如在某些归档系统中)并且存储时间已知并且可以信任,则可以使用该时间;

  • ...

【讨论】:

  • 我更愿意使用当前时间和当前版本的 CRL 执行吊销检查,因为(在我的场景中)CRL 验证程序在签名时不会找到任何有用的 CRL。验证者正在检索的 CRL 是否有可能在签名时不能用于验证签名? CRL 的“ThisUpdate”值是今天,“NextUpdate”是明天。 (我没有看到任何参数表明 CRL 从哪个日期开始有效,这就是为什么我认为我可能无法使用该 CRL 来使用旧日期进行验证)。
  • @jbac “是否有可能验证者正在检索的 CRL 在签名时不能用于验证签名?” - 确实,正如您可以阅读我的回答:关于证书的所有吊销信息都可以从任何发布的 CRL 中删除在该证书的原始有效期结束之后。因此,如果您想验证您提供的示例签名之类的签名,则必须使用较旧的 CRL。
  • @jbac 另一方面,如果您只想验证其签名者证书在验证时仍然有效的签名,您可以(并且应该)使用最新的 CRL(或请求 OCSP 响应只是及时)。但是,在这种情况下,您选择的示例 PDF 不合适... ;)
  • 是的,我的错,我指的是签名证书仍然有效的情况。抱歉,我没有弄清楚这一点,显然示例 pdf 不是这种情况。所以,如果我想为这两种情况创建一个通用的解决方案(证书可能仍然有效或无效),我应该首先检查它是否仍然有效,并且使用签名时间或今天的日期验证签名是否连续?
猜你喜欢
  • 2011-09-22
  • 2014-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-18
  • 1970-01-01
相关资源
最近更新 更多