我有一个有效的 PKCS7 文件加载到 CMSSignedData 对象中。此 PKCS7 文件包括纯文本消息和附加的有效数字签名(都在同一个文件中)。
现在我想给这个文件加上时间戳。这是我正在使用的代码(source):
private static CMSSignedData addTimestamp(CMSSignedData signedData)
throws Exception {
Collection ss = signedData.getSignerInfos().getSigners();
SignerInformation si = (SignerInformation) ss.iterator().next();
TimeStampToken tok = getTimeStampToken();
ASN1InputStream asn1InputStream = new ASN1InputStream
(tok.getEncoded());
DERObject tstDER = asn1InputStream.readObject();
DERSet ds = new DERSet(tstDER);
Attribute a = new Attribute(new
DERObjectIdentifier("1.2.840.113549.1.9.16.2.14"), ds);
DEREncodableVector dv = new DEREncodableVector();
dv.add(a);
AttributeTable at = new AttributeTable(dv);
si = SignerInformation.replaceUnsignedAttributes(si, at);
ss.clear();
ss.add(si);
SignerInformationStore sis = new SignerInformationStore(ss);
signedData = CMSSignedData.replaceSigners(signedData, sis);
return signedData;
}
private static TimeStampToken getTimeStampToken() throws
Exception {
Security.addProvider (new
org.bouncycastle.jce.provider.BouncyCastleProvider());
PostMethod post = new PostMethod("http://My-TrustedTimeStampProvier.com");
// I'm omitting the part where I pass the user and password
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
//request TSA to return certificate
reqGen.setCertReq (true); // In my case this works
//make a TSP request this is a dummy sha1 hash (20 zero bytes)
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
byte[] enc_req = request.getEncoded();
ByteArrayInputStream bais = new ByteArrayInputStream(enc_req);
post.setRequestBody(bais);
post.setRequestContentLength (enc_req.length);
post.setRequestHeader("Content-type","application/timestamp-query");
HttpClient http_client = new HttpClient();
http_client.executeMethod(post);
InputStream in = post.getResponseBodyAsStream();
//read TSP response
TimeStampResponse resp = new TimeStampResponse (in);
resp.validate(request);
TimeStampToken tsToken = resp.getTimeStampToken();
return tsToken;
}
我可以获得一个有效的时间戳,我可以将它放入我的 CMSSignedData 对象中,并将其保存到一个文件中,将字节从 signedData.getEncoded() 写入硬盘。但是当我用第三方软件验证我的新的带时间戳的文件时,这个软件告诉原始签名是好的,但时间戳与签名不对应。该软件还可以显示原始纯文本消息。
我认为问题出在这一行:
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
我想我必须传递一个摘要而不是一个虚拟字节数组,但我不知道哪个摘要,或者我必须时间戳的正确字节是什么。我成功地可以SignerInformation
从我的signedData
. 然后我尝试reqGen.generate()
将mySignerInformation.getSignature()
. 时间戳验证失败。然后我通过了 的 Sha1 摘要mySignerInformation.getSignature()
,但我的时间戳验证再次失败。
RFC3161规范说:
2.4.1。请求格式
时间戳请求如下:
TimeStampReq ::= SEQUENCE { version INTEGER { v1(1) }, messageImprint MessageImprint, --a 哈希算法 OID 和要被处理的数据的哈希值
(...)
messageImprint 字段应该包含要加时间戳的数据的散列。哈希表示为八位字节字符串。它的
长度必须与该算法的散列值的长度相匹配
(例如,SHA-1 为 20 字节,MD5 为 16 字节)。MessageImprint ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier, hashedMessage OCTET STRING }
但是,如果我想对 CMSSignedData 对象中的字节进行时间戳记,它不会告诉我在哪里或如何获取 MessageImprint 数据。
我是这个数字签名的新手。