I'm trying to verify a time stamp token embedded into a PDF using the PDF 2.0 feature of the "Document Time-stamp Dictionary". The embedded time stamp token is exactly the token of a time stamp response from a time stamp authority server.

Since the OpenSSL library of Ruby doesn't seem to provide the necessary interfaces to do the task, I'm trying to do it manually. And I'm stuck at verifying that the digest value of the time stamp token is equal to the digest value of original content.

Here is the openssl pkcs7 -in response.der -inform DER -print output of a sample time stamp token: https://pastebin.com/s4CTGeQX

What I was able to do:

  • I can navigate the ASN1 structure of the PKCS7 object to locate the used digest algorithm inside the SignerInfo. In the example case this is "SHA512".

  • I also found the authenticated attribute called "messageDigest (1.2.840.113549.1.9.4)".

However, the messageDigest attribute value doesn't match the result of using SHA512 over the original content.

I'm not quite sure whether I have to further process messageDigest somehow or whether the digest I'm looking for is hiding (in plain sight) somewhere else. So any information on how to verify the signed content digest in the time stamp token is appreciated - thanks!

  • $\begingroup$ Exactly what are you checking? If I take SHA512 of the bytes extracted with openssl asn1parse ... -strparse 65 -out x, which are the contentInfo value beginning 30 81 and ending 31 5A, I get b8e362aa8b2eaa8e4eb81a019f14ffb96b4af0910ce0ad4e15621ada378518d07f327a7feca3ec2523ef212ce3a1fa8bbab6a6aef68706d82caaa531a43266e3 which does match the value of messageDigest in signedattrs $\endgroup$ Nov 9, 2022 at 1:59
  • $\begingroup$ Ah, thank you! Your comment really helps, especially that the openssl command output shows another DER encoded structure lurking in there. I think I can now get to what I want $\endgroup$
    – gettalong
    Nov 10, 2022 at 21:10
  • $\begingroup$ Thanks again, I just figured it out. See my answer for details. $\endgroup$
    – gettalong
    Nov 10, 2022 at 21:50

1 Answer 1


Thanks to the comment of @dave_thompson_085 I found what I was looking for.

So it turns out that PKCS7/d.sign/contents/d.other contains the DER encoded version of the TSTInfo structure. And this structure contains the messageImprint field which contains the hash/digest of the PDF that was originally sent in the time stamp request.

I can compare that value to the re-computed value when verifying the time stamp token, to ensure that the token really covers the PDF.

In Ruby code this looks something like this:

# Parse the time stamp token
p7 = OpenSSL::ASN1.decode(signature_dict.contents)
raise "not a signed-data" if p7.value[0].value != "pkcs7-signedData"

# Retrieve the signed data portion d.sign
signed_data = p7.value.find { |x| x.tag_class == :CONTEXT_SPECIFIC && x.tag == 0 }.value[0]

# Retrieve the ContentInfo structure
content_info = signed_data.value[2]

# Parse the embedded TSTInfo structure
content = OpenSSL::ASN1.decode(content_info.value[1].value[0].value)
digest_algorithm = content.value[2].value[0].value[0].value

# Retrieve the original hash sent in the time stamp request and
# recompute the hash from the PDF data
original_hash = content.value[2].value[1].value
recomputed_hash = OpenSSL::Digest.new(digest_algorithm).digest(signature_dict.signed_data)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.