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 NA
 $ gpg    : chr "/usr/local/bin/gpg"
 $ version:Class 'numeric_version'  hidden list of 1
  ..$ : int [1:3] 1 4 21
 $ home   : chr NA
 $ gpgme  :Class 'numeric_version'  hidden list of 1
  ..$ : int [1:3] 1 7 0

Use gpg_restart to switch to another GPG executable or different home directory, e.g. to build an application which uses its own configuration and keyrings:

gpg_restart(home = "~/myapp")
gpg (GnuPG) 1.4.21
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-E, RSA-S, ELG-E, 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_keylist() to see the current contents of your keyring. It is empty to start with:

gpg_keylist()
[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] "756C53BB8F92FDDD"
gpg_keylist()[c("id", "name", "email")]
                id  name           email
1 756C53BB8F92FDDD 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_keylist()
keyring[c("id", "name", "email")]
                id             name              email
1 756C53BB8F92FDDD            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_keylist(secret = TRUE)
secring[c("id", "name", "email")]
                id  name           email
1 756C53BB8F92FDDD 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-----

mQENBFf/TDUBCADGjhZ6vhWlRWndV6GmH3uWC4bxzaTE6tOjzm1KJf5ujzrvH9kp
XOfWkuWt6slBLAJTJzCowQRYx1ROLGbKoudpTs9KjO3CvPjD/b+MQR9TnzpiTa5y
tEhclv8y9F17jY1pZrnp/oOBfyYtNbcK489Vy7yFbkOJu23OWdpsRb8jthZu/2qZ
cF7tyHR3JFMymtoM6SB741hheGkNqDexi0u+1sHPRsAB5GiamWiWsZRIewwniWgo
a4HAQucBXdt0x7SuZGTQ4FnFF5M2RJPatyWNeD20ja+C/IZDtKEAKBeXDpH5npzF
X+M0r1jMamVEtzxH/KXk8aV8CPpsN7UznxEXABEBAAG0F0plcnJ5IDxqZXJyeUBn
bWFpbC5jb20+iQE4BBMBAgAiBQJX/0w1AhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIe
AQIXgAAKCRB1bFO7j5L93cTmB/99pbivoKpHQmAfp0rK1k2j2ae4nLeRUBQU8nWa
S66Dm3iwCvWi7JdmIwSpC6kbo8wAedvr61F899RE5vgDRgp7aEznmPugdBUV61qs
1P58gl7flqStkURIwJ8Lf6ZOUl31Z63OtGahav9Sgi+LWa0eGiLYe09vJ4IPH9aF
wSTDKMzEALvzPLxJ+yaAOoB0jlQYmeHUuuKcEo4x9tw7rShHr1RbtI0rh2Fw7i0s
BeBvXlbNky1obYxS/53L9VDAWGjuuY7wHrbjjMpyn4g+hWTYzbrehmmNxQMGLIET
KuGZMjXOoDmQWWqT71ROXh8eTv3vuBe5EwIfJfu5lbcTgtV4
=eZxb
-----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-----

lQOYBFf/TDUBCADGjhZ6vhWlRWndV6GmH3uWC4bxzaTE6tOjzm1KJf5ujzrvH9kp
XOfWkuWt6slBLAJTJzCowQRYx1ROLGbKoudpTs9KjO3CvPjD/b+MQR9TnzpiTa5y
tEhclv8y9F17jY1pZrnp/oOBfyYtNbcK489Vy7yFbkOJu23OWdpsRb8jthZu/2qZ
cF7tyHR3JFMymtoM6SB741hheGkNqDexi0u+1sHPRsAB5GiamWiWsZRIewwniWgo
a4HAQucBXdt0x7SuZGTQ4FnFF5M2RJPatyWNeD20ja+C/IZDtKEAKBeXDpH5npzF
X+M0r1jMamVEtzxH/KXk8aV8CPpsN7UznxEXABEBAAEAB/9h3sfV2LxyqrR0Fi3n
5LzWSPhLWEJPjjCkHaxhUDkJ5i/2joxMhy7/5YixIXnyVe8PbdHeXMX3vMkhf2Fr
oMDQd7hV1wGba+pzMTCpwa/fWC0/J2tFvvesLAG+7BToQe3idnBNltbbjgETZ9bB
12jrsXFLk/5Wdby6ai/wDrEYuFigmmLc8r41oiTPeAmSpmAXU9RKtGN/GVxsMhLe
QsUK1VLrUSzGjxNMLAc3QMppGRb031LByHqLsCP4Wq2h7aqaSM7Y83CXpTia219T
DsACZh4hhU5+RGGyqh8C1eFEpRbl1Fuc9g08XV2ZuQtyzjnlRfjIRPO/koV7+UjG
MBqBBADILs4kxag1QayXHunpX4QjRyc44K3pyQ43BWqrafODEWTB+J9/7GizG9CL
YCjoAZM1Z1AqRpc2L//xYccP5x7PdUMPqzJmyk0UOZ4LRu/T1evFKbp9T5p3SyUp
oV0Fi8GPxO/B5ldfaCKqZOmcl/JJPtB4VKfSrUFBBAM6LbQgnQQA/esWvrtkijlb
LflIivXUwjPI6cX/LYWMFdbtzRHJdicWN5+XdqvlQF+5ny8dxW5PrxR+KHHoGrbZ
d+bb2eh7h7ZAEk/iXeg7mHjg59AmAtdTaKHf4CLAA6FbuHDCanbF4Ud8gXhQ1ZAc
Y2l6qB8P6V8kFBd4Y5KArcD4uOdKKEMD/28F0Lh1cNh2SGc0RC+J3a5BpLJOlNF0
zw9sAxy1acb4Ju7Jb84angTgw6nYrF96qAP1QngNJU62+1bs5LwwVJ7K81Z//ZVm
f0iyjBBZfFILzP2cS8jvVfW1yvop8YKyljoKEBLvo1IN77YjoQLtuRUOKGuMwJkI
Sl7VksvUUrbKR/60F0plcnJ5IDxqZXJyeUBnbWFpbC5jb20+iQE4BBMBAgAiBQJX
/0w1AhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRB1bFO7j5L93cTmB/99
pbivoKpHQmAfp0rK1k2j2ae4nLeRUBQU8nWaS66Dm3iwCvWi7JdmIwSpC6kbo8wA
edvr61F899RE5vgDRgp7aEznmPugdBUV61qs1P58gl7flqStkURIwJ8Lf6ZOUl31
Z63OtGahav9Sgi+LWa0eGiLYe09vJ4IPH9aFwSTDKMzEALvzPLxJ+yaAOoB0jlQY
meHUuuKcEo4x9tw7rShHr1RbtI0rh2Fw7i0sBeBvXlbNky1obYxS/53L9VDAWGju
uY7wHrbjjMpyn4g+hWTYzbrehmmNxQMGLIETKuGZMjXOoDmQWWqT71ROXh8eTv3v
uBe5EwIfJfu5lbcTgtV4
=6wKW
-----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_keylist()[c("id", "name", "email")]
                id           name              email
1 756C53BB8F92FDDD          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-----

iQEcBAABAgAGBQJX/0w3AAoJEHVsU7uPkv3daQoIALIEijsgXhrnF4aNbYqbVTyd
p78zSiGLExlgAxCs41+YkVpvmxW8gLzRNI3q99I6yRbFrziQ3pH7VaEhRFkXyLQl
B9Z5INraMe7QHSQbca0yE8LKQlfXYd37JBdbogEXh/Y/W5Go2xMVDsMIigh+BzT9
5QV4c6XzQjSemIB2pLQcnaiJganzsPZNTVUkz0mzcV81O/dLBjpSA6Ic1BaB628U
GChTU8kk/pEVAotM+o5ahh73xMVO3+5D9wgoN0V6aqKXEXeEjc0y10tLDDnp0Ki5
X+EkkwvR/LZrOJSdohxN2qSdK5rIN8AR3QzaKSOauD0NpPUMK37d9k9RtRseGng=
=Tna1
-----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 5D72313BFCD83F17268143C3756C53BB8F92FDDD 2016-10-13 10:56:23 SHA1    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-----

hQIMAzCzOshC83uFARAAiBXEr9rN7phsBdQIbpn7AsVA415ebv5PWXbElaVYAGhA
LXMfpJqaCdQHtrZbhhU93oqUhTKet55eZZ0G5PZp7zDC9p3RJomnuOxjTWOhlmZD
Qh1JTEvX532lXPwsjL3n6vP9Q+FuMIyEHDvapphCy/qJxsTkKP6498KqKKXRNFuQ
Ts9VgnYAVVN/R1/I4PmuIbit8jEbUO9bI9iocaUdiKbZ+9HPzNmkqcPqytFsUhBm
zY6qGWKK1K0sB7jBpS6zHYa+P0glx/AUVUYJ0n1KcenZWY38ml1epP2wU1QkMg9q
d902elCALmnD172qPz1YS0PSYEXXNPuh6K64blIixr8D6N/2yZ/ufI0bnqApz3Uz
7iiETPZlDgbb9LbCAHfqsGoi62LYzeKvTPa4OFvOgBmbUAPX1xw88oJWxXLtAra/
G+b70plZSCbZIG1hKvPqIgly4VE/kk861FT1zqX9rmR/EiZstLkT877NG35Iyxme
m6trD1bYuFgnXRadVGNmysstyPoTFjZUp5GJ/QPVINkSF5ukEfDXt/d5DHVQQe8l
Sa8GjuyMTxVtlsOaHv4bgW0NLQT9mMNBbvbmIilSsU6YjuTjkgGlny4l6j9C2CbG
d3ikSnWK17iNkwYcBH0SRLWcc0OrUyDF4b3x3dMrdqgRTx+y0LggfwQQaZGpIjrS
UwG72npFcit1YYaJNhvGGXernfDGQgDAE75FHRywtkOOEfJWm2M7wtLCD2UEGyFt
nHrCbfP/bbk2dxC1asc7THTzy+COFK6AJUCYA08yoH5PLH7U
=Fhu5
-----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-----

hQEMA3VsU7uPkv3dAQgAkqAkV83R9Finj+3feRRvqu8RymLMP54uVyC2EuWlq6Zk
zGjj28/fw49fHXDlDQdXFwx6VUwRY6cAoL8hLp29WozGJXZswRjKeoi6gv3AWmlU
VmuocxJ0ypFQtfXgaXzc++K6/iLXnikEkY4WmafvqE+ERb6H/IJ66GgyEL4lhWkg
bMwUmscUcld38J/OPwcFINkFDDjfG/2c/nzBjECcNqMUAlPhYHezSmgUt7Dtj5fT
kFcEVe14VXK05w9Jfi1ZwsTLqROJgeZMNeGHFwdKykMdM7qznqOhRmWIySUvJWnK
rvBdGhP003rdSmnrnKQx6eBQv6zgiFmyX1dGYeJW/tJNAdOmURDYWq5dplKOk4n1
pRz5KWwD8X9zdmhN4BBDDGKhOdadEuusIlW2RtRIyxGY4+qEzws96SccjcFFGRkC
40NqEu3w+MJeTCbZdA4=
=UETx
-----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-----

hQIMAzCzOshC83uFARAAhLZsvVeF8hzpwgGiVBytVMBmwZ5m4SRaVpVUKNbSsDbT
7CehpWTQnTd3x3dtuEU3KmoYdNxm6MmoTjUV3Gxo+A0rYM+zdYwNp5A1Qsc+1jrk
V94Km30QoKFRHnPykV6d2FoN8B1G52cWFj74kzM1ajVTq9eVPtmaOwgGKweFG2Cj
BMAIaYdnVOV2V8B4Cis9UlCo0uHIJhtzA0fn/U3bU0mYXKR8Y6AR3Cjwv1NuDIOr
XY3EWzpOpAGABFM5uMVCXIRhdClluCJabOZGi/c4/Nijq/+Gb3D7AMm4OqX0I2cN
VAAPjg1b1Zb7v6dGMXGT+jN5TVxJgMchqfQL28iGZG/7UZGrO54jrf0AtZnV8U8V
5pigojAw8xnr4It25kYUwMRJAByZxgedpr8sojXH9szbYDsy6iRMDVuGXNd5ukBb
pGgdnWZpNGVnu9piwEXvQUNMeKTyIAcnsLGxEQKGpOQNFsorawOu3WyY0b3IlH6o
3RtjrNAZ7SGsmqgyid/JLyRV+j7QhqhUywEv2gD9Y2I+ZzSc/z4WOpcoJ/et7Msh
9UFUoDWY7kn0zbVM9NC2VR9oyGf/fOKTrTMZQrf5iQFo7WOIk4xodM9h3FbGecQV
mvY4hD7lMuJs313MT/hEFWcEq8/gpM5q1VXeJXSmHHW5qvp5SIvo3VAZ8hqUTgrS
wMAB0RH7IYmaKaGccherH/qMKnODd4aY5qIyi4FA8x/hlgMdaPSjz9SKtgb1oDeu
43Vh5Q5ZMc5FdgVWCWAfckVDHdE0T2vcMs+AXQyc381r00XPDpEZiQjUkqy3s6T3
UmKIq7aXUGNgN1Y5kdDlXL7DyHHDPxmONc4361plg+w9gyYltYjwThCz0u3+maII
GZrSzWLNSNxvVTjOqU+Cmq7KERYbDUms/gs2PtkzjSy0G0ygnxmrOB6Ygy0+GX2b
EWtxyOrcJ63EfIJUTPb7CIxwsv4d/BMNZLcbbUr6RSTk1XFyy5Vf1L+wkG2KBK6p
P0AWF08m0yUtK5K1jMoG2j4L8cVuB04lIzILVNMq8EPNY2tYYOsmR6dT2gjSAk2C
yuoll+0KstigG1MVL+tbrgpN3bbGxmV9DUWWdFwGXB79mv18HZSKdG5cloxd47ZX
AaPzmylRfR8s760U2V7fqtwIxZfJgVdpWaZdBDoEg3M5RRlsraSkCWPK0WPbmWeQ
xyY=
=wf2i
-----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] "5D72313BFCD83F17268143C3756C53BB8F92FDDD"

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