我必须传递来自 CMSSignedData 对象的哪些数据才能生成有效的时间戳?
Which data from a CMSSignedData object must I pass to generate a valid Timestamp?
我已将有效的 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() 中的字节写入硬盘。但是当我用 third party software 验证我新的带有时间戳的新文件时,该软件告诉原始签名没问题,但时间戳与签名不一致。这个软件还可以给我显示原始的纯文本消息。
我认为问题出在这一行:
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
我想我必须传递摘要而不是虚拟字节数组,但我不知道哪个摘要,或者我必须给时间戳记的正确字节是什么。
我成功地从我的 signedData
中获取并验证了一个 SignerInformation
对象。然后我尝试将来自 mySignerInformation.getSignature()
的字节传递给 reqGen.generate()
函数。时间戳验证失败。然后我通过了一个mySignerInformation.getSignature()
的Sha1摘要,但是我的时间戳验证又失败了
2.4.1. Request Format
A time-stamping request is as follows:
TimeStampReq ::= SEQUENCE { version INTEGER
{ v1(1) }, messageImprint MessageImprint,
--a hash algorithm OID and the hash value of the data to be
(...)
The messageImprint field SHOULD contain the hash of the datum to be
time-stamped. The hash is represented as an OCTET STRING. Its
length MUST match the length of the hash value for that algorithm
(e.g., 20 bytes for SHA-1 or 16 bytes for MD5).
MessageImprint ::= SEQUENCE {
hashAlgorithm AlgorithmIdentifier,
hashedMessage OCTET STRING }
但是如果我想在 CMSSignedData 对象中的字节上加时间戳,它不会告诉我在哪里或如何获取 MessageImprint 数据。
我是数字签名方面的新手。
你是对的,问题是你给不正确的数据加了时间戳。其余代码对我来说似乎是正确的。
所以问题是您必须为签名的散列加上时间戳。从您的 CMSSignedData
获取签名并对其进行哈希处理;您可以使用以下代码(假设您的 PKCS7
中只有一个签名者并且您使用的是 SHA1
哈希算法):
CMSSignedData signedData = ...
// get the signers of your CMSSignedData signedData
Collection ss = signedData.getSignerInfos().getSigners();
SignerInformation si = (SignerInformation) ss.iterator().next();
// hash the signature
byte[] signDigest = MessageDigest
.getInstance(TSPAlgorithms.SHA1, new BouncyCastleProvider())
.digest(si.getSignature()); // since you're adding the bc provider with Security.addProvider you can use "BC" instead of passing the new BouncyCastleProvider()
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
// generate the TSRequest
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, signDigest, BigInteger.valueOf(100));
...
希望这对您有所帮助,
我已将有效的 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() 中的字节写入硬盘。但是当我用 third party software 验证我新的带有时间戳的新文件时,该软件告诉原始签名没问题,但时间戳与签名不一致。这个软件还可以给我显示原始的纯文本消息。
我认为问题出在这一行:
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
我想我必须传递摘要而不是虚拟字节数组,但我不知道哪个摘要,或者我必须给时间戳记的正确字节是什么。
我成功地从我的 signedData
中获取并验证了一个 SignerInformation
对象。然后我尝试将来自 mySignerInformation.getSignature()
的字节传递给 reqGen.generate()
函数。时间戳验证失败。然后我通过了一个mySignerInformation.getSignature()
的Sha1摘要,但是我的时间戳验证又失败了
2.4.1. Request Format
A time-stamping request is as follows:
TimeStampReq ::= SEQUENCE { version INTEGER { v1(1) }, messageImprint MessageImprint, --a hash algorithm OID and the hash value of the data to be
(...)
The messageImprint field SHOULD contain the hash of the datum to be time-stamped. The hash is represented as an OCTET STRING. Its
length MUST match the length of the hash value for that algorithm
(e.g., 20 bytes for SHA-1 or 16 bytes for MD5).MessageImprint ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier, hashedMessage OCTET STRING }
但是如果我想在 CMSSignedData 对象中的字节上加时间戳,它不会告诉我在哪里或如何获取 MessageImprint 数据。
我是数字签名方面的新手。
你是对的,问题是你给不正确的数据加了时间戳。其余代码对我来说似乎是正确的。
所以问题是您必须为签名的散列加上时间戳。从您的 CMSSignedData
获取签名并对其进行哈希处理;您可以使用以下代码(假设您的 PKCS7
中只有一个签名者并且您使用的是 SHA1
哈希算法):
CMSSignedData signedData = ...
// get the signers of your CMSSignedData signedData
Collection ss = signedData.getSignerInfos().getSigners();
SignerInformation si = (SignerInformation) ss.iterator().next();
// hash the signature
byte[] signDigest = MessageDigest
.getInstance(TSPAlgorithms.SHA1, new BouncyCastleProvider())
.digest(si.getSignature()); // since you're adding the bc provider with Security.addProvider you can use "BC" instead of passing the new BouncyCastleProvider()
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
// generate the TSRequest
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, signDigest, BigInteger.valueOf(100));
...
希望这对您有所帮助,