Last time, you received an ActivityPub message claiming to be from your goddaughter, Marigold. But it's possible that someone is tricking you, pretending to be Marigold. How can you be sure the message is really from her?
We're going to do a bit of crypto here, but don't be afraid. All you need to know is:
- Everyone on ActivityPub has two related keys: a public key (which is public), and a private key (which is, er, private). We'll represent Marigold's public key as PPPPP.
- Any message can be represented by a unique signature, which we'll represent by SSSSS.
- Signatures are created using private keys.
- Signatures are verified (checked) using public keys.
Let's look at the HTTP headers of Marigold's message to you.
Content-Type: application/activity+json
Date: Fri, 05 Apr 2019 20:40:09 GMT
Digest: SHA-256=DDDDD
Host: house.example.com
Signature: keyId="https://pegs.example.org/users/marigold#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="SSSSS"
As you see, the Signature line contains a bunch of different parts: "signature" gives the message signature, and "keyId" tells us where to find the public key. The other parts of Signature aren't directly relevant right now, but I can explain them in comments if you like.
keyId isn't the public key; it just tells us where to find the public key. If it contained the public key itself, it would be really easy to fake a message and sign it using your own key. So we have to go looking.
Unsurprisingly, this message is signed using Marigold's own main public key (https://pegs.example.org/users/marigold#main-key). If it was signed using someone else's, it would be time to ask questions!
Now, do we already have a copy of Marigold's public key? If this is the first message from her, probably not. keyId told us to look in https://pegs.example.org/users/marigold#main-key. The "#main-key" part is a "fragment", which doesn't form part of the address itself. So let's take a look at https://pegs.example.org/users/marigold.
Up till now, we've only received ActivityPub objects from other people. Now that we're going to fetch them ourselves, over ordinary HTTPS. we have to remember to include the header
Accept: application/activity+json
Otherwise the remote server would assume we were an ordinary web browser, and show you text/html content meant for mortal humans.
We look up https://pegs.example.org/users/marigold and receive:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id":"https://pegs.example.org/users/marigold",
"type":"Person",
"inbox":"https://pegs.example.org/users/marigold/inbox",
"preferredUsername":"marigold",
"name":"Marigold Mobbs",
"summary":"Ordinary. Not called Sarah or Anne.",
"publicKey":{
"id":"https://pegs.example.org/users/marigold#main-key",
"owner":"https://pegs.example.org/users/marigold",
"publicKeyPem":"PPPPP"
},
(... and plenty more information)
Now we know that Marigold's public key is PPPPP. We check the first message's signature with this key, and it matches! So we can be reasonably sure that message came from Marigold.
Next time we'll look at the types of information you can send with ActivityPub: posts, reblogs, likes, follows, and so on.
Click for: previous part,
all parts.