Verifying TBS hash


Apologies if this turns out to be specific to the way that certspotter’s CT log works, rather than the CT information that LE is providing.

I’m trying to match up the certificates list in certspotter’s issuances log for a given domain against local copies of the certificates which were issued by LE. One of the fields included in “tbs_sha256” which aiui should be a SHA256 hash of the TBS certificate.

I’m doing this using Python’s cryptography library:

    with open(filename, 'rb') as f:
        pem_data =
    cert = x509.load_pem_x509_certificate(pem_data, default_backend())
    tbs_hash = hashlib.sha256(cert.tbs_certificate_bytes).hexdigest()

but the resulting hash never matches the CT log. Am I missing something obvious?


Try hashing the DER encoding of the certificate, I think you could try x509.public_bytes(DER)

Honestly the tbs_sha256 that CertSpotter is reporting seems … wrong. See later posts in thread, CertSpotter is right.

I’ve tried calculating from both Python and Go, using this API response.

Both my implementations report 1812ebb746bae88796afde410fc2d0698c780c62c72e631a1406e077a3ed19ef for SHA-256 over the TBSCertificate DER bytes, but CertSpotter reports 70a8dd710f3955ebfce0a6a83059f8273201150762ccd5b67cb55a099de5ac1b.

I can’t figure out how 70... is derived.

Might be worth asking CertSpotter to clarify.

Does this help?

“tbs_certificate” is the DER-encoded TBSCertificate (see [RFC5280]) component of the Precertificate – that is, without the signature and the poison extension. If the Precertificate is not signed with the CA certificate that will issue the final certificate, then the TBSCertificate also has its issuer changed to that of the CA that will issue the final certificate. Note that it is also possible to reconstruct this TBSCertificate from the final certificate by extracting the TBSCertificate from it and deleting the SCT extension.


I can’t figure out how they’re standardizing the certs before calculating the SHA. it looks like you may be able to figure it out through some github source:

Nice, you solved it. The problem is that if CertSpotter returns a cert rather than a precert (not something the user can control), you need to delete the SCT extension (1 3 6 1 4 1 11129 2 4 2) and re-encode the DER.

Once you do that, it runs okay:

$ go run tbs_hash.go
2020/03/18 07:18:03 CertSpotter reports: ad59070419ae87ef36aab07b404fda1470fcde529e2789240e5a90c5a0b9114c, we report: ad59070419ae87ef36aab07b404fda1470fcde529e2789240e5a90c5a0b9114c

Sorry CertSpotter for doubting you :frowning: .


Yep, that’s exactly the piece I was missing; thanks!

I even read that part of the RFC, but suspect my eyes then glazed over at the ASN.1 :roll_eyes:

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.