Digital Signatures

The purpose of digital signing is to establish the authenticity of data. That is, that it originates from the expected source and has since not been changed. It helps to connect the data that is distributed, because the value of authenticity is retained as long as the signature remains, and also because the authenticity of other parties, some completely unheard-of, will be automatically established through a chain of trust.

ClearText Signing with PGP

PGP has a simple transformation called "cleartext signing" that prefixes a few header lines and postfixes a few lines with the signature. Assuming the data looks like this:

{
  "meta": { ... },
  ...
}

This can then be signed when a private key is available, as explained below. In the test/ directory, we only have a public key, meaning that we cannot sign but validate what another party signed in test/pubkey.json.asc:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

{
  "meta": { ... },
  ...
}
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEEDbBIN8Ky/cH07Y9B/Oo6V20bEJcFAmZJBxAACgkQ/Oo6V20b
EJehxwv/cME7TdHA17eKw252joOuQ57y3fwq1LupzsXw7FsJD6ncreUzVAJHaUDA
hWfsO6zF7wCy5zhIrGvPq5JVGf7wBCNf3BlUD3+e70yPge3j/fLDpyqmLBIl0Yqt
qHGxmxmEvVhlIpPdZ1SvQi93IxUUUgyS7Hpg5eT5mNIoP6DICDqFeFgqxg7V6ct7
zZrirY5cw5t/KXrCHZe9SGKz9/E6k2sQNscfWcotW5UIoTN7zM1RqvxNGHvxQTWL
dwBbNyY1e4sJHu+t8w+J5pZZvAy9vaMVyGzE+MOwOXICCAQCBUkoYqWB4l9YXfGL
QOUQR3s4k6v7v5qNIc/vmd+Y5Xmx2fPTLDDdkLyCBYwCYOTAG6d5tNxUun9pmhkT
8+UcFRjouau8kBjnMPqZgHlQ8aPGYFa8ePzEyQCiSAzutxwnvoWGFsjKKMvKcH58
38TQgnXfM0MI76PcUifeqcatw10PsGtbilWH9ZPW3aJ40bhhyP8xXJm8tg+0SUs0
TbLs7hNP
=xbVL
-----END PGP SIGNATURE-----

Normally the data is much longer; the signature has an almost fixed length, so it will not grow when the data does. Usefully, the markers around the data are so clear that it is easy enough to ignore the crypto if so desired. But when it is needed, it's there. It can then be validated with a statement like:

shell$ gpg --verify test/demo.json.asc
gpg: Signature made za 18 mei 2024 21:52:48 CEST
gpg:                using RSA key 0DB04837C2B2FDC1F4ED8F41FCEA3A576D1B1097
gpg: Good signature from "Demo Carbon Budget Node Signing Key <carbondemo@example.com>" [ultimate]

This requires the prior import of the public key, with a one-time command:

shell$ gpg --import test/pgpkey.asc

Note that the private key is not presented, so the --clear-sign command above does not work. Generate one (with its unique fingerprint) using:

shell$ gpg --gen-key

The key can now be used, referencing the fingerprint for this key and some JSON file, using:

shell$ -local-user 0DB04837C2B2FDC1F4ED8F41FCEA3A576D1B1097 --armour --clear-sign somefile.json > somefile.json.asc

This produces a file somefile.json.asc that has a signature like above. Since the public key is also known, the signer can validate it as before.

To allow anyone else to verify, as shown above, export the public key with its fingerprint:

shell$ gpg --armour --export 0DB04837C2B2FDC1F4ED8F41FCEA3A576D1B1097 > test/newkey.asc

Anyone with access to test/newkey.asc can validate the signature, but they cannot create fresh signatures (on other JSON objects) with the same key, because that would require the private key, which must be kept away from others. The tools shown here can perform signing well away from the publication software.

Publishing Keys

The public key can be used to sign lots of data, each time with a fresh signature. This static key can be stored safely on a web server, at a URL that should be part of the JSON document's meta-data. It is also possible to make it available in multiple places. There is no security implication in doing this, even at untrusted places. Anyone may see the public key, as long as nobody sees the private key it is secure.