GPG Advanced Placement

More Fun with Gnu Privacy Guard!

Charles Shapiro
1 Jan 2022

Arguing that you don't care about the right to privacy because you have nothing to hide is no different from saying you don't care about free speech because you have nothing to say.

Edward Snowden, 21 May 2015

Introduction

So now you have your very own GPG key. What can you do with it?

GPG Key Anatomy

If you created your key with Gnu Privacy Guard version 2 or greater, you actually have no fewer than four keys:

The use of the first two keys is obvious -- anyone can send you a message which you alone can read. But the reason for the other pair is less clear -- they enable you to encrypt a message which every one in the world can decrypt. Why would you want to do that?

The answer turns out to be interesting and subtle. Remember, your keys are bound to your identity. Hence, that public decryption key will work only for messages which you yourself have encrypted. This property enables anyone to verify that a given message had to come from you alone, and that it hasn't been tampered with in transit. Digital signatures are actually more secure than signing a document in the old-fashioned way with pen and paper, because they are tightly bound to the document itself. An untrustworthy lawyer could alter your contract, for example, by substituting a page other than the one you signed. No such shenanigans are possible with a digital signature.

This guide will give you step-by-step instructions on how to digitally encrypt and sign a file, and how to decrypt and verify the digital signature on something sent to you. At the very end, I'll explain some interesting ways to conceal a message, should that become necessary.

Index

Encrypting and Decrypting

Use the --encrypt argument to encrypt a file:
!:/tmp> gpg --encrypt --armor -r "Jeffrey Meng" gorey.txt
!:/tmp> ls -l gorey.txt.asc
-rw-r--r-- 1 devel devel 1540 Jan  1 14:54 gorey.txt.asc
  

For this to work properly of course, gpg must know your recipient ("Jeffrey Meng" in the example above). It knows this through your keyring, kept in ${HOME}/.gnupg/pubring.gpg. You can use the --list-keys argument to list out who is currently in there:

!:/tmp> gpg --list-keys
    pub   rsa2048 2009-11-03 [SC]
      EDA61F66187B60C547A1A4472B8B223DCADDA963
uid           [  full  ] Aaron Ruscetta (To Freedom, Civil Liberty and Human Rights.) 
sub   rsa2048 2009-11-03 [E]

pub   rsa2048 2009-11-19 [SC]
      581FB1617CF4B6C924B084F7F95C2258FEAE18E8
uid           [  full  ] Brian MacLeod 
uid           [  full  ] Brian N MacLeod 
uid           [  full  ] Brian MacLeod 
sub   rsa2048 2009-11-19 [E]

pub   rsa4096 2018-01-11 [SC] [expires: 2022-01-11]
      BB73731A1EE36BEAB5C5616EDDFB4744312D0A19
uid           [  full  ] Jeffrey Meng 
sub   rsa4096 2018-01-11 [E] [expires: 2022-01-11]

If you don't have your recipient's public key information, you can find it on a public keyserver (such as
the Ubuntu keyserver or the MIT keyserver) by their email address or by their key ID if they've given that to you. You can see the Key IDs you already have as the string of hex numbers just below the lines beginning with pub in the example above. Be sure to prepend the key ID with 0x if you are searching for someone by it on a keyserver.

Once you have found their key information, off-click on the link under the key id, and save link as a convenient name. This should get you a file which looks like this:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Hostname: 
Version: Hockeypuck ~unreleased

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
=rh3Z
-----END PGP PUBLIC KEY BLOCK-----

      

(Note that the example here has been mangled for length). You can import this public key to your keyring with the --import command:

!:/tmp> gpg --import jeffreymeng.pubkey.asc 
gpg: key DDFB4744312D0A19: 10 signatures not checked due to missing keys
gpg: key DDFB4744312D0A19: public key "Jeffrey Meng " imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:  35  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:  35  signed:  12  trust: 35-, 0q, 0n, 0m, 0f, 0u
!:/tmp> 

If the key is unsigned, or if it wasn't signed by at least one person already signed by you in your key ring, you will have to sign the key:


!:/tmp> gpg --sign-key "Jeffrey Meng"
      
pub  rsa4096/DDFB4744312D0A19
     created: 2018-01-11  expires: 2022-01-11  usage: SC  
     trust: unknown       validity: unknown
sub  rsa4096/36EAAF9A8DCCB51B
     created: 2018-01-11  expires: 2022-01-11  usage: E   
[ unknown] (1). Jeffrey Meng 


pub  rsa4096/DDFB4744312D0A19
     created: 2018-01-11  expires: 2022-01-11  usage: SC  
     trust: unknown       validity: unknown
 Primary key fingerprint: BB73 731A 1EE3 6BEA B5C5  616E DDFB 4744 312D 0A19

     Jeffrey Meng 

This key is due to expire on 2022-01-11.
Are you sure that you want to sign this key with your
key "Charles Shapiro (correct email address) " (C9307B868C387D47)

Really sign? (y/N) Y
      
    
  
You can now encrypt messages to Jeffrey Meng, or verify that a message purporting to be from him really came from him.

You can, of course, also encrypt a file or message for multiple recipients, as:

    
gpg --encrypt -r "Charles Shapiro" -r "Jeffrey Meng" --armor gorey.txt      
    
  

Decrypting a document

If someone has sent you an encrypted document with you in the recipients list you can decrypt it with the --decrypt argument:

:/tmp> gpg --decrypt gorey.txt.asc > gorey.txt.decrypted
gpg: encrypted with 1024-bit ELG key, ID C10E8B28013253B2, created 2003-01-30
       "Charles Shapiro (correct email address) "
      
!:/tmp> diff gorey.txt gorey.txt.decrypted
      

What does Signing Mean?

Signing a document involves creating a hash of the document, then encrypting the hash and the document with your private encryption key. Anyone can then use your public decryption key to view both the document and information about the signature.

A document hash is a long number made by performing an arithmetic operation on every bit in the document (or picture, or program file). One oversimplified way to create this number would be to simply add every byte in the file together, ignoring overflow, to produce a single "hash" byte at the end of the process. Real hash algorithms, of course are much more sophisticated ( and standardized, so anyone can use them ). The interesting thing about a standard document hash is that it depends totally on your document. If the document changes, even by one or two bits, hashing it will create a different number. So anyone who knows how your document hash was created can re-create the hash number you sent them and see if the number they have matches the number you have sent. If it does, the document is unchanged. If it does not, something happened to it between when you signed it and when they checked it.

Unlike encryping, signing a file requires no --recipient. This is because anyone with access to your public key information can decrypt the document. Signing a document verifies that it came from you, but does not conceal its contents.

For this example, we will be using a 1 kb document called gorey.txt, which contains:


A is for Amy who fell down the stairs
B is for Basil assaulted by bears
C is for Clara who wasted away
D is for Desmond thrown out of a sleigh
E is for Earnest who choked on a peach
F is for Fanny sucked dry by a leech
G is for George smothered under a rug
H is for Hector done in by a thug
I is for Ida who drowned in the lake
J is for James who took lye by mistake
K is for Kate who was struck with an axe
L is for Leo who swallowed some tacks
M is for Maude who was swept out to sea
N is for Neville who died of ennui
O is for Olive run through with an awl
P is for Prue trampled flat in a brawl
Q is for Quentin who sank in the mire
R is for Rhoda consumed by a fire
S is for Susan who perished of fits
T is for Titus who flew into bits
U is for Una who slipped down the drain
V is for Victor squashed under a train
W is for Winnie embedded in ice
X is for Xerxes devoured by mice
Y is for Yorick whose head was knocked in
Z is for Zillah who drank too much gin	  
	  

You have several options to sign a document with gpg. You can encrypt the document with your private key and send the whole thing as a package, you can clear-sign the document so that anyone can read it even without gpg, or you can put the document signature in a different file from the original. All these methods are equally secure; which one you use depends on what is most convenient for you and your intended audience.

Signing a document

If you use the --sign argument to gpg, it will both sign and encrypt your document with your private encryption key. You can of course use the --armor switch to create a signed, encrypted copy of your document which contains no non-ascii characters:


!:/tmp> gpg --sign --armor gorey.txt
!:/tmp> cat gorey.txt.asc
-----BEGIN PGP MESSAGE-----

owFN0zmLFEEUB/DZQ2RrEdxEDDxqYAMjWY02k73v+14xeNP9ZrqY6qqxju0d1gMU
PMBgE0MxWjBQMFUEUwUDg/0UfgARI191W2jU0PXrV/X+9fr4XF+td+jryNGTF6P3
Z3re9Z02BlraYPe6O3Tw/eHRGBeWN7XhY3mXF5nmTZSSp7pQ3GXIrQNhLBuPahys
kBysBS8dprzR5Q0EEhNRTEgwUFYqwAYCBXTZZFyeRJtrlVJxE/bQ3nHd5MCtRNHK
2FR0U2AUWlcWSjLdpkJakesgJBmbjmwalOpy65MAUtMNBwIuEQnNRDSD2rSol1xT
S4agVykacsa32GxUs5g4eqRaIReqKuQyEnNRzKVVY2k4OpURVUYS2sjmI5qHHG3J
nNZtLrsYSuWCkiS2ENkCOIwpUcqGOuCFcBkH6vIQ2WKEi6hLZwuQUhe0rdU5cgdJ
27KlqJbAp//VK7DjymwdfYjAlqNbxgMhZSVTEUJtclTKC7YSyYoUB0jRqPKOfCv7
d65CstXIVo2nUxjIO5LKNCW4kAfwhglsLbI1j8rRQtkCqHYMLRcG2XpU65mmbBOt
rM+rqQLeDGIjig1PX5dVOmiEzaqjN4WzbDOaTeF8FX1TYkE7UfeNILai2FLVDVop
Op0wMXHOUwNCse3otkU5C/auh3KrODCuZDuR7QilBHLMG5im1UCIBNluXN9Fc0jD
kOKB9qbqKw9gL4I9bUS4+Exb5BlCWl5fW+lynmmr/Sj36dog+zt+IUeaLp77JOMt
oZ7d6a8N9dYuX6j3/zj7/PPpg77hG/fe3o6//Zne8KfX2MD5+ObqWu21uPb71sVH
X4Y/fhh8/PQn1t+cjP6qvbp089uVld2XI5/k+5me+sl6fe948A8=
=OsLh
-----END PGP MESSAGE-----
 

Anyone who has access to your public key information (e.g. through keyserver.ubuntu.com or pgp.mit.edu) can decrypt this document and view the signature information:

	  !:/tmp> gpg --decrypt gorey.txt.asc > gorey.txt.decrypted
gpg: Signature made Sat 01 Jan 2022 11:21:15 AM EST
gpg:                using DSA key EA0789BFD47E0324317CAC5BC9307B868C387D47
gpg: Good signature from "Charles Shapiro (correct email address) " [ultimate]
gpg:                 aka "Charles Shapiro (cshapiro) <72300.3632@compuserve.com>" [ultimate]
gpg:                 aka "Charles Shapiro (New email address) " [ultimate]
!:/tmp> diff gorey.txt.decrypted gorey.txt

      

In this example, I have redirected stdout to another file. You can see the gpg signature information on the screen, and the last line of the example shows that the original file and the decrypted file are exactly the same.

Clear-signing a document

Sometimes you want intermediataries to read your document without reference to gpg, even though everyone must agree on its contents. This is handy, for example, for resumes which you must pass through not-necessarily-honest recruiters. For that purpose, you can use the --clear-sign argument:

	  gpg --clear-sign --armor gorey.txt
	
      

This ask you for your passphrase, then append an ASCII-translated signature to your file in the file gorey.txt.asc ( or gorey.txt.gpg if you elect not to armor your encrypted file):

	
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

A is for Amy who fell down the stairs
B is for Basil assaulted by bears
C is for Clara who wasted away
D is for Desmond thrown out of a sleigh
E is for Earnest who choked on a peach
F is for Fanny sucked dry by a leech
G is for George smothered under a rug
H is for Hector done in by a thug
I is for Ida who drowned in the lake
J is for James who took lye by mistake
K is for Kate who was struck with an axe
L is for Leo who swallowed some tacks
M is for Maude who was swept out to sea
N is for Neville who died of ennui
O is for Olive run through with an awl
P is for Prue trampled flat in a brawl
Q is for Quentin who sank in the mire
R is for Rhoda consumed by a fire
S is for Susan who perished of fits
T is for Titus who flew into bits
U is for Una who slipped down the drain
V is for Victor squashed under a train
W is for Winnie embedded in ice
X is for Xerxes devoured by mice
Y is for Yorick whose head was knocked in
Z is for Zillah who drank too much gin
-----BEGIN PGP SIGNATURE-----

iF0EARECAB0WIQTqB4m/1H4DJDF8rFvJMHuGjDh9RwUCYafiJgAKCRDJMHuGjDh9
R+cdAJ9PFlzd50a610gMS+p+ldHXQhRdWACg3qCJOFhbm52H1WgrdUQVm9Ax20w=
=wl3+
-----END PGP SIGNATURE-----
	
	

Signing a document into a separate file

If your resume is a PDF or word processing document, it's not convenient to append the signature directly to it. In that case, you can sign it to a separate file. To create a separate signature file, use the detach-sign argument:


	gpg --detach-sign --armor gorey.txt
      

This will ask you for your passphrase (in a separate window) and then create a file called gorey.txt.asc. Beware that the .asc filename is also used to encrypt and armor your file, although if it already exists you will get a warning that it will be overwritten. The signature file will look something like this:


-----BEGIN PGP SIGNATURE-----

iF0EABECAB0WIQTqB4m/1H4DJDF8rFvJMHuGjDh9RwUCYafe+wAKCRDJMHuGjDh9
RwvaAKCLrbLAuYFuvhBwqrpk9EzX2dwJCQCdFfPM93gqtcvPrDLePeixajiKMyE=
=c/+d
-----END PGP SIGNATURE-----
      

You can also create a purely binary signature, which is not human readable (or even recognizable by many programs) by omitting the --armor argument. This file is called gorey.txt.sig and is about 50% smaller than the armored version.

Verify a detached signature by passing it to gpg; gpg will assume that the filename stem is the name of the file to be verified:

	  !:/tmp> ls gorey.txt
gorey.txt
!:/tmp> ls gorey.txt.asc
gorey.txt.asc
!:/tmp> gpg --verify gorey.txt.asc
gpg: assuming signed data in 'gorey.txt'
gpg: Signature made Sat 01 Jan 2022 11:51:03 AM EST
gpg:                using DSA key EA0789BFD47E0324317CAC5BC9307B868C387D47
gpg: Good signature from "Charles Shapiro (correct email address) " [ultimate]
gpg:                 aka "Charles Shapiro (cshapiro) <72300.3632@compuserve.com>" [ultimate]
gpg:                 aka "Charles Shapiro (New email address) " [ultimate]

      

If your document has been altered, gpg will tell you so:


!:/tmp> gpg --clear-sign gorey.txt
!:/tmp> vi gorey.txt.asc 
!:/tmp> gpg --verify gorey.txt.asc
gpg: Signature made Sat 01 Jan 2022 04:04:16 PM EST
gpg:                using DSA key EA0789BFD47E0324317CAC5BC9307B868C387D47
gpg: BAD signature from "Charles Shapiro (correct email address) " [ultimate]
!:/tmp> 
	  

A note on fingerprints

Your key fingerprint is the 41-character string which gpg displays just below the word pub when you pass it the --list-keys argument. It is a secure hash of your public key. This is a convenient handle for finding gpg keys on-line or in your local keyring.

But a subtle problem has arisen here. For many years, people referred to their GPG fingerprints by the last 8 characters (32 bits). So, for example, they would have business cards printed up referring to "57e37087" as their GPG key. Alas, increases in computing power have made creating public keys with the last 32 bits identical to existing keys easy. So just giving the last 8 characters of your fingerprint is no longer a secure way of referring to your gpg key, although the last 16 characters is for now. Many key servers return matches on the last 8 characters of a fingerprint, so a bad actor could create a fake key which looks real to casual inspection. Early versions of gpg also used this 32-bit convention, although this problem has been patched as of the current version.

To make this extra crunchy, the happy folks at evil32.com created a bunch of fake gpg keys with the same last-32-bits as real ones, then revoked them in 2016. This meant that if you search for one of those keys by email address on many servers which use the old (32 bit) way of referring to a key, you may see revocation certificates that look authentic. The way around this, of course, is to always search for a key by its full fingerprint, or at least the last 16 characters (64 bits) of the full fingerprint. And you must verify that a revocation matches the allegedly revoked key fingerprint exactly.

Here is an example of a spurious revocation located by name :

pub unk(#0)0/0d1cc5c42c9888284dfc9730ea103ba357e37087 
	 Hash=7bf3c2f042c4c39d31067c8795cf4f70
sig revok ea103ba357e37087 2016-08-16T05:12:14Z ____________________ ____________________ [selfsig]

pub dsa1024/0e573da0f13969ef1dd5acaa3798e3d757e37087 2000-05-06T11:48:46Z
	 Hash=4eae0cbb87b75a7b32da0e618fd24504

uid Klaus Knopper 
sig  sig  3798e3d757e37087 2005-05-13T10:23:10Z ____________________ ____________________ [selfsig]
sig  sig  3798e3d757e37087 2000-05-06T11:48:46Z ____________________ ____________________ [selfsig]
sig  sig  3798e3d757e37087 2000-05-06T11:48:46Z ____________________ ____________________ [selfsig]
sig  sig  dbd245fcb3b2a12c 2004-04-02T21:04:30Z ____________________ ____________________ dbd245fcb3b2a12c
sig  sig  9710b89bca57ad7c 2005-06-12T22:18:32Z 2005-06-26T22:18:32Z ____________________ 9710b89bca57ad7c
sig  sig  9710b89bca57ad7c 2005-06-28T07:04:48Z 2005-07-12T07:04:48Z ____________________ 9710b89bca57ad7c
sig  sig  9710b89bca57ad7c 2005-07-18T20:23:09Z 2005-08-01T20:23:09Z ____________________ 9710b89bca57ad7c
sig  sig  9710b89bca57ad7c 2005-08-04T07:11:08Z 2005-08-18T07:11:08Z ____________________ 9710b89bca57ad7c
	  

The last 8 characters of the revoked key 57e37087 match the real key, but the next 8 ( ea103ba3 ) do not match -- the real key's digits are 3798e3d7.

Steganography! (hiding your data)

Sometimes you need to hide things. Gnu Privacy Guard and the properies of some file types make this entertainingly easy. In particular, .jpg files are compressed in a way that ignores excess bytes at the end of their image payloads. So, given an image file, we can append encrypted data to it without affecting its function. In order to retrieve the data, we will need to know the original size of the image, or the size of the encrypted file, of course.

So, given this image:

We can hide and recover encrypted data at its end:

	    !:/tmp> ls -l gorey.txt.gpg
-rw-r--r-- 1 devel devel 662 Jan  1 12:09 gorey.txt.gpg
!:/tmp> ls -l notreal.jpg
-rw-r--r-- 1 devel devel 548319 Jan  1 12:09 notreal.jpg
!:/tmp> cat gorey.txt.gpg >> notreal.jpg
!:/tmp> rm gorey.txt.gpg
!:/tmp> tail -c 662 notreal.jpg > gorey.txt.gpg
!:/tmp> gpg --decrypt gorey.txt.gpg
A is for Amy who fell down the stairs
B is for Basil assaulted by bears
C is for Clara who wasted away
D is for Desmond thrown out of a sleigh
E is for Earnest who choked on a peach
F is for Fanny sucked dry by a leech
G is for George smothered under a rug
H is for Hector done in by a thug
I is for Ida who drowned in the lake
J is for James who took lye by mistake
K is for Kate who was struck with an axe
L is for Leo who swallowed some tacks
M is for Maude who was swept out to sea
N is for Neville who died of ennui
O is for Olive run through with an awl
P is for Prue trampled flat in a brawl
Q is for Quentin who sank in the mire
R is for Rhoda consumed by a fire
S is for Susan who perished of fits
T is for Titus who flew into bits
U is for Una who slipped down the drain
V is for Victor squashed under a train
W is for Winnie embedded in ice
X is for Xerxes devoured by mice
Y is for Yorick whose head was knocked in
Z is for Zillah who drank too much gingpg: Signature made Sat 01 Jan 2022 12:22:02 PM EST
gpg:                using DSA key EA0789BFD47E0324317CAC5BC9307B868C387D47
gpg: Good signature from "Charles Shapiro (correct email address) " [ultimate]
gpg:                 aka "Charles Shapiro (cshapiro) <72300.3632@compuserve.com>" [ultimate]
gpg:                 aka "Charles Shapiro (New email address) " [ultimate]
	

The image shown here has encrypted data appended to it; you can verify this works by adding my public key information to your keyring (see Encrypting above), putting the last 662 bytes of the image into a .gpg file with the tail(1) command, and then decrypting it with your own gpg instance.

This is of course a fairly crude way to hide data. To recover your data, a sophisticated adversary could scan for likely .gpg message packets in your files, or perhaps write a program to compare the length of the compressed jpeg image with its actual length. More sophisticated methods of hiding your data would not need the length of your data to reconstitute it. You could, for example,write a program to sneak your data into the least significant bits of every pixel in a jpeg, and a corresponding program to reassemble them from there back into the original file. I will leave that as an Exercise for the Reader.

Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.