Signing Posts with gpg


Recently I had the idea to cryptographically sign my blog posts with gpg. It came to me while I was thinking about various forms of news fakes, whether intentionally misrepresenting news orgs, individuals, or AI generated by the latest round of eldrich horrors we have unleashed.

The idea itself is simple: By signing the posts you can add trust to the source.

If many people begin to do it (and it’s made easy to do) than tooling will grow around the practice to make verification easier, and the value further increases. If enough of that continues we could see a similar practice from news organizations themselves, which is really the ultimate goal.

I can’t make ProPublica start signing their posts, but I can start the ball rolling by doing it myself and explaining how. If you find that valuable, maybe you’ll do it too.

How to do it

This blog (and my personal one) use hugo to generate the contents. That’s actually unimportant in this case as I’m not using my blog engine for any part of the signing process. If you use jekyll or pelican, or any of the hundreds of other static site generators this will work for you as well. If you’re using WordPress, though, you’ll need to find some other path forward.

A gpg signature is just a block of gpg nonsense between a few comment blocks. We could generate these and then place them back into the HTML files as comments, but I don’t like the idea of touching the generated files after they’re generated. Instead I opt for the creation of a signature file that lives next to the one we’re signing.

In the case of this blog, all my posts are in folders and named index.html. I therefore generate signatures in the same folder named index.html.asc. Here’s how:

gpg --batch --yes --local-user $GPG_FINGERPRINT --armor --detach-sign index.html

This assumes I have gpg running on my machine, that I have a gpg private key in that manager, and that I have passed to my command above the fingerprint of that key in a variable. If that’s all true then out comes the asc file.

That’s it! Then I push my files up to my web server and I’m done!

Okay, okay, so I’m doing a tiny bit more…

I actually use a Makefile to manage my blogs because I can never remember the commands to things like hugo. Here’s an excerpt from my actual Makefile for this blog showing all the magic sauce.

INDEX_FILES != find public/ -name 'index.html'
SIG_FILES := $(INDEX_FILES:%.html=%.html.asc)

build: ## build hugo source
	hugo --gc --minify

public/%.html.asc: public/%.html
	gpg --batch --yes --local-user $(GPG_FINGERPRINT) --armor --detach-sign $<

deploy: build $(SIG_FILES) ## send built files to webserver
	rsync -rvhe ssh --progress --delete ./public/

.PHONY: build deploy

At the top of the file I use GNU Make’s find command to locate all the index files (my blog posts). Then I do a bit of substitution to create a list of all the asc files that should exist once generated. Further down the page I use this same pattern to create a make target. That make target depends on the html file existing, and when run executes that simple command we did before. It may look fancy if you’re not used to make, but that’s basically it. It’s a dependency iterator.

Finally my deploy target will run the hugo build, then sign all the files, and finally rsync them to my server.

Verifying signatures

Signing is good and all, but how do you actually check that a post is signed properly? Great question, me!

The process is currently fairly manual, which is a shame. You’ll need three elements to verify things:

  • My public key
  • My file I’m signing (the index.html of the webpage)
  • My signature file (the index.html.asc file)

Using those you’d first verify that my public key is mine. There’s a lot of out-of-bounds ways to do that. You could see that it’s hosted on another of my websites and trust that. Or maybe I have it verified through social means, like Keybase. Or maybe I’ve done some fancy DNS shenanigans. Yeah, basically it’s a pain-in-the-butt.

Once it’s verified, you can import it to your own GPG key store. Yay! Side benefit, you can encrypt email to me now too!

Once the key is in gpg you can run the verify command using the other two files:

gpg --verify index.html.asc index.html

If it worked you’ll get a Good signature message back that looks like this:

gpg: Signature made Wed 22 Feb 2023 21:22:52 UTC
gpg:                using RSA key 368CD75F5CD5F1E9A72F639F4E0FEB0E09DDD7DF
gpg: Good signature from "James Tomasino <>" [ultimate]

Uh, that sucks

Yeah, it really does. That’s a hugely painful process and one of the reasons nobody does this except for important things. But, does it really need to be hard? Wouldn’t it be fairly trivial to automate those steps and give a good/bad result?

There’s at least one browser plugin that attempts to automate this and add a visual feedback mechanism. This post describes an alternative method of signing the page that allows you to curl … | gpg the page and verify it in one go.

What I’m getting at is that this isn’t yet a solved problem and there’s room for improvement. Certainly before many others take up the steps of adding their own signatures the verification side of things will need some love.

Just creating and managing gpg keys isn’t easy and intuitive. It’s taken me some time to get my head around it, and I still have to look up anything non-trivial. Projects like Keybase get my kudos for trying to make entry into the public/private key world easier, even if I do question some of their decisions.

What next?

I’d love to see this practice grow. I want automation around it. I want us to move on from LetsEncrypt and start using DANE/TLSA to sign our own domains and have auto-verification without needing to trust on first use (TOFU). I want SSHFP to gain popularity in client usage so we can auto-trust host identities when they change as well. All of these things have the same challenge in common, and the technology is there for us to make it all better, more seamless, and more trustworthy.

And I think these are steps we’ll need to take (to varying degrees) to better filter what’s real and not, what’s intentional and not, on an internet of machines making shit up.

What can I do?

If you’ve got the skills, some passion for it, and the time, why not take a crack at improving what I’ve just laid out. I’m certain there’s better ways. Should we have a tag on the page with an alt-content link to the signature? Should the fingerprint of the public key for signing be indicated in a TXT entry in DNS? Play around with stuff. Try things and break things. Maybe you’ll stumble on a solution that works for more people.

Also, keep talking about it. We have the tech to sign stuff and prove it was you who wrote it. We can use that more. How can that be made easier?

Have fun with it and maybe our playground ideas will have some impact down the line.

This page is cryptographically signed with my public key. (learn more)