Keeping Secrets Safe with GnuPG

GnuPG stands for GNU Privacy Guard, a form of public key cryptography based on the OpenPGP standard. This article will show you how to perform a few essential tasks with GnuPG using PHP and the GnuPG PECL extension.

For our purposes, as someone who would like to transmit and receive encrypted messages, you need three keys: your own public key, your own private key, and the public key of the person that you would like to communicate with.

Private keys should never be shared, but your own is required to generate an encrypted message. Public keys can and must be distributed. Your public key is required by anyone who wishes to encrypt a message intended for your eyes only or to decrypt a message that you have sent them. The public key cannot be used to forge a signature or decrypt messages meant only for you.

GnuPG also serves as a means to verify the authenticity of non-encrypted data via encrypted signatures, but that topic is not covered here.

The GnuPG extension discussed here depends on the GnuPG installation on whatever machine you are working with along with the configuration of the user account under which the code will run. The examples provided here are intended to be run from the command line and will interact with the GnuPG environment of the user running them. It should be trivial to extend the concepts here to work from a web script.

Generating Keys

Before you can do anything with GnuPG, you must have a private/public key pair. Though the PECL extension does not let you create these keys right from PHP, you can easily do this from the command line with the gpg command, or a friendly GUI frontend for whatever OS you choose. In Linux, all you need is:

gpg --gen-key

After answering a few easy questions about your identity, selecting a passphrase for use of your private key, and accepting the defaults for everything else, your GnuPG keyring will be generated. The keyring stores your keys and the keys of anyone that you plan to interact with.

Once generated, your keyring is automatically populated with your first private/public keys. You should export a copy of both your public and private keys and keep them in a safe place so that you can reuse the same keys in all future interactions with GnuPG. You may also want to consider publishing your public key to a key server to make it available in a safe way to anyone who needs it. More information about GnuPG tools and key servers can be found on the gnupg website.

Exporting Keys

One way to export your GnuPG public key is with PHP. Before you can perform the export however, you must find the key “fingerprint” of your new public key. Every key in your keyring will have a fingerprint that is used as a unique identifier. To list them all, loop through a gnupg_keylistiterator() object:

<?php
$iter = new gnupg_keylistiterator();
foreach ($iter as $fingerprint => $user) {
    print "$fingerprint: $usern";
}

For the keyring of a user named “testuser” on my workstation, the above code results in the following output:

6436010991C352981F95F9A5D5AF916244CD449B: Test User (This is a test account) <test@example.com>

The key itself is binary data, so to actually encode and print an ASCII version of the public key we need some new code:

<?php
$gpg = new gnupg();
$export = $gpg->export("6436010991C352981F95F9A5D5AF916244CD449B");
print $export;

This creates a new gnupg object, and uses the export() method to export the public key corresponding to the provided fingerprint. In my case, the test user’s key is:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.2 (GNU/Linux)

mQGiBFDP4+cRBACupVdbSBVa5wLjKFoELiS9IunzQA6hj2P1jCYmXtwSPwvvrXRX
LqhpkmXmWrHvo+4WW8aDjI2csuh8z4gNB+YGkaB3iKZxyhdazKco0R1BU7fjPrUn
4iqVqkpaixcdMVDPkjjBTJDEdjxWWieErSzbzkjy8wuTJR+lAVFWTcuTOwCg9WAg
bLwpPIlcULQ+D/l3sLt2AaED/jgdcTvMsVG96xZGWT9bewl/HVX+2ZpCWIkbE34E
AABS8c9hwNEz3QvkEqoxcf5sC8ujtvTf3Gky4E6h4KZrpCZuZ8kFRQiAhqP6XwNi
8jJM0BK8ooYPFP4UsfRQImc4XczL/njtwpwJ8wX2UOb7oglJB5acMulvzpD4cRvK
FkqIA/9NzGFExUeJeJ8b6GC17KDOIHfBwlrcGvc85c574fUVekV9jGGaAzl6i89O
OIWQCELH9s8quTA23tDeMcHRwUMHAYxh7lf2stoJmwz0IySpUCkmxcUhB5TIzBOB
jHifGAlJKby6gNJhlvghTxXlVGsQiM14JLJo2+hS2qMc17vp77QyVGVzdCBVc2Vy
IChUaGlzIGlzIGEgdGVzdCBhY2NvdW50KSA8dGVzdEB0ZXN0LmNvbT6IYAQTEQIA
IAUCUM/j5wIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJENWvkWJEzUSb6iIA
nRhCMJ3CN+1QXSD2zAtsoWgVebqZAKDLp1636WZ3XZAIVOlHBnpMTfcdA7kCDQRQ
z+PwEAgAkRnVYvmbpkGCCU8T56Wh2+jMA5GVPbPuyqmprfvgrt7yp9EEXfXRrGVA
KYMbFDU4+Vdc2U9Wm/T6P7McvI3dJARWOD6NeqOh+k6yTYxH+qznsU2d/7+Vugfx
zxVXtllhpnyWc5AlAr6yo98UdjO+q5RNM3fzSdjHth+MCJqEBfkyTui2VN9odnnS
IkDFHJyVcaUfQ580RpLWllwAC3+Urntc02S6xxEr03fKIe/wuhXC9gpG5lWu3hyj
k0PlsSh55w5gb4zF3Q2yDbToAuvSg1My0ytIPddmjjBx8gs4PCnB+48R52jsT/uI
Ju6j83Ddy1Ph1cnalChIpMit2Hm40wADBQf+J+NRhzLByj3YRsRK9ohZ0dm1+sMU
S86Xv676IQMgTzohFCAicnFZtTJx6N35pox/azOOGxAoof79TPv1r897bysAXjmu
qjW2mMpAfhnNY18Wf2SB+R7oYsID/z4fOQltyX54z8Fi40OO6UtI1X3Lh4KGzhUK
AnkgVeYGxupiK2HHtxeknEiKuAY+RXiQRYIC8YgAZboKqbWt8WVSMwctLbtRgXQC
VlkcZzwPk+mPocxYcnID+mZgIpwX0/+Zs4OOlyrf1k1oI6zQA7tJ+etGugtee78j
e8gub9CYvyCJMFM3pI5OEKC9BRSjPjgSw/6kQEc4bHP1Bn4WqPgh2d61Q4hJBBgR
AgAJBQJQz+PwAhsMAAoJENWvkWJEzUSbAhYAoKnL/nGFmJejtoPdws+QRW95VECn
AJ4yFFV1axpTDbIClQQTerJS7xpxjg==
=g/FY
-----END PGP PUBLIC KEY BLOCK-----

The extension only permits us to export the public key, so if you would like to export your private key in a similar fashion for backup purposes then you’ll need to use the GnuPG tool itself.

Importing Keys

We now have three keys as required for a private exchange. We’ve generated our own public/private keys, and you have the public key for testuser above that we’ll use as a third party public key.

To communicate with testuser, we’ll need to get her public key into our keyring. Importing a public key can be done by passing the public key to the import() method:

<?php
$keydata = "FULL PUBLIC KEY FROM ABOVE";
$gpg = new gnupg();
$info = $gpg->import($keydata);

You can verify that this key has been added to your keyring on the command line with gpg --list-keys and use the gnupg_keylistiterator() example from earlier to get the fingerprint.

Encrypting Messages

With all of our keys in our keyring, we can encrypt an important message for testuser. The message contains the answer to the question:

Q: How many programmers does it take to change a lightbulb?

We’ll use testuser’s public key to ensure that she will be the only person able to decrypt the answer. The code for encrypting the answer is:

<?php
$gpg = new gnupg();
$gpg->addencryptkey("6436010991C352981F95F9A5D5AF916244CD449B");
$enc = $gpg->encrypt("A: None, that is a hardware problem!");
print $enc;

We use addencryptkey() to specify which public key to encrypt this message with, which should be the recipient’s key, not the sender’s. In this case it is the public key fingerprint of testuser.

Once encrypted, nobody will be able to decrypt the message unless they have testuser’s private key in their keyring… which should be no one other than testuser herself.

Decrypting Messages

From the other side, once testuser receives the message, she can use the code below to decrypt the secret answer:

<?php
$encrypted = "THE UNREADABLE ENCRYPTED MESSAGE SHOULD BE PASTED HERE";
$gpg = new gnupg();
$gpg->adddecryptkey("6436010991C352981F95F9A5D5AF916244CD449B","test1234");
$plain = $gpg->decrypt($encrypted);
print $plain;

The encrypted message is stored in $encrypted. We create a new gnupg() object, specify which of testuser’s keys should be used for decrypting the message by fingerprint and our private key password, and then print the decrypted message. It is important to mention that decrypting a message will always require the recipient to enter the password that they associated with the private key in addencryptkey(), except in gnupg v2 where you cannot enter a plain text password in the code – you will be prompted for a password while running this code in the CLI. If you plan to use this function in a web based tool with gnupg v2, you might want to consider excluding the passphrase on the web user’s private key.

In Closing

Now that we know how to encrypt and decrypt with GnuPG, why would you want to in PHP? With SSL you can secure data transmitted between a web server and client, and SSH can be used to secure shell sessions, so why something else?

Sometimes you need information to be secure, not only while it is in transit, but also where it is stored and all layers in between. Maybe you need to transmit financial information to a remote location; can you trust system administrators or those who have physical access at the location? Or perhaps a project requires you to transfer data using an insecure protocol, such as FTP.

While our simple examples above are useful for transferring encrypted messages, GPG and the gnupg extension can also be used to validate the authenticity of unencrypted messages by the use of signatures. Email is also a notoriously insecure method of communication and messages are easily forged, and though not described in detail here, using encryption and encrypted signatures ensures security and authenticity.

I encourage you to further explore the GnuPG tool options and concepts if you’re writing an application that calls for serious data security.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Matt

    very helpful. do a part 2 please — if I want to store info securely in my database using gpg, how do I secure my keys? anyone with access to the server could decrypt everything?