我在使用受信任的时间戳通过 Bouncy CaSTLe 创建有效的 CMS 签名时遇到问题。签名创建效果很好(我想将签名包含到 PDF 文件中),签名有效。但是在我将受信任的时间戳包含到签名的未签名属性表中之后,签名仍然有效,但 Reader 报告说签名包含嵌入的时间戳,但它是无效的。这让我相信,我加时间戳的散列不是正确的,但我似乎无法弄清楚它有什么问题。
签名代码:
Store store = new JcaCertStore(Arrays.asList(certContainer.getChain()));
CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator();
JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA1withRSA");
signedDataGenerator.addSignerInfoGenerator(
infoGeneratorBuilder.build(contentSignerBuilder.build(certContainer.getPrivateKey()), (X509Certificate)certContainer.getSignatureCertificate()));
signedDataGenerator.addCertificates(store);
CMSTypedData cmsData = new CMSProcessableByteArray(data);
signedData = signedDataGenerator.generate(cmsData, false);
Collection<SignerInformation> ss = signedData.getSignerInfos().getSigners();
SignerInformation si = ss.iterator().next(); // get first signer (should be only one)
ASN1EncodableVector timestampVector = new ASN1EncodableVector();
Attribute token = createTSToken(si.getSignature());
timestampVector.add(token);
AttributeTable at = new AttributeTable(timestampVector);
si = SignerInformation.replaceUnsignedAttributes(si, at);
ss.clear();
ss.add(si);
SignerInformationStore newSignerStore = new SignerInformationStore(ss);
CMSSignedData newSignedData = CMSSignedData.replaceSigners(signedData, newSignerStore);
createTSToken
代码:
public Attribute createTSToken(byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException, IOException {
// Generate timestamp
MessageDigest digest = MessageDigest.getInstance("SHA1", "BC");
TimeStampResponse response = timestampData(digest.digest(data));
TimeStampToken timestampToken = response.getTimeStampToken();
// Create timestamp attribute
Attribute a = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, new DERSet(ASN1Primitive.fromByteArray(timestampToken.getEncoded())));
return a;
}
时间戳数据
:
TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator();
TimeStampRequest req = reqgen.generate(TSPAlgorithms.SHA1, data);
byte request[] = req.getEncoded();
URL url = new URL("http://time.certum.pl");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-type", "application/timestamp-query");
con.setRequestProperty("Content-length", String.valueOf(request.length));
OutputStream out = con.getOutputStream();
out.write(request);
out.flush();
if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("Received HTTP error: " + con.getResponseCode() + " - " + con.getResponseMessage());
}
InputStream in = con.getInputStream();
TimeStampResp resp = TimeStampResp.getInstance(new ASN1InputStream(in).readObject());
response = new TimeStampResponse(resp);
response.validate(req);
if(response.getStatus() != 0) {
System.out.println(response.getStatusString());
return null;
}
return response;
感谢您的帮助!
示例文件:
Signed PDF with LTV - 编辑
最佳答案
signed_lipsum.pdf,第一个版本
时间戳 token 引用作为签名者一些
CN=e-Szigno Test TSA2,OU=e-Szigno CA,O=Microsec Ltd.,L=Budapest,C=HU
发布者
CN=Microsec e-Szigno Test Root CA 2008,OU=e-Szigno CA,O=Microsec Ltd.,L=Budapest,C=HU
序列号为 7.
不过,它本身并不提供此证书,封装签名 CMS 容器和某些验证相关信息 PDF 文档部分也未提供此证书。
因此,至少在我的计算机上没有机会以任何方式验证时间戳记号,Adobe Reader 不接受时间戳是完全正确的。
您是否以适合您的 Adobe Reader 的方式在您的计算机上提供了相关证书?如果您有但仍然无法正常工作,请提供它以进行进一步测试。如果没有,请尝试检索并提供它们。
您可能希望增强时间戳记号本身以在将其包含到签名中之前包含该证书。
signed_lipsum.pdf,第二版
在更新的文件 signed_lipsum.pdf 中,签名时间戳包含 TSA 证书,但它是错误的!
就像在第一个版本中一样,时间戳引用签名者证书
- 主题 CN=e-Szigno 测试 TSA2,OU=e-Szigno CA,O=Microsec Ltd.,L=布达佩斯,C=HU
- 颁发者 CN=Microsec e-Szigno Test Root CA 2008,OU=e-Szigno CA,O=Microsec Ltd.,L=Budapest,C=HU
- 序列号 7.
另一方面,包含的证书具有
- 主题 CN=e-Szigno 测试 TSA2,OU=e-Szigno CA,O=Microsec Ltd.,L=布达佩斯,C=HU
- 颁发者 CN=Microsec e-Szigno Test Root CA 2008,OU=e-Szigno CA,O=Microsec Ltd.,L=Budapest,C=HU
- 序列号 5.
我假设测试 TSA 使用多个签名设备/软 token 和单独的证书,并且 OP 包含错误的证书。
因此,您可能希望包含正确的证书。
顺便说一句,iText 签名的 PDF 中的时间戳包含与时间戳中的引用相匹配的证书...
RFC 3161 时间戳请求可以要求 TSA 自动包含签名者证书。 Bouncy CaSTLe 允许像这样设置这个标志:
TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator();
reqgen.setCertReq(true); // <<<<<<<<<<<<<<<<<<<<<<<<<<
TimeStampRequest req = reqgen.generate(TSPAlgorithms.SHA1, data);
您可以试试这个,而不是自己包含证书。
启用 LTV
来自评论:
Just out of curiosity, what extra needs to be added to make a PDF LTV enabled?
引用 Leonard Rosenthol(Adobe 的 PDF 大师)的话:
LTV enabled means that all information necessary to validate the file (minus root certs) is contained within. So this statement [...] would be true.
the PDF is signed correctly and contains all necessary certificates, a valid CRL or OSCP response for every certificate
关于java - Java中带有时间戳的数字签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21849187/