I have a valid PKCS7 file loaded into a CMSSignedData object. This PKCS7 file includes a plain text message and a valid attached digital signature (all in the same file).
Now I want to timestamp this file. This is the code I'm using (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;
}
I can get a valid TimeStamp, and I could put it into my CMSSignedData object and save it to a file writting the bytes from signedData.getEncoded() to the harddisk. But when I validate my new shinny timestamped file with a third party software, this software tells the original signature is ok, but the Timestamp doesn't correspond with the signature. This software also can show me the original plain text message.
I think the problem is in this line:
TimeStampRequest request =
reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
I think I have to pass a digest instead of a dummy byte array, but I don't know which digest, or what are the right bytes I have to timeStamp.
I successfully could get and verify a SignerInformation
object from my signedData
. Then I tried to pass to the reqGen.generate()
function the bytes from mySignerInformation.getSignature()
. The timestamp verification failed. Then I passed a Sha1 digest of mySignerInformation.getSignature()
, but my timestamp verification failed again.
The RFC3161 specification says:
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 }
But it doesn't tell me where or how I get the MessageImprint data if I want to TimeStamp the bytes inside a CMSSignedData object.
I'm a newbie in this digital signature stuff.
You're right, the problem is that you're timestamping the incorrect data. The rest of the code seems correct to me.
So the thing is that you've to timestamp the hash of the signature. To get the signature from your CMSSignedData
and hash it; you can use the follow code (supposing that you've only one signer in your PKCS7
and you're using SHA1
hash algorithm):
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));
...
Hope this helps,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With