---
title: Playing With Certificate Transparency
description: How it works, why it exists and how you can do things with it
publishedDate: 2024-03-23
updatedDate:
    - 2024-10-24
tags:
    - projects
    - favourite
---
One of my recent security lectures talked about TLS certificates and the chain of trust that's incorporated in them, which led me to have a poke at some of the certificates that are used by my own website. Something that wasn't mentioned in the lecture but that I was reminded of was the existence of certificate transparency logs.

Certificate transparency logs are a public, auditable ledger of all the certificates that are issued by [certificate authorities](https://en.wikipedia.org/wiki/Certificate_authority) like [Let's Encrypt](https://letsencrypt.org). These days, it's functionally a requirement for CAs to submit details of the certificates they issue to CT logs for them to be usable on the public internet.

I'm going to take this post as a chance to talk a little about how CTs work and how I built a small monitoring service that tells me when certificates are issued for my domains.

# What's a CT log?

> "Certificate Transparency logs are "append-only" and publicly-auditable ledgers of certificates being created, updated, and expired." - [https://certificate.transparency.dev/logs/](https://certificate.transparency.dev/logs/)

Every certificate that is issued by a CA has to be logged in a CT log - or more realistically, multiple. When a browser/user-agent is validating a certificate, it checks to see that the certificate has been entered into at least two CT logs - otherwise, it rejects it.[^1]

CT logs can be operated by anybody - notable ones are run by Google, Cloudflare and Let's Encrypt, [but those are far from all of them](https://certificate.transparency.dev/logs/). For acceptance for use by Google Chrome, a CT must [pass certain requirements](https://googlechrome.github.io/CertificateTransparency/log_list.html) like having an uptime of 99% and not having a delay of more than 24 hour between a record being submitted and a record being visible in the log.[^2]

# But why?

The need for CT logs was highlighted by the [2011 hacking of DigiNotar](https://en.wikipedia.org/wiki/DigiNotar). Attackers gained access to their certificate issuance infrastructure and issued over 500 fraudulent certificates that were then used to perform man-in-the-middle attacks on various targets in Iran. Domains included were the likes of Google, Yahoo, Mozilla and the Tor Project.

Concerningly, DigiNotar also had a part to play in the issuance of certificates for the Dutch Government, and while it's not thought that they were targeted, their certificate issuance chain was also compromised.

Knowledge of the hacking only came to light 6 weeks after the actual incident occurred, despite DigiNotar themselves knowing about it only 9 days afterwards. The eventual impact was that DigiNotar was distrusted as a CA entirely, but the incident also highlighted a need for transparency from CAs that did not exist at that time.

The first CT logs were launched in March and September 2013. Since then, the number of logs in operation grew and ultimately have proven their worth as they've been used to show that multiple CAs were issuing bad certificates - all of which ended up being distrusted.

# Accessing data in CT logs

The core data structure behind CT logs is called a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) which is exposed through a complicated API, and I don't really understand either of those things.[^3] But that's fine, because realistically as an end-user that's trying to pull data, instead of talking to one or multiple CT logs yourself, a tidy little service called [crt.sh](https://crt.sh) exists that you can use. It exposes a search engine for data that's aggregated from a number of different certificate logs, meaning you're going to get a fairly comprehensive set of results for any query that you make.[^4]

[Searching for a domain like `akpain.net`](https://crt.sh/?q=akpain.net) will give you a list of all the certificates that have been generated for that domain.

![A screenshot showing the crt.sh search results for akpain.net](domainresults.png)

You also have access to use operators like `OR` to search across multiple domains at once, and using [the advanced search panel](https://crt.sh/?a=1) gives you the ability to tweak many more search parameters.

Detailed breakdowns of each certificate that is listed in any results are also available - for example, [here's the page for the certificate that this site was using at the time of publication](https://crt.sh/?id=11905901614). There you can see which CT logs the information was sourced from, a breakdown of the revocation status of the cert and the current validity, as well as the raw contents.

crt.sh *also* allows you to access the results of search queries using a handy dandy Atom feed - in these feeds, you get a short summary of the certificate and a PEM-encoded copy of it.

```xml
<entry>
    <id>https://crt.sh/?id=11905901614#q;akpain.net</id>
    <link rel="alternate" type="text/html" href="https://crt.sh/?id=11905901614"/>
    <summary type="html">www.akpain.net&lt;br&gt;&lt;br&gt;&lt;div style="font:8pt monospace"&gt;-----BEGIN CERTIFICATE-----&lt;br&gt;truncated-----END CERTIFICATE-----&lt;/div&gt;
    </summary>
    <title>[Certificate] Issued by R3; Valid from 2024-01-29 to 2024-04-28; Serial number 041189d1bd5fb881de02a1b1f5ce644da2df</title>
    <published>2024-01-29T10:25:05Z</published>
    <updated>2024-01-29T11:25:06Z</updated>
  </entry>
```

# Building a homebrew certificate monitor

While there exist plenty of commercial solutions to monitor certificate logs for your domains, these cost (sometimes lots of) money and I am a student. I will be building my own.

It's quite simple to build something that polls crt.sh's Atom feed every hour and checks the result against a database of known certificates. When new certificates are detected, they're parsed, recorded in the database and an email is sent listing some details about the certificate. I also threw together a quick web page to show off the contents of the database.[^5] Every now and then, a cleanup job runs to remove expired certificates which are always ignored during certificate ingest.

![A redacted screenshot of my certificate monitor site](certificatemonitor.png)

In the web UI, alongside any certificates that are active or expired, any certificates that are within thirty days of their expiry date are listed as "expiring soon". As I use Let's Encrypt and [Caddy](https://caddyserver.com) to issue and manage my certificates, they're automatically replaced after two-thirds of their lifetime has passed - more specifically, in the case of a Let's Encrypt certificate, that's after 60 days of its 90 day lifetime has passed, which is why I have the threshold set to 30 days.

![A redacted screenshot of an email sent to me from the certificate monitor](email.png)

If you're interested in seeing the source code, get in touch with me (deets on my home page) and I'll be happy to send it your way.

Despite the simplicity of the overall app, I learned a few things about certificate formats and how they work there were a couple of interesting stumbling blocks, both centered around precertificates.

## Precertificates

Precertificates are a mechanism that exists to prove that a given certificate has been/will be included in a given CT log. A precertificate contains all the same information as the eventual "full" certificate and is encoded in the usual [DER format](https://en.wikipedia.org/wiki/X.690#DER_encoding), but has a flag added to "poison" it and prevent it from being used normally.

When a certificate is submitted to a CT log, the log responds with a Signed Certificate Timestamp (SCT) that records and cryptographically certifies the time it  was submitted. In the case of precertificates, these SCTs are embedded into the corresponding full certificate, proving that a record of this certificate has been made public, and that the main certificate will presumably also be submitted to a CT log once it is generated.[^6] [^7]

I ran into two issues when dealing with precerificates. The first was the fact that a precertificate and a certificate share a serial number, so you absolutely cannot use that as a primary key (whoops).

The other issue I ran into was working out just how to detect if a given certificate was a precertificate or not. Since precertificates contain a poison extension that prevents them from being considered valid, it's possible to just look out for the existence of that in the certificate extension, but I found it surprisingly challenging to work out just what that extension was.

Eventually, I found out that the ASN.1 identifier for the poison extension is `1.3.6.1.4.1.11129.2.4.3`, courtesy the footer of [a technical article on the Let's Encrypt website](https://letsencrypt.org/2018/04/04/sct-encoding.html) - though really it should have occured to me to check the RFC for this before I spent a while doing lots of random Googling. Now, to check if a certificate is a precertificate or not, it's as simple as checking for the presence of an extension with the aforementioned ID.

The reason I wanted to be able to tell if a certificate was a precertificate or not was so that I could reduce the amount of noise in the UI of the app. As it currently stands, precertificates are recorded in the database but are completely ignored by both frontends. In the future, I'd like to add alerts for when precertificates are logged without a corresponding certificate also being logged and vice versa - but both those situations are sufficiently unlikely to occur that I imagine their existence is purely theoretical in the contexts I'm working in.

# An entirely pointless endeavour

My little certificate monitor is now online and puttering away as expected. While I've enjoyed seeing what my servers have been up to and knowing when new certificates have been issued, I don't imagine that I'll detect anything untowards through it - I wouldn't class myself as a high-enough value target. That said, it's definitely a nice to have and it was an interesting topic to learn about!

---

# Further reading

* RFC6962 - the RFC that governs CT logs: [https://datatracker.ietf.org/doc/html/rfc6962/](https://datatracker.ietf.org/doc/html/rfc6962/)
* "How Let's Encrypt Runs CT Logs": [https://letsencrypt.org/2019/11/20/how-le-runs-ct-logs.html](https://letsencrypt.org/2019/11/20/how-le-runs-ct-logs.html)
* A site run by Google explaining more about certificate transparency: [https://certificate.transparency.dev](https://certificate.transparency.dev)

# Thanks

Thank you Ellie for letting me know about some typos in this article :)

---

[^1]: The exact rules for this are dependent on the duration of the certificate - longer-living certificates need entering into more logs. [Documentation about what Google Chrome requires can be found here.](https://googlechrome.github.io/CertificateTransparency/ct_policy.html)
[^2]: A list of currently accepted logs can be found here: [https://googlechrome.github.io/CertificateTransparency/log_list.html](https://googlechrome.github.io/CertificateTransparency/log_list.html)
[^3]: Eg: [https://sapling.ct.letsencrypt.org/2024h2/ct/v1/get-roots](https://sapling.ct.letsencrypt.org/2024h2/ct/v1/get-roots)
[^4]: Of course, this is far from infallable - if you read your data out of this instead of directly reading CT logs, you're of course going to have to put 100% trust in crt.sh. For me, that's okay, but it might not be for you and that's also okay.
[^5]: This was built with the [GOV.UK Design System](https://design-system.service.gov.uk/), which is the open source web framework created by the Government Digital Service. I hadn't used this before, and I hope to write about the experience of using it as a non-government being soon, but the TL;DR is that it was very useful.
[^6]: If you take a look at a certificate detail view inside of Firefox, you can see a list of SCTs included in it at the bottom of the page - one for each CT log that it was entered into.
[^7]: It also doesn't matter if the full certificate is never submitted to a CT log despite being issued because the record of that precertificate is still going to be there.
