我正在尝试签署带有时间戳和LTV的pdf,以便在Adobe Reader中显示如下:
在英语中,它表示“签名包含嵌入式时间戳”和“签名是LTV启用”。这是我正在使用的代码:
PrivateKey pk = // get pk from an encrypting certificate created using encrypting file system
Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader(src);
FileOutputStream fout = new FileOutputStream(dest);
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
ExternalSignature signature = new PrivateKeySignature(pk, "SHA-512", "SunMSCAPI");
TSAClient tsc = null;
String url = // TSA URL
tsc = new TSAClientBouncyCastle(url, null, null, 4096, "SHA-512");
List<CrlClient> crlList = new ArrayList<>();
crlList.add(new CrlClientOnline(chain));
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(sap, digest, signature, chain, crlList, null, tsc, 0, CryptoStandard.CMS);
基于this answer,我需要一种方法将TSA证书的CRL提供给CrlList
,但是......我怎样才能获得TSA证书?我是否需要向TSA发出timestamp-query
请求并阅读回复,然后将其添加到CrlList
?请注意,这在MakeSignature.signDetached
调用sgn.getEncodedPKCS7
时已在 ReflectionException
Class App\Http\Controllers\LoginController does not exist
内完成。请注意,我使用的是免费的TSA服务器。
更新
由于它是一个免费的TSA服务器,我只需在Adobe受信任的证书中添加TSA服务器证书,现在它可以正常工作。 但是,我已经使用智能卡进行了另一项测试来签署文档,这就是我得到的内容(我已将根证书添加到Adobe的可信证书中):
签名详情:
签署证书详细信息:
基于this link,启用LTV意味着验证文件所需的所有信息(减去根证书)都包含在PDF中。因此,如果PDF正确签名并且包含所有必需的证书以及每个证书的有效CRL或OSCP响应,并且如果它包括CRL和OCSP上的签名,而不仅仅是签名证书,则PDF将启用LTV。看起来我得到了所有这些要求,或者我错过了什么?如果是这样,我怎么知道获得LTV启用pdf的缺失是什么?
答案 0 :(得分:1)
首先,基于@mkl评论,我将TSA服务器证书添加到Adobe受信任证书,以便消息
签名包含嵌入式时间戳,但不可能 验证
成了
签名包含嵌入的时间戳
并解决
签名未启用LTV,将在(...)
之后过期
我可以注意到使用
List<CrlClient> crlList = new ArrayList<>();
crlList.add(new CrlClientOnline(chain));
有一些CRL(某些证书有多个分发点)未被添加 - 使pdf LTV未启用。为了解决这个问题,我已经这样做了:
// long term validation (LTV)
List<CrlClient> crlList = new ArrayList<>();
for(Certificate cert : chain) {
X509Certificate c = (X509Certificate)cert;
List<String> crls = this.getCrlDistributionPoints(c);
if(crls != null && !crls.isEmpty()) {
crlList.add(new CrlClientOnline(crls.toArray(new String[crls.size()])));
}
}
private List<String> getCrlDistributionPoints(final X509Certificate cert) throws Exception {
final byte[] crldpExt = cert.getExtensionValue(X509Extension.cRLDistributionPoints.getId());
if (crldpExt == null) {
final List<String> emptyList = new ArrayList<String>();
return emptyList;
}
ASN1InputStream oAsnInStream = null;
ASN1InputStream oAsnInStream2 = null;
List<String> crlUrls = new ArrayList<String>();
try {
oAsnInStream = new ASN1InputStream(new ByteArrayInputStream(crldpExt));
final ASN1Object derObjCrlDP = oAsnInStream.readObject();
final DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP;
final byte[] crldpExtOctets = dosCrlDP.getOctets();
oAsnInStream2 = new ASN1InputStream(new ByteArrayInputStream(crldpExtOctets));
final ASN1Object derObj2 = oAsnInStream2.readObject();
final CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2);
for (final DistributionPoint dp : distPoint.getDistributionPoints()) {
final DistributionPointName dpn = dp.getDistributionPoint();
// Look for URIs in fullName
if (dpn != null) {
if (dpn.getType() == DistributionPointName.FULL_NAME) {
final GeneralName[] genNames = GeneralNames.getInstance(dpn.getName()).getNames();
// Look for an URI
for (int j = 0; j < genNames.length; j++) {
if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) {
final String url = DERIA5String.getInstance(genNames[j].getName()).getString();
crlUrls.add(url);
}
}
}
}
}
} catch(IOException e) {
throw new Exception(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(oAsnInStream);
IOUtils.closeQuietly(oAsnInStream2);
}
return crlUrls;
}