使用ItextSharp验证数字签名 [英] Verify digital signature with ItextSharp

查看:359
本文介绍了使用ItextSharp验证数字签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to verify digital signatures in c# using iTextSharp.

我已经在iText网站中跟踪了该示例(

I've followed the example in the iText web (http://gitlab.itextsupport.com/itextsharp/tutorial/blob/master/signatures/chapter5/C5_03_CertificateValidation/C5_03_CertificateValidation.cs) but the results aren't what I expected. Specifically, when trying to verify a signature through OCSP or CRL, the result usually is that the signature couldn't be verified. I think that shouldn't happen, since Adobe Reader verificates the signatures ok.

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

The pdf I'm using to test the verification can be found in this link: https://blogs.adobe.com/security/SampleSignedPDFDocument.pdf

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

This is the code I'm using (short version of the example from the link above):

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

我不知道为什么无法验证签名,因为Adobe可以做到.任何想法都表示赞赏.

I don't get why the signature couldn't be verified, since Adobe can do it. Any ideas are appreciated.

推荐答案

此问题有两个方面:

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

使用来自的示例代码C5_03_CertificateValidation.cs 该OP无法验证所涉及的证书(尤其是签署者证书)是否已被撤销,签署时" 今天都没有".另一方面,我可以立即验证签名时" .

Using the sample code from C5_03_CertificateValidation.cs the OP couldn't verify that the certificates in question, in particular the signer certificate, was not revoked, neither "at the time of signing" nor "today". I, on the other hand, immediately could verify that "at the time of signing".

OP的测试与我的测试之间的主要区别在于,前者的测试是使用OP的时区UTC-3进行的,而我的则是使用UTC + 2.

The major difference between the OP's tests and my tests were that the former ones occurred using the OP's time zone UTC-3 while mine were using UTC+2.

因此,我使用不同的系统时区来运行代码,并且确实是:验证仅在UTC-01及以上的时区(即UTC-01,UTC,UTC + 01,...)成功完成了.

Thus, I ran the code using different system time zones, and indeed: The verification only succeeded in time zones UTC-01 and up, i.e. UTC-01, UTC, UTC+01, ...

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

The time returned by DateTime cal = pkcs7.SignDate in the tests turned out to be given using the current local time zone.

因此,显然,CRL验证代码不是根据当地时区使用时间,而是在某个固定时区(大概在UTC本身)中使用时间.

Apparently, therefore, the CRL validation code does not use times according to the local time zone but instead in some fixed time zone, presumably in UTC itself.

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

Thus, one can make the example code work universally by

改变

crlVerifier.Verify(signCert, issuerCert, date)

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

OP可以在测试后确认.

as the OP could confirm after tests.

OP提到他希望使用相关PKI的CRL的当前时间和当前版本来执行吊销检查.

The OP mentions that he would prefer to execute revocation checks using current time and current versions of the CRL of the PKI in question.

尽管这种方法似乎利用了最新的信息,但其用途是有限的:

While this approach appears to make use of the most up-to-date information available, its usefulness is limited:

  • 如果受审查的证书现在已超出其原始有效期(即,其有效截止日期是过去的日期),但在签署时尚未到期,则有关其可能被撤销的信息CRL上可能不再有签名.实际上,根据RFC 5280

  • If the certificate under scrutiny is beyond its original validity period now (i.e. its valid-not-after date is in the past) but was not at the time of signing, information about its possible revocation at the time of signing may not be on the CRL anymore. Indeed, according to RFC 5280

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

An entry MUST NOT be removed from the CRL until it appears on one regularly scheduled CRL issued beyond the revoked certificate's validity period.

因此,在有效期限后发布的任何版本中,可以从CRL中删除.

By implication, therefore, it may be removed from the CRL in any version issued after the validity period.

因此,使用比证书有效期末新的CRL是没有意义的.

Thus, it makes no sense to use a CRL newer than the end of the validity period of the certificate.

即使受审查的证书尚未超过其原始有效期,PKI提供程序也可能已经倒闭.在那种情况下,所有用于吊销信息的访问点现在可能仅服务于PKI仍处于活动状态时创建的CRL的最终版本,或者根本不提供.

Even if the certificate under scrutiny is not yet beyond its original validity period, the PKI provider might have gone out of business. In that case all access points for revocation information now might only serve a final version of the CRL created when the PKI was still active or none at all.

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

In that case one can use that final CRL or the newest CRL one still has available (e.g. in some CRL cache) as long as the CRL is recent enough.

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

Thus, validation policies often allow and even require the use of an older CRL as long as it is recent enough, at least the CRL's nextUpdate value must be after the signing time, and at the same time old enough, at least the CRL's thisUpdate value must be before the end of the validity period of the certificate.

但是,在检查是否可以使用较旧的CRL时,要考虑的一项签名时间是与之比较的一个时间.

One item to consider, though, is what one considers to be the signing time to compare with when checking whether an older CRL can be used.

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

The pkcs7.SignDate used in the iText example code above may merely be the content of a field in the PDF or the CMS container which might have been faked: Someone might have gotten hold of the private key; after the associated certificate has been revoked, that person might still misuse the key setting the signing time information to a date before revocation.

因此,您可以不同地确定签署日期.例如

Thus, you might determine the signing date differently. E.g.

  • 如果签名容器包含签名时间戳,或者PDF文档包含更高版本的文档时间戳,并且如果可以信任该时间戳,则可以使用该时间戳的时间;

  • if the signature container contains a signature time stamp or the PDF document contains a later document time stamp, and if that time stamp can be trusted, the time of that time stamp can be used;

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

if the signed document has been locally stored for some time (e.g. in some archiving system) and the time of storage is known and can be trusted, that time can be used;

...

这篇关于使用ItextSharp验证数字签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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