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.
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.
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.
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.
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 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) 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_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)
Use gpg_keygen()
to generate a new public private keypair:
(mykey <- gpg_keygen(name = "Jerry", email = "jerry@gmail.com"))
[1] "CAD9E36C91F2A408"
gpg_list_keys()[c("id", "name", "email")]
id name email
1 CAD9E36C91F2A408 Jerry jerry@gmail.com
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")
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")
Searching: https://pgp.mit.edu
considered imported unchanged
1 1 0
keyring <- gpg_list_keys()
keyring[c("id", "name", "email")]
id name email
1 CAD9E36C91F2A408 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 CAD9E36C91F2A408 Jerry jerry@gmail.com
To export our newly created public key:
str <- gpg_export(id = mykey)
cat(str)
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFgKVBoBCADwN2fxLVqxWV+1AxEpOkF8TAkZaqcDXUY6CClmkn/C6ltIEtx7
y9MU+AzT1awf7h5xbIopEhezxRcF/bfWrbI++r1SbBMgK33/CE4m3JkTUBd4EKPo
/a4OS9NNWAHQh0juehAz5ylUI73fzcovbYpFEvwx9JGzPmQqnXvR9vHNCDlHo71o
yW5Bogq45ksLcAOz7Akgw2K1/gXaXWw/CZGqJ9NWM2xOi3gzCA7+2n+zmECSj617
SjggwU2sI+tVh6Z5P+PUBr6cRSBGjuRB6YdGAuJsFXkNsui+k1Jcdy/7RHc8CvJR
v44a4U+6kTZY04UxtpC3sCYWKJ4CMr2Y5V4LABEBAAG0F0plcnJ5IDxqZXJyeUBn
bWFpbC5jb20+iQE4BBMBAgAiBQJYClQaAhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIe
AQIXgAAKCRDK2eNskfKkCNZgCACVS/sgf9WZ+8PmsVDeHffLs10FYbfq/MLlhUaT
OAO/OiJl2lMVeC2Qsk9zkM+UmsKLJ9J3kSrHtlSpdxPn0uCW8vLCx7Di5fdn+KDy
ASmB5GjHtqe/E1WBo19NmiSptDYT+HIfZSAfpvsPGUlSGS4VaDx1XuGYuR72AFYT
8WBHJTEFjvOds1zvDruG2vd6M/GBKVYRm+gCHbZ/zpyx5ZtmIzKF+arUKFF8zweY
aE9OC8EkQB7Pv/9e//bf41KZAJ4db0c4aBHy5BotXYIzqCniGHEX1RxW/qhp8OpU
mo6RxGe7bIIXr8Yw99qUsEj3QXqLQkXFqtulqs9h0NQLx6SC
=xqSm
-----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-----
lQOYBFgKVBoBCADwN2fxLVqxWV+1AxEpOkF8TAkZaqcDXUY6CClmkn/C6ltIEtx7
y9MU+AzT1awf7h5xbIopEhezxRcF/bfWrbI++r1SbBMgK33/CE4m3JkTUBd4EKPo
/a4OS9NNWAHQh0juehAz5ylUI73fzcovbYpFEvwx9JGzPmQqnXvR9vHNCDlHo71o
yW5Bogq45ksLcAOz7Akgw2K1/gXaXWw/CZGqJ9NWM2xOi3gzCA7+2n+zmECSj617
SjggwU2sI+tVh6Z5P+PUBr6cRSBGjuRB6YdGAuJsFXkNsui+k1Jcdy/7RHc8CvJR
v44a4U+6kTZY04UxtpC3sCYWKJ4CMr2Y5V4LABEBAAEAB/9k112TKW8BNHuxCXCS
1hnXjDPlr5gdGXL5uaNno+hucuOPBhv4/D/Idbks1G5FmTi+dMorI6U/mmVn5nLe
dFx3g9smVqhQwPFLtrjYJ5M7QEwZ4yJsFNKUvdAej+tzFtfPBP74SvpWkAqNgV53
OozpImcssXbYwEBcg3APXJN1dkYMQzaph/nzOlPcn8pD0IT8VtvAVddwB+2XR8Gy
AuYtKRawF3AxEH4pNPgHSO6/uhfeESYWfeyr7GaSQHWlbULKgriRHI1YSPvled+L
il0Ld1Vb3MDafrN9FeKAKR+rk5j+jWQ2m4U9ZGBB28bSk2vpSoNUgU23uNsMIi8F
HI6BBADxM+U42/LlNnaDRjFo+4gDNihSOOfzDhwUn/SMw5PFRo8iv0lWfFUVl/l6
IEaPTK0igV+R355Xvkj2B5Wh/VV3SXONfkIDZZr1gTrrJofE3auCHZnsBvsynHOx
GuSvHmxuUcr7aVRBjqXudKOnzvf4hUclMLuCbcnnU4L7JLkumQQA/vQFW5BZcX2e
jDjVIEK5GNzJ93PwWyFM71/gtnMBclDYJsolj3OqvhOyCnII+jjVAzFWLr4Zkm2z
WapL9hDH4bJQqdJCZpq6WyvaXY0IzED/AgkVrDlERf21oX9l/VuCwho0U3luY8O/
1L8UQmbGfzsAwkJaYOAQhowsC6KXDEMD/0ldD6zHQk1h5W2BxyrI42kzRTMt4eQB
NcDV2OMSa1JFqqba+h4N876lEKKubKdkpzLXtHzXZOtM2lOSQxxHhE9nlJViE5yM
S2AKRWKUoDknjWlHRlb2oLnQUA8eYhmzKo2gzo2cs5CzWxOKmwegOTPc5nYaDsXs
ZmYzqQd8CQuvOfC0F0plcnJ5IDxqZXJyeUBnbWFpbC5jb20+iQE4BBMBAgAiBQJY
ClQaAhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDK2eNskfKkCNZgCACV
S/sgf9WZ+8PmsVDeHffLs10FYbfq/MLlhUaTOAO/OiJl2lMVeC2Qsk9zkM+UmsKL
J9J3kSrHtlSpdxPn0uCW8vLCx7Di5fdn+KDyASmB5GjHtqe/E1WBo19NmiSptDYT
+HIfZSAfpvsPGUlSGS4VaDx1XuGYuR72AFYT8WBHJTEFjvOds1zvDruG2vd6M/GB
KVYRm+gCHbZ/zpyx5ZtmIzKF+arUKFF8zweYaE9OC8EkQB7Pv/9e//bf41KZAJ4d
b0c4aBHy5BotXYIzqCniGHEX1RxW/qhp8OpUmo6RxGe7bIIXr8Yw99qUsEj3QXqL
QkXFqtulqs9h0NQLx6SC
=EKSe
-----END PGP PRIVATE KEY BLOCK-----
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 CAD9E36C91F2A408 Jerry jerry@gmail.com
2 51716619E084DAB9 Michael Rutter marutter@gmail.com
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.
Let’s use the private key we generated earlier to sign a file:
myfile <- tempfile()
writeLines("This is a signed message", con = myfile)
sig <- gpg_sign(myfile)
writeLines(sig, "sig.gpg")
cat(sig)
-----BEGIN PGP SIGNATURE-----
iQEcBAABAgAGBQJYClQcAAoJEMrZ42yR8qQIrbMH/RI2ELfrhkVlxeJot3m7sjwl
dCE0FLKZhG7VA2O0tjX22HjH9orrKZq5HO4BPF8iWe1F5B89pbFTLcnyKFPOIoII
eqrLAg6QEfpDVXdkBVdG5dUZmw1Ux57P9x23EgICX7ITuNuEToDV4gs6bthXj5wM
qqlKmGUSdCTER2UyAUvHwEz3Z8Ki+waBX59agxt2PS0b6j5AAnprliGhrAj/2Ue7
g3L3/LiNEcP3Kgcwi4KCTqpWC5QpA1ktJnTdvZoa91KP2gysrQiKadf80CgwhNdm
OIOE4xN2VnqHn9lQ9e6lmgYm5yLts+BRuucQf+0bL22iXkeT4Q8dtMYeMXc42GI=
=68qC
-----END PGP SIGNATURE-----
You can also create a signed message which includes the data itself by setting mode
to normal
or clear
, which is useful for email:
clearsig <- gpg_sign(myfile, mode = "clear")
writeLines(clearsig, "clearsig.gpg")
cat(clearsig)
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
This is a signed message
-----BEGIN PGP SIGNATURE-----
iQEcBAEBAgAGBQJYClQcAAoJEMrZ42yR8qQIiQEIAOCpELtnzwVIxzG1c51m+JSA
sO+uQTAXIiSoj9bQ3Aa6vIUoEbgpzUHdT7Ol+SeCOWfC+6HpQSQKYDbBdkI39FaA
PC9oIr18belQ4EgrsNbBYBOpP5SYMsB9iBkxG6c9WhCHs+ob0Gm6samEbr4A+FTZ
3xd9ddCb1u//A/rA58nDdVqDo75/dwcMBdNTV3ExOUa0bEXyzyNxoZ2ar27URJSQ
4W5OshW9irK7qGo//da5FtXidjGkKNvm/YB9FqO2xDPzJ3GqOxyeRssreSwUaa2V
kKtO46EcYyAqFQigXaF7tdMRRdPRzYTzzvSlRvqKUsA9QNmx2jxT28adllBpqSk=
=3yDr
-----END PGP SIGNATURE-----
The gpg_verify
function will see if a signature is valid for any of the keys in the keyring:
gpg_verify("sig.gpg", data = myfile)
fingerprint timestamp hash pubkey success
1 D60D771FB0FB5AABA8FA6A28CAD9E36C91F2A408 2016-10-21 19:45:00 SHA1 RSA TRUE
If the signature is in clear
or normal
mode, the signature file contains both the message and signature:
gpg_verify("clearsig.gpg")
fingerprint timestamp hash pubkey success
1 D60D771FB0FB5AABA8FA6A28CAD9E36C91F2A408 2016-10-21 19:45:00 SHA1 RSA TRUE
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 <- "6212B7B7931C4BB16280BA1306F90DE5381BA480"
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.gpg', 'Release')
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.
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.
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-----
hQIMAzCzOshC83uFAQ//fYQIF1NbrEdTUSVThr4bHu04QU8xknUZmalcyPePn4Ey
kWWk/ErMWeuJwAPZeqf8s4y61jq6ySWOXxC1sRMQCanFfqZIuu++QrGP4eL2uOqj
bSEfPZxYbpec2blvonMe97tb+GysTfqe0gQZhXiZKAB5RrXvFvVHLVVvVO8ypwk4
u2L3ptPPgnFNtr5rYzx3YI2aDNuV1jwl9B+l+acnXqiVH1ILmLa/lVq1L5h/yUWt
hpTduvaJfCpZO6xeP1Cly36YQOtJSo7kkV7Pt30lI4nDiETVUrIKiwb7kLe5fuxW
87ru6Ojm4pD3lmIHRqTiohQhBhUpcBbUwTnK5C7kpWocTrRiQ/kRIrjb8cj7fIyE
sPT+m/wtYx6mwMTkCzUT67ZLeXs3U7wKNVlxqUv8mOGaockBZAYLyFfnFz9njDSQ
+iX23IFDCpn+350/YYvAj00cPLEeKznFW7FMoAhr8+CEGfxVc6pxNKYz0TTqDvHC
peIpKBXLob2bH8eStis2E1s50jsEEMqaR1lOqJOcLg+sj/5p6Xw0RszDnAl2TAnM
VaKqPx6ILSaXlrEBM7qbdkJLXzViY/Rnnsi2h9W2AQTH7oZ0rLTUWeIFzCbEtzkT
723liCdoV3Sx30UocuO2jUHg77RyWQkMyU+csjcGJ90HyNda++8TF/gvwK7FzPvS
UwH3N1eEq7VEhnhB1bUO4mpx1btPmWZjgztz7sLJtfMh/d+Opxjwr/Pd2yUVA4UN
sNIHKICdJUroZ0G1qHRGRgJhxWl7lgNJg40DnjUdDtPGhOID
=AGUg
-----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.
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: GPGME 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-----
hQEMA8rZ42yR8qQIAQf/VTJ9lI2OK3s6AMinn9bojLjEnYjaATxXV9S90zbjBFrx
byU7pTzmPuN3JpYbL5991ppIMTtjPaKAEOGTpG0dnqSlkK2w2goJ/eD1s4+wb1Vd
VlvtUnAW5Wmn1XBXUWhEEIw6u0fZ9GuHtTYPvWv9XJtl0PHaDFaQyMYXLCVAe7tE
HtKr8qH5jaOFQloYOMHqCGmY8Q1l+qcDkLf/3a18Y2+RwekOj38mjzwxQ3+9l5Dj
Mwr1ApW0vspgtxMJzzBTlnskjusPLQem+zNDWjn7hDCRJG2K1rDJKXdqZ1nv6rnu
TcYVyVxuNE0vKhdn04HDN/1HAS/ZXoJWz/aNKSgFcNJNAb4vx+FC8pvrT7AR46OH
5Gh7YnK5TLZR4JrGjWiJmtQR9oXz3ztRau9vIxs9aq6DApIz7IqphtuLD3Hl6nJr
Mm/oYfFdvO9JXVjMDmA=
=rs4D
-----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"
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?
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-----
hQIMAzCzOshC83uFAQ//RgEhwyPJVGlCcJO3QWC5QqH653Aa3Igz4TFuMQ9EP/N5
kkuE1HuJuiylsf9aynFTRQUTzDoHnmJGAZKWaE1xRjuov0WuTrM0zAt+lf1oxxlz
V9qQ3B+pUlJXDCHwfydOwh5hnrVAFpXkxwRdaKwMARWsxbuqnZOiCnzdL/xSXCwL
RcUi5vQUgqe6ShNesGgTn374ATHW9XYrNwCh3nANMExDO5QqoS289gGuTnLu2gdL
gml9wKzWjPAJh+puLUVg4onVne2iMws+0iwvX3Lt4FhPpy47OZrLQeQ8EuWknTYQ
2b85yUaUhWiAY3GR3T5kmK7ypyGjiFF2NYbyHzCjO2isF+783wnWrMgVA8baOyOy
Z8fhwakoOiE7Ntbnq203N6KwCC9g7SGcKaxSD6cPZkIW8G7UU4Oay3RGFarnRAm+
+2xUGq/qoGbUz24VBlp4MsMkuEbAA/j4GdRTysrEENitPmE6ERmJfhJiAJ4q/OV7
RDLF/ivJv+msCwgVy/DY3B/tIRbGnev4zb83qQdLEMfWX274tmGfdy8P4xYjDOgb
okW7W59/JKXoBv4kYtNDeWT3HRlLsOn8INruywKFcFSqAD5Uk0FlTGrIh9aG26ur
Jm2NglxOLlNNhCzi342RO3uy+2UUh7u4bTXPZ73PTEd8QOsu9CZ1dfPR3mVa+TrS
wMABtsudqzrhTNJFERfxKuZnwgZ0FvZixp+697SrgtgJrHnw75dBPwWQhsKoL5Eb
EFntUrQ9bO4t85zVr7tsnuRVS1C0AbsWyk5ZKGE1YFvtvFwozPE55HGlkn0gZ6rp
tt27N0HgZz5MDLBnnrU+YyidNEbBSQfGScZuTn8MRirGEZnRkNOcXTcg6h+Pug93
Ej/WlwQk9al0/+C660ahtBszN5jk7EmUvSJkGNLT/i2fO7fDUFN7muAJGap9M8DK
edNvMZvd0nNMo0rl6lAVOH/v4UaxBKw37FWwb2KEtvLYKKA4YUnXXPfx9vJcs7Kc
OVOoE8uq/HYcQ2MAWyTJLwiG7gtgDPvV+33i0NQuvCam9bqIrrAgOC6gJS+s0yLU
2oeALM13LRrKUvumtmacZLJE7n/1UJqVTbICHUioVNp2zfn0lhE+QqvO/klRmWXJ
llhQNduv+3ADyJUTdiZWFcrcxDGgmKXbDjWmszZ0c0NBQ3cYV3Y8aYxmel8UG1H9
XaE=
=d9ZM
-----END PGP MESSAGE-----
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] "D60D771FB0FB5AABA8FA6A28CAD9E36C91F2A408"
The signer fingerprint (if any) will be added as an attribute to the decrypted message.