我使用iText 5.5.3签署PDF文档。我需要将这些文档加上时间戳并启用LTV。我按照说明使用了addLtv方法(代码示例5.9,Lowagie白皮书中的第137页)。我得到一个带有2个签名的PDF,这是正常的:第一个是我自己的签名,第二个是文档级时间戳。
但是,Acrobat告诉我我的签名是启用了LTV,但时间戳签名不是:
Image from Acrobat Pro XI http://img15.hostingpics.net/pics/727285so2.jpg
这是因为时间戳证书的吊销信息未嵌入文档中:
Missing revocation info 1 http://img15.hostingpics.net/pics/491507so2a.jpg
Missing revocation info 2 http://img15.hostingpics.net/pics/312720so2b.jpg
根据我的理解,addLtv方法应该获取所需的所有吊销信息并将其嵌入到文档中。这是正确的,还是我必须手动"获取并嵌入这些信息?
答案 0 :(得分:9)
这是这个问题的示例代码:
public void addLtv(String src, String dest, OcspClient ocsp, CrlClient crl, TSAClient tsa) throws IOException, DocumentException, GeneralSecurityException
{
PdfReader r = new PdfReader(src);
FileOutputStream fos = new FileOutputStream(dest);
PdfStamper stp = PdfStamper.createSignature(r, fos, '\0', null, true);
LtvVerification v = stp.getLtvVerification();
AcroFields fields = stp.getAcroFields();
List<String> names = fields.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = fields.verifySignature(sigName);
if (pkcs7.isTsp())
{
v.addVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
else
{
for (String name : names)
{
v.addVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
}
PdfSignatureAppearance sap = stp.getSignatureAppearance();
LtvTimestamp.timestamp(sap, tsa, null);
}
此代码标识PDF最近填充的签名字段,并检查它是文档时间戳还是通常的签名。
如果是文档时间戳,则代码仅为此文档时间戳添加验证信息。否则,代码会为所有签名添加验证信息。
(这背后的假设工作流程是文档首先被签署(用于认证和/或批准),然后文档进入LTV循环,添加验证信息和文档时间戳,但不再有通常的签名。您的工作流程可能会有所不同,因此也可能是您的程序逻辑。)
只有在完成所有这些操作后,才会添加新的文档时间戳。
对于最终添加的时间戳,没有明确地向PDF添加验证信息(如果来自相同TSA的文档时间戳已经连续应用,则可以应用先前时间戳包括的验证信息)。这就是为什么Adobe Reader / Acrobat通常不会考虑启用此文档时间戳LTV。
如果您还需要此最终文档时间戳的验证信息,只需将此方法(与上述方法相同,但不添加文档时间戳)应用于包含文档时间戳的文件:
public void addLtvNoTS(String src, String dest, OcspClient ocsp, CrlClient crl) throws IOException, DocumentException, GeneralSecurityException
{
PdfReader r = new PdfReader(src);
FileOutputStream fos = new FileOutputStream(dest);
PdfStamper stp = new PdfStamper(r, fos, '\0', true);
LtvVerification v = stp.getLtvVerification();
AcroFields fields = stp.getAcroFields();
List<String> names = fields.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = fields.verifySignature(sigName);
if (pkcs7.isTsp())
{
v.addVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
else
{
for (String name : names)
{
v.addVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
}
stp.close();
}
iText addLtv
示例不(必然)创建支持LTV的PDF的原因在于它更接近ETSI在PAdES规范中提出的LTV的最佳实践,而不是Adobe的LTV的最佳实践。
根据ETSI TS 102 778-4 V1.1.2(2009-12),应用LTV的PDF文档的结构如图2所示。
通过添加更多DSS信息以验证上一个上一个文档的时间戳以及新文档的时间戳,可以进一步延长保护的生命周期。如图3所示。
另一方面,根据Adobe (as written by their PDF evangelist Leonard Rosenthol on the iText mailing list in January 2013),
启用LTV意味着验证文件所需的所有信息 (减去根证书)包含在。所以这个你的陈述会 是真的。
PDF已正确签名并包含所有必要的证书, 每个证书的有效CRL或OSCP响应
但是,因为该陈述的唯一方法是真实存在 对于DSS,您必须具有启用LTV的DSS才能显示。 没有时间戳 (常规或文件级别)是必需的。
由于这种分歧,根据ETSI的LTV PDF文档通常由Adobe软件提供,以便有一个不支持LTV的文档时间戳。
另见
答案 1 :(得分:0)
我所做的是通过请求两个时间戳(使用第一个提取 LTV 数据并更新 DSS,使用第二个实际为文档添加时间戳)在为文档添加时间戳之前嵌入时间戳的 LTV 数据:
从 tsa 令牌中提取签名证书:
IDigest messageDigest = tsaClient.GetMessageDigest();
byte[] tsImprint = new byte[messageDigest.GetDigestSize()];
messageDigest.DoFinal(tsImprint, 0);
byte[] tsToken;
try {
tsToken = tsaClient.GetTimeStampToken(tsImprint);
} catch(Exception e) {
throw new GeneralSecurityException(e.Message);
}
Asn1Sequence asn1Seq = Asn1Sequence.GetInstance(tsToken);
ContentInfo sigData = ContentInfo.GetInstance(asn1Seq);
TimeStampToken token = new TimeStampToken(sigData);
IX509Store tokenCerts = token.GetCertificates("COLLECTION");
List<X509Certificate> signingCerts = new List<X509Certificate>();
foreach(X509Certificate cert in tokenCerts.GetMatches(token.SignerID)) {
signingCerts.Add(cert);
}
// now perform LTV steps for signingCerts[0] ...