DISCLAIMER: Author is not an expert in cryptography (he is not an expert in anything really). Use this stuff at your own risk. If you find bugs or inaccuracies, please create an issue or PR on the github repository.

GPG basics

The GNU Privacy Guard, also known as GnuPG or simply GPG, is a popular open source implementation of the OpenPGP protocol (RFC4880). The system is widely adopted for securing integrity and confidentiality of internet communications through the use of various cryptographic methods. Important applications include encryption and authentication of messages (such as email or software downloads) via public key encryption and cryptographic signatures.

Like most modern crypto systems, GPG makes use of public key methods. The private key is known only by its owner and is used to create signatures or decrypt a message. The corresponding public key is made freely available and so that it can be used by anyone to verify signatures, or encrypt messages which can only be encrypted by the keypair owner.

Compare to HTTPS

The major difference between GPG and PKI systems (such as HTTPS) is how we exchange and authenticate public keys. HTTPS is based on a system with Certificate Authorities (CA). Anyone can create a keypair for any domain/personal name, however we only trust public keys which have been signed by an official CA.

This CA is typically a commercial vendor which verifies your identity (e.g. via a copy of your passport) and then uses their own keypair to sign a certificate containing your name and public key. The public keys of CA’s are hardcoded in HTTP clients. The main disadvantage is that CA’s are expensive and everything collapses if any of them is compromised or not doing their job well.

The web of trust

GPG uses a different system which does not rely on authorities. In GPG, peers sign each other’s keys, and it is up to the user to manage who they choose to trust in their personal keyring. For any given signature, GPG will check if it was created by a trusted party in the keyring, or by a third party which has been verified by someone in the keyring, and so on: a “web of trust”.

The easiest way to exchange (signed or unsigned) public keys is via a keyserver. GPG is compatible with existing PGP key servers. These servers mirror each other so most keys are available on either one.

GPG key servers might not use HTTPS. In GPG we only trust keys only on basis of who has signed them, regardless of how they were obtained. For this reason it is also perfectly valid to share GPG public keys via e.g. a website or email.

Managing your keyring

It is important to know which version of GPG you are running and where your home dir is. Your home directory contains your configuration and the keyrings. GPG defaults to your system keyring, which is the same as the gpg command line utility and system package manager use.

str(gpg_info())
List of 5
 $ gpgconf: chr "/usr/local/bin/gpgconf"
 $ gpg    : chr "/usr/local/Cellar/gnupg2/2.0.30_2/bin/gpg2"
 $ version:Class 'numeric_version'  hidden list of 1
  ..$ : int [1:3] 2 0 30
 $ home   : chr "/Users/jeroen/.gnupg"
 $ gpgme  :Class 'numeric_version'  hidden list of 1
  ..$ : int [1:3] 1 7 0

Use gpg_restart to switch to another home directory, e.g. for a client which uses its own configuration and keyrings. For this example we store keys in a temporary directory.

gpg_restart(home = tempdir())
gpg (GnuPG) 2.0.30
libgcrypt 1.7.3
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: ~/.gnupg
Supported algorithms:
Pubkey: RSA, RSA, RSA, ELG, DSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

Use gpg_list_keys() to see the current contents of your keyring. It is empty to start with:

gpg_list_keys()
[1] id          fingerprint name        email       algo        timestamp   expires    
<0 rows> (or 0-length row.names)

Generate keys

Use gpg_keygen() to generate a new public private keypair:

(mykey <- gpg_keygen(name = "Jerry", email = "jerry@gmail.com"))
[1] "0E7D600FE234C49B"
gpg_list_keys()[c("id", "name", "email")]
                id  name           email
1 0E7D600FE234C49B Jerry jerry@gmail.com

Import from disk

The gpg_import function reads an armored GPG key from disk:

curl::curl_download("https://stallman.org/rms-pubkey.txt", "rms-pubkey.txt")
gpg_import("rms-pubkey.txt")
considered   imported  unchanged 
         1          1          0 
unlink("rms-pubkey.txt")

Import from keyserver

Use the gpg_recv function to download a given key ID from the keyserver. For example let’s import the public key from Michael Rutter which is used to sign the Ubuntu r-base packages from CRAN:

gpg_recv(id ="E084DAB9")
considered   imported  unchanged 
         1          1          0 
keyring <- gpg_list_keys()
keyring[c("id", "name", "email")]
                id             name              email
1 0E7D600FE234C49B            Jerry    jerry@gmail.com
2 2C6464AF2A8E4C02 Richard Stallman        rms@gnu.org
3 51716619E084DAB9   Michael Rutter marutter@gmail.com

Note that for imported keys, we do not have the private key:

secring <- gpg_list_keys(secret = TRUE)
secring[c("id", "name", "email")]
                id  name           email
1 0E7D600FE234C49B Jerry jerry@gmail.com

Export a key

To export our newly created public key:

str <- gpg_export(id = mykey)
cat(str)
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mQENBFgA6oMBCADHqziSGHibhcJok2LG5p0A0lxdUIurOCUg7P06VYJeKW8hvkPx
SOVQetYvm7FGNim0pR6m7WuZkTUoqge2wopGzOb5ecyfDxd0WvSTHxb2l3IXIlDW
F1woy+oT090YM6JDgsnbTHAXUqS/QW0hHZKECP/ktkH9ZJcIs2sMZ3UrnTwDT18q
+FGmKQVg5M1GPCciNe6upwto7+xWka93ebe/P3WW9JrjAoIXLPX3pX4Ii4RfiW3u
p2RCeJglDAfOPTGKSAxIQdF827Sm3bmbCXTnhWzcRWSpHqC4Eka8j7gNNBZxkvF0
k1lRY0A0SagIyclfHZJyN/UeSUc7OkfxquGxABEBAAG0F0plcnJ5IDxqZXJyeUBn
bWFpbC5jb20+iQE5BBMBCAAjBQJYAOqDAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwEC
HgECF4AACgkQDn1gD+I0xJshewgAmOuhMjYIeiU3FMEy12MoqDdNQRh61aEwSIWo
O1RFwNJm0+yEIojUVwcQEwCgzLvJB5Qv+Zc6X3FcNeY1JbBcauVCuuLpNglNMFGM
ssZhiIkRGWQ2OiJCnNkzJxI4hEvoScuaTrUrTw1SW5kS5yuJZyT3mIXcp63H29Uy
7LyQJnSxuYq8YwWZepfzU+DHuQ/lgsQPXkKlcS5fxEopuUpt2sELb/Q1AuScBIAu
IYbhPOAZcY6VB2PgELuO9r6+BrJUsVqdsCYluhBv9LT4/z57Nwd+oHtCJARjJqsr
Gb6018dyrRioTO+5nSihuj97rdRSjCyba1YGGd9yXg1GVqbpuQ==
=H9HW
-----END PGP PUBLIC KEY BLOCK-----

If you also own the private key you can export this as well:

str <- gpg_export(id = mykey, secret = TRUE)
cat(str)
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v2

lQOYBFgA6oMBCADHqziSGHibhcJok2LG5p0A0lxdUIurOCUg7P06VYJeKW8hvkPx
SOVQetYvm7FGNim0pR6m7WuZkTUoqge2wopGzOb5ecyfDxd0WvSTHxb2l3IXIlDW
F1woy+oT090YM6JDgsnbTHAXUqS/QW0hHZKECP/ktkH9ZJcIs2sMZ3UrnTwDT18q
+FGmKQVg5M1GPCciNe6upwto7+xWka93ebe/P3WW9JrjAoIXLPX3pX4Ii4RfiW3u
p2RCeJglDAfOPTGKSAxIQdF827Sm3bmbCXTnhWzcRWSpHqC4Eka8j7gNNBZxkvF0
k1lRY0A0SagIyclfHZJyN/UeSUc7OkfxquGxABEBAAEAB/9DBHBHcRlCMiIvkZCs
7Ai0lubTl05Z2M11gLX6F2tbiZK4g7EwUmRNn/02G47j3xgefvGL29Kqx7dM/8z5
BdD9tJauE7ifhqQ7tK+0ldHSUMDFjQoO4eZX9yGJKCpkjigfes2f4scYvO95Fk+3
tsJPuOmS7a22jEW5cOc0UaFdUqAGS5hITCCjenk0ypHaMKZHBCEb4YqKO4Qb54ZE
nCXbHU+yjLmJnYlievp2+FGlTrhKRbZt4KpKodBDNolH0UML6twvSYCfqXguRV1H
f9WHyf9V9qrJbUEKso4QkYxrdyHzOmEBfXzqmhmp6wXsTVIEqKDNbi0U6Cd1heBl
z2JlBADSymWymq57Oao3F+hgFOWMQshlvXMUA74nVn2OZ/Ku/Hm+mwUH6pD2yAPg
/JS6ldl9SRMF3eAwMxkFvrHLki0y4wTmTArbpcY/QfWCErcou4Zu1xOliavgw4xO
ARDPGHK/OxKjo1simKCkNaXymmG1AE+Z3ZH3EZ42vsA5FMb1GwQA8n4stq2icx8r
5pUB+2NUP6iM2ehE5Qt9CjHVy1V9cGrAp4rMZD16nyzND7KcMMp56gcio9fDQZF5
Bnn2+ymoFMByqjm8jaq9/VQVe24NpRoRRicbdnpILzPxdmXGbOI7mqmJ2yNvmw0r
iLXcANphcTTdknf2Czv8QhRNNNFfDSMD/0jH6PD5SrF06D8zXbHEMRTc7QHGr47g
rNK1XvqBi9tOw6j8lHmkVq37XPZIgufE6AYkkumfzJZzOrgfXx6a/xJzPVAwlgWx
Z9XMqkbiYwgwAEsylWXCIr1GGbMwyuZLzYK9SHiUEpP57Ybeeg+NzVVkmMOkb6Ku
UzVR4M+26f3fRxK0F0plcnJ5IDxqZXJyeUBnbWFpbC5jb20+iQE5BBMBCAAjBQJY
AOqDAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQDn1gD+I0xJshewgA
mOuhMjYIeiU3FMEy12MoqDdNQRh61aEwSIWoO1RFwNJm0+yEIojUVwcQEwCgzLvJ
B5Qv+Zc6X3FcNeY1JbBcauVCuuLpNglNMFGMssZhiIkRGWQ2OiJCnNkzJxI4hEvo
ScuaTrUrTw1SW5kS5yuJZyT3mIXcp63H29Uy7LyQJnSxuYq8YwWZepfzU+DHuQ/l
gsQPXkKlcS5fxEopuUpt2sELb/Q1AuScBIAuIYbhPOAZcY6VB2PgELuO9r6+BrJU
sVqdsCYluhBv9LT4/z57Nwd+oHtCJARjJqsrGb6018dyrRioTO+5nSihuj97rdRS
jCyba1YGGd9yXg1GVqbpuQ==
=uCsd
-----END PGP PRIVATE KEY BLOCK-----

Delete a key

Delete a key from its ID or fingerprint. Let’s delete the RMS key:

gpg_delete('2C6464AF2A8E4C02')
[1] "2C6464AF2A8E4C02"
gpg_list_keys()[c("id", "name", "email")]
                id           name              email
1 0E7D600FE234C49B          Jerry    jerry@gmail.com
2 51716619E084DAB9 Michael Rutter marutter@gmail.com

Digital Signatures

A digital signature is a mathematical scheme for demonstrating the authenticity of a digital message or document. If you sign a file using your personal secret key, anyone can verify that this file has not been modified (i.e. the hash matches the one in your signture) via your public key.

GPG signatures are widely used by Linux package managers such as apt to verify the integrity of downloaded files. Typically the public key is shipped with the OS, and the private key is owned by the repository maintainers. This way we can safely install software from any mirror or network.

Sign a file

Let’s use the private key we generated earlier to sign a file:

myfile <- tempfile()
writeLines("This is a secret message", con = myfile)
sig <- gpg_sign(myfile, mykey)
cat(sig)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAABCAAGBQJYAOqEAAoJEA59YA/iNMSbMyAH/is6uYJhNMd4B09G+hftavSy
EFJ9qLhcpMnffuAVaq8MB9Br6rGU17OJvLxVgxt68vbN+sCtHmDYsjwrHeIX1MPz
cd6PW8Qkxb/QxCShbbqJ6u3C0ADCeNF6lplai9uxBVxpmguqXNpsL9gQuRLhZ6OF
Zpg9rUIJdIxx4a0tVQJTbMRgGSQ8N0EvbbR5Zrz/m6ug3PTcx5G1tmI/stPPOgSl
PkplJASw9zXIx/ugC3hJrkUk4GSRzP44TGVdXlKz7M1uiagkbJy3zIViYhRn/Mh0
Zl09l+yQ5omAyh2jBF4ZldLcaRTDfH90EWfvQ9pza0yyKZlvyuYlluZcwb4lJQ8=
=hEcd
-----END PGP SIGNATURE-----
writeLines(sig, "myfile.sig")

Verify a signature

The gpg_verify function will see if a signature is valid for any of the keys in the keyring:

gpg_verify(myfile, signature = "myfile.sig")
                               fingerprint           timestamp   hash pubkey success
1 459AB4E97A1E2B31D76478D00E7D600FE234C49B 2016-10-14 16:24:04 SHA256    RSA    TRUE
unlink("myfile.sig")

Let’s verify a Debian file. The Debian page on CRAN says the following:

The Debian backports archives on CRAN are signed with the key of Johannes Ranke (CRAN Debian archive) jranke@uni-bremen.de with key fingerprint 6212 B7B7 931C 4BB1 6280 BA13 06F9 0DE5 381B A480

Let’s import his key so that we can verify the Release file, which contains checksums for all files in the repository:

# take out the spaces
johannes <- gsub(" ", "", "6212 B7B7 931C 4BB1 6280  BA13 06F9 0DE5 381B A480")
gpg_recv(johannes)
considered   imported  unchanged 
         1          1          0 
# Verify the file
library(curl)
curl_download('https://cran.r-project.org/bin/linux/debian/jessie-cran3/Release', 'Release')
curl_download('https://cran.r-project.org/bin/linux/debian/jessie-cran3/Release.gpg', 'Release.gpg')
gpg_verify('Release', 'Release.gpg')
                               fingerprint           timestamp hash pubkey success
1 6212B7B7931C4BB16280BA1306F90DE5381BA480 2016-06-22 09:26:03 SHA1    DSA    TRUE

Looking good! We can trust the checksums in the Release file to be legitimate.

Anonymous Encryption

GPG uses public key encryption. You can use someone’s public key to encrypt a message or document, in a way that only the owner of the corresponding private key will be able to decrypt. This is a great way to send somebody highly confidential data.

Encrypt a message

For example we want to send an email Glenn Greenwald containing top secret information that may not be snooped by our ISP or government. His homepage at the intercept shows his GPG key in long form.

glenn <- '734A3680A438DD45AF6F5B99A4A928C769CD6E44'
gpg_recv(glenn)
considered   imported  unchanged 
         1          1          0 
writeLines("TTIP is super evil!", "secret.txt")
msg <- gpg_encrypt("secret.txt", receiver = glenn)
writeLines(msg, "msg.gpg")
unlink("secret.txt")
cat(msg)
-----BEGIN PGP MESSAGE-----
Version: GnuPG v2

hQIMAzCzOshC83uFAQ/+OTJsUScxxCwBHAGE+ruZk8ZUd2z4+Sz0FKk/lwP7g8Am
IO4gc6T/yKC+qR/eeaXLP2FQKsG/qzmrByZEZhkvgObIWL4Tr/KUeSFbdz3xcAUs
8M9kPrvtnz7hI6dygasILHzoa7YZe62+1M7UiIG4K54gNFfr7VcOib+gNNDoXeif
GyMT46/67dd9PPDXA0dVbZ/ZKCLO4fUInPoT7caYzSsadACp1l/HOf5J3UWlHz61
FguDeqpR8fhM1xJ7pzc5BUdjUmX0pV/5DupuyLHgn2WF7WTILgTcdX/wjPRP7qbn
X+8ZGKA7676xxyNN6bmEJoqh+hvb7ieEYUVRcMci8TegOW+wvo4h1NtA3Y6L5eb4
fus43xIZ+ZLqhSkTeu5ZQEpj2clt5CrtHskliigK6dYK9zMYTeQ9e3Gnbf4eDjFq
kjkwDHlFKxu/IkkDsrtQgIlxAFvhvc3Ar3fr212EyWwmdmmdlDvZ3F3rO6gpZJ+R
V7TQYiQS0E/oUXqmJRJCm7pFpLHRI/olOsMjXUsZDkESTzqN2WBy+RY4FK/R7iNC
SNCIXfxbHjTNgHctzhKK3La9bBIh7RpVydACzY6VdtKFlKJBcLbrH77QgjoBvWk5
PqjVAZonputklXjKfDeO3sQ+7UUrcxrtF/skZjnYTbUmqymC95hMhZFErgUj8xLS
TwGBzmbW20WtuhukGqxaRdvv2m9hfFHvZ80Qkoc5m0cY8kwbmOZWIOtWgyXVxac8
nMfh5IMaaI53XUwNZxrWsNwYZ/OfyRhZdkS8dKos4Ds=
=OcB/
-----END PGP MESSAGE-----

You can safely send this message over any channel (email, twitter, etc). Nobody in the world (not even ourselves) will be able to decipher this message, except for Glenn Greenwald.

Decrypt a message

Decrypting a message is even easier, you don’t have to specify a key. GPG will automatically pick the correct private key from your keyring, and error if you don’t have it. For example we will not be able to decrypt the message we created above for Glenn Greenwald.

# This will error, we do not have this private key
gpg_decrypt("msg.gpg")
Error: GPG verify signatures and decrypt message error: Decryption failed

To demonstrate decryption, we encrypt a message using our own keypair (for which we own the private key).

writeLines("This is a test!", "secret.txt")
msg <- gpg_encrypt("secret.txt", receiver = mykey)
writeLines(msg, "msg.gpg")
cat(msg)
-----BEGIN PGP MESSAGE-----
Version: GnuPG v2

hQEMAw59YA/iNMSbAQgAndFvuEYC0vxnQQI0CBqp7gbNOw4CpmJ7v75pYTuumbu0
xNFrGN9qJ9P3G2dE43AHpDz78sZOMlLXWghLC6OYrS+fR+aydtcKPjKylTNENDzf
TQmTL/EXZ0GJ3AelGXJIO5jbgU56idl69/6hOR3dYSFuyGMA2YJhSzToCC0/abFB
rz6eRXLcJwI1727d5uj6kGbTPlK3ztFAcfpaSH/6zPdGu+95stGzwS0W8Y8UV+e9
njZkIG8bd5/sW552X6n6sD0Y1sUd+tLqVbFkwoFcltvQIcv4ehOBWtAQElt45rNT
MPGeKbK7iCe66qbdKTrhjz/HL4GzDC5tjSWk51XzsNJJAYvIQDrxK311dTaD8OMy
EyF/j9peSa7tlt54tDzLHLTdVybW8xpJxDOaooCw5iizdeTQNG+IG67P588RfzZ0
IDJuNNbzVzm0Bw==
=a2/T
-----END PGP MESSAGE-----

Decryption is simple, given that we own the secret key for the message:

gpg_decrypt("msg.gpg")
[1] "This is a test!\n"

Authenticated Encryption

So we showed how to encrypt a message so that it can only be read by the receiver. But how does Glenn Greenwald verify the sender of this information? Perhaps someone is trying to leak fake documents?

Sign and Encrypt

In signed encryption, also known as authenticated encryption, uses combined encryption and signing. The public key of the receiver is used to encrypt the message, and the private key of the sender to sign the message. This way the message is both confidential and the integrity of the sender can be checked and verified, only by the receiver.

msg <- gpg_encrypt("secret.txt", receiver = glenn, signer = mykey)
writeLines(msg, "msg.gpg")
cat(msg)
-----BEGIN PGP MESSAGE-----
Version: GnuPG v2

hQIMAzCzOshC83uFAQ/+NC74T0yPgbN1hkIRoP9uz8I6isucVI1xTwYwC+fFSMCY
RprHzCP+oLRQvqiHp1sSeiwE+ppTknlQIWt8vrXYIP7hgehTb7lVWZMNoFV60EwO
qaoP0qhFNeHmYnHAHL0PXLDQ/qECGLVlpZCdUFXXh6MMvEiX1EW9eNWoPLi3pmB5
krQWi7MkRuL3zNo2sYlTFYiEpa22bZYQWGwtFn2F19C3A8Fzi9GnVN+579Y1ceB/
7bo7dWm6qzN6Ra3WYPtLVtwhxdxFN4OloEff7u9EldVDrx/TaNDtfSueSHf9hCLZ
WzlEXAllrY+6kF4EJ7jVbYrAwTgVFmKUK/o6VgqUwREe5la9fWq/UUJmgXldx5vp
tyjIgL/0bXFeAKZ9/VoFKB4lDfoWvkm33lOQNaLWPHPpoIN0ac6aXjCo3xQnFLQQ
5YCnNgPTzzbD1j7OkHPnDuGke4naNYuIXcpaKLTei6K72/51JGznNIipZgyqueoJ
w9COV6444di6xS4sRYqbFzaobBR/SkA7RlSoCJmRVAD4JH8Uv5JWQtkdmKpyAjyQ
DCa5gqn2k7tLFQntU8vWzWuu7yhMFPf0x4GCAiUQBx1mzCrXjWXLF2DMW6m3q505
mllKFrPl0J7l9cE1R0vyoSdMtN8fxf/k69PMDu57d2+Xow+EJWobdTIMwVCemMXS
wLwBsNGFJ0H7YPhFyD/ZBx6AZ0nMV7YLdxpxi2tdoDGMohDvTDpLJEvY57f0byrN
eqcagLYDags2ZwHlwaGJnxzJ1Rk5DT47A10W48FPFEuLPNzAA/oVsm0xdKZp4yen
4rCk2DbU50W5diHrCPLI5mHj7JHb9rmC1QEkkFi/ttzknuaZP/EzxDGyigG62nkz
7z1YFpzhdK7H7J/Mevgge+vrhNzPupwRRvLInJFuRO0fJO99F1HkaQuZ/4ALLgZE
4rRB6LP4MrysF40+iXWQixdapf2z5C5MVyNa05aFiopxKSjw24fYrCN+eqXuUSIv
zgvVvYyvMp+QsYTM4irxpaPbK02d2lq2ftx4JeN10jyIGL+LDpUqcsz8YRv2aI2j
fdiNvTwQoP0Y2+A/BpI1PQgozKvjJoDqIELLiHhi25MvzRVA/HtbAt1DTFbmHUKS
5qP9bM9HChmFYCNI705qTZOl/E23vc3soAui63u9xZ4m59b6wytHMmcYBCU7Lg==
=9r9F
-----END PGP MESSAGE-----

Decrypt and Verify

If the encrypted message contains a signature, it will automatically be verified when the message is decrypted. The function raises an error otherwise.

For purpose of illustrating authenticated decryption, we encrypt and sign using our own key (which usually does not make sense):

msg <- gpg_encrypt("secret.txt", receiver = mykey, signer = mykey)
writeLines(msg, "msg.gpg")
gpg_decrypt("msg.gpg")
[1] "This is a test!\n"
attr(,"signer")
[1] "459AB4E97A1E2B31D76478D00E7D600FE234C49B"

The signer fingerprint (if any) will be added as an attribute to the decrypted message.