0

I am trying to get my content timestamped so I know when it was changed. First I used a shell script but I want to implement it in my python program. the shell script works fine for now but I can't get the python version to work for me. This is the working shell version

in_file='test_content'
out_file="${in_file}.tsr"
ts_server='http://time.certum.pl/'
openssl ts -query -data "$in_file" -sha1 -cert | curl -o "$out_file" -sSH 'Content-Type: application/timestamp-query' --data-binary @- "$ts_server"
openssl ts -verify -data "$in_file" -in "$out_file" -CAfile "/usr/lib/ssl/certs/Certum_Trusted_Network_CA.pem"
openssl ts -reply -in "$out_file" -text

I tried to mimic this with rfc3161 package but the verification is not going as expected. This is the python code

import rfc3161

cert = file('/usr/lib/ssl/certs/Certum_Trusted_Network_CA.pem').read()
rt = rfc3161.RemoteTimestamper('http://time.certum.pl/', certificate=cert)
data_to_sign = file('test_content').read()
print rt.timestamp(data=data_to_sign)
>>> (False, 'Bad signature')

I don't know what is wrong since both scripts should do the same thing. Can somebody give me a clue on what is wrong with the python version?

melanholly
  • 762
  • 1
  • 9
  • 20

3 Answers3

2

The following code works using

Because

  • Python 2 and module rfc3161 are no longer maintained
  • I'm not sure about the certum.pl endpoint url and where to get the correct CA certificate

You should choice the hash algorithm (default is sha1, deprecated).

import rfc3161ng
import os
from struct import unpack

with open('freetsa.pem', 'rb') as cert_fh:
    cert = cert_fh.read()

rt = rfc3161ng.RemoteTimestamper('https://freetsa.org/tsr', certificate=cert, hashname='sha256')

with open('test_content', 'rb') as content_fh:
    data_to_sign = content_fh.read()

nonce = unpack('<q', os.urandom(8))[0]

print(rt.timestamp(data=data_to_sign))

Security warning

Nonce is optional but recommended, as explained in paragraph 2.4.1 of RFC3161

The nonce, if included, allows the client to verify the timeliness of the response when no local clock is available. The nonce is a large random number with a high probability that the client generates it only once (e.g., a 64 bit integer).

cisba
  • 61
  • 6
1

python-rfc3161's author here. If bad signature is returned, it means that the certificate your declared for TSA is not the one that is really used for signing.

The patch provided by melanholly does not seem right to me, you should never use certificate bundled with a signature to check if you cannot verify its origin (using a PKI for example). It does not seem to be the case here.

  • You are right I need to validate the certificate first then validate TSA answer. I will Add it to the original post. Thanks – melanholly Aug 08 '16 at 07:41
0

The problem lies in the library rfc3161 that I used. It seems like the author does not include check for TSA authority certificate so I had to make a change to the library.

The changed code is in api.py in check_timestamp function. You have to change the code block for certificate loading with this one:

EDIT: The Certificate from the response should be validated against some certificate store. If you cannot validate it u should raise an Exception

if certificate != "":
    try:
        certificate = X509.load_cert_der_string(encoder.encode(signed_data['certificates'][0][0]))
        # YOU SHOULD VALIDATE THE CERTIFICATE AGAINST SOME CERTIFICATE STORE !!!! 
        if not validate_certificate(certificate): #NOTE: I am not ready with this function.
            raise TypeError('The TSA returned certificate should be valid one')
    except:
        raise AttributeError("missing certificate")
else:
    try:
        certificate = X509.load_cert_string(certificate)
    except:
        certificate = X509.load_cert_der_string(certificate)

EDIT2: for validation you can use the code described here:

Now the verification is working as expected.

Community
  • 1
  • 1
melanholly
  • 762
  • 1
  • 9
  • 20