我尝试使用iTextSharp验证c#中的数字签名。
我已经关注了iText网站(http://gitlab.itextsupport.com/itextsharp/tutorial/blob/master/signatures/chapter5/C5_03_CertificateValidation/C5_03_CertificateValidation.cs)中的示例,但结果并非我的预期。具体来说,当尝试通过OCSP或CRL验证签名时,结果通常是签名无法验证。我认为不应该发生这种情况,因为Adobe 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
我不明白为什么签名无法验证,因为Adobe可以做到。任何想法都表示赞赏。
答案 0 :(得分:0)
这个问题有两个方面:
使用来自C5_03_CertificateValidation.cs的示例代码,OP无法验证相关证书,特别是签名者证书是否未被撤销,“在签署时”也不是“今天”。另一方面,我可以在签署时立即验证“。
OP的测试与我的测试之间的主要区别在于前者使用OP的时区UTC-3,而我的使用UTC + 2。
因此,我使用不同的系统时区运行代码,实际上:验证仅在UTC-01及以上时区成功,即UTC-01,UTC,UTC + 01,......
DateTime cal = pkcs7.SignDate
在测试中返回的时间结果是使用当前的本地时区给出的。
因此,显然,CRL验证码根据本地时区不使用时间,而是在某个固定时区,大概是UTC本身。
因此,可以通过
使示例代码普遍起作用OP可以在测试后确认。改变
crlVerifier.Verify(signCert, issuerCert, date)
到
crlVerifier.Verify(signCert, issuerCert, date.ToUniversalTime())
OP提到他更愿意使用当前时间和当前版本的PKI CRL执行撤销检查。
虽然这种方法似乎利用了最新的可用信息,但它的用处有限:
如果受审查的证书现在超出其原始有效期(即其有效 - 不是在过去的日期之后),但在签署时尚未签署,则有关其可能的撤销时间的信息。签名可能不再在CRL上。实际上,根据RFC 5280
不得删除条目 从CRL直到它出现在一个定期安排的CRL上 超出撤销证书的有效期。
因此,在有效期之后,可以从中删除 。
因此,使用比证书有效期结束时更新的CRL是没有意义的。
即使受到审查的证书尚未超出其原始有效期,PKI提供商也可能已经停业。在这种情况下,撤销信息的所有访问点现在可能只服务于PKI仍处于活动状态或根本不存在时创建的CRL的最终版本。
在这种情况下,只要CRL足够新,就可以使用最终的CRL或最新的CRL(例如在某些CRL缓存中)。
因此,验证策略通常允许甚至要求使用较旧的CRL,只要它足够近,至少CRL的nextUpdate
值必须在签名时间之后,并且同时足够老,至少CRL的thisUpdate
值必须在证书有效期结束之前。
但是,需要考虑的一个问题是,在检查是否可以使用较旧的CRL时,需要考虑的是与签名时间进行比较。
上面的iText示例代码中使用的pkcs7.SignDate
可能只是PDF或CMS容器中可能已伪造的字段的内容:某人可能已经掌握了私钥;在相关证书被撤销后,该人可能仍会滥用将签名时间信息设置为撤销之前的日期的密钥。
因此,您可能会以不同方式确定签署日期。 E.g。
如果签名容器包含签名时间戳或PDF文档包含更晚的文档时间戳,并且该时间戳可以信任,则可以使用该时间戳的时间;
< / LI>如果签名文档已在本地存储一段时间(例如在某些存档系统中)并且存储时间已知且可以信任,则可以使用该时间;
...