PEAR is archived and read-only

This mirror preserves historical PEAR package releases and metadata so existing references remain available.

Home » Encryption » Crypt_GPG » Manual

Introduction and Overview

Introduction

Crypt_GPG is a PHP package to interact with the GNU Privacy Guard (GnuPG). GnuPG is a free and open-source implementation of the OpenPGP protocol, providing key management, data encryption and data signing. Crypt_GPG provides an object-oriented API for performing OpenPGP actions using GnuPG.

GnuPG is distributed as an executable program with a command-line argument syntax for performing actions. Crypt_GPG uses PHP's program execution functions to run GnuPG as a subprocess, performing the desired action. Crypt_GPG automatically handles process control, stream handling and error checking of the GnuPG subprocess. Crypt_GPG uses PHP streams internally for most actions, allowing (among other things) any streamable resource to be used with the Crypt_GPG file commands.

Though GnuPG can support symmetric-key cryptography, this package is intended only to facilitate public-key cryptography.

Overview

The basic internal overview of a GnuPG command executed in Crypt_GPG is as follows:

  1. build the required command line,
  2. open the GnuPG subprocess with the command line,
  3. stream data to and from the GnuPG subprocess on various IPC pipes until the command is finished,
  4. close the GnuPG subprocess and,
  5. check for errors.

Crypt_GPG handles all these details internally so GnuPG can be used with minimal effort from the developer.

Generating a GnuPG Key

Outlines how to generate a GnuPG key for use with Crypt_GPG.

Crypt_GPG does not yet support generating GnuPG keys. Generating a GnuPG key for use with Crypt_GPG is much the same as generating any other GnuPG key on a system.

Though Crypt_GPG supports specifying the keyring to use, Crypt_GPG, by default, uses the keyring of the current user. If using Crypt_GPG with a webserver such as Apache, the current user is the Apache user and the key will need to be generated as the Apache user. To do this, run the gen-key command as:

$ sudo -u apache gpg --gen-key
    

The following example walks through the process of generating a key that supports both encrypting and signing. First, run the command:

$ gpg --gen-key
  

This will display the following copyright information and a list of available key types:

gpg (GnuPG) 1.4.6; Copyright (C) 2006 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.
Please select what kind of key you want:
   (1) DSA and Elgamal (default)
   (2) DSA (sign only)
   (5) RSA (sign only)
Your selection? 1
DSA keypair will have 1024 bits.
  

Select (1) DSA and Elgamal (default) to allow the generated key to both encrypt and sign data. This will generate a public-private key pair in the GPG keyring and prompt for the size of the encryption key:

ELG-E keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
  

Select the default value of 2048. Enter greater or fewer bits depending on how secure the encryption must be. The default value is considered safe for most applications. GnuPG then prompts for the time period over which the generated key will be valid:

Please specify how long the key should be valid.
         0 = key does not expire
      <n> = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
  

Unless the key needs to expire after a certain time period (preventing subsequent decryption), a key that does not expire should be used. Next, enter the three parts of the key's user id. The first part of the user id is the real name of the person or organization that will use the key to sign or encrypt data. The second part is an email address and the third is a comment about the key. Both the email address and comment are optional:

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"
Real name: Test User
Email address: test@example.com
Comment: test key
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
  

After entering the primary user id of the new key, the passphrase must be selected. A secret passphrase is essential to securing encrypted data. Guessable passphrases will render encryption useless. For critical data such as credit card numbers, a non-dictionary word that is at least 8 characters long is recommended.

You need a Passphrase to protect your secret key.
Enter passphrase:
Repeat passphrase:
  

Following the passphrase, GnuPG will gather entropy for a period to ensure the generated key uses sutitably random numbers. When enough entropy is collected the key is generated and added to the keyring:

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

... snip ...

gpg: key DB15A2C9 marked as ultimately trusted
public and secret key created and signed.
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid:    1 signed:    0 trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   1024D/DB15A2C9 2008-08-05
      Key fingerprint = F94A F628 5725 7147 0569 F9FF E995 8292 DB15 A2C9
uid                  Test User (test key) <test@example.com>
sub   2048g/6AD96F48 2008-08-05
  

At this point the key is in the GnuPG keyring and ready to be used by Crypt_GPG.

Usage

Creating The Crypt_GPG Object

The Crypt_GPG class is the main entry point for using Crypt_GPG. To use GnuPG in your project, create an instance of Crypt_GPG and then call methods on the object to perform GPG actions.

The Crypt_GPG class supports several options, which may be specified in the constructor. Options may be used for the following:

Specifying Keyring Location

Sometimes, specifying the location of the keyring is required. One such case case is when using Crypt_GPG from the context of a Web page when the Web-server's user does not have a home directory, or does not have write access to its home directory. This is often the case on shared hosts. In this case, you should specify the GnuPG keyring location as an existing writeable directory. This is done using the homedir option. For example:

<?php

require_once 'Crypt/GPG.php';

// Specify homedir as an existing writeable directory if the web user
// does not have a home directory, or if the web user's home directory
// is not writeable.
$gpg = new Crypt_GPG(array('homedir' => '/my/writeable/directory'));

?>

Showing Debug Information

If, for some reason, Crypt_GPG does not seem to work correctly, detailed debugging information may be turned on. This is done using the debug option. When providing bug reports for the Crypt_GPG package, you may be asked to turn on deubg mode. Example:

<?php

require_once 'Crypt/GPG.php';

// Enable debug mode. This will dump a lot of output when Crypt_GPG
// actions are performed.
$gpg = new Crypt_GPG(array('debug' => true));

?>

Specifying the GnuPG Binary Location

Crypt_GPG works by talking to the GnuPG subprocess. As a result, it needs to know the location of the GnuPG binary to work properly. In most cases, Crypt_GPG will detect the location of the GnuPG binary automatically. If the location is detected incorrectly, or if the GnuPG binary is installed in a custom location, the binary option may be used. For example:

<?php

require_once 'Crypt/GPG.php';

// Specify custom location of GnuPG binary.
$gpg = new Crypt_GPG(array('binary' => '/home/joe/bin/gpg'));

?>

If an invalid binary location is specified, Crypt_GPG will throw an exception.

Referring to Keys

Outlines how to refer to GnuPG keys in code using Crypt_GPG.

Fingerprint

Crypt_GPG supports referring to a key in several ways. The most definitive way to refer to a specific key is to use the key's fingerprint. Key fingerprints are generated by performing a checksum on the actual content of a key. A fingerprint appears as a string of hexadecimal characters, sometimes separated by spaces or colons. For example: F94A F628 5725 7147 0569 F9FF E995 8292 DB15 A2C9. The fingerprint of a key can be retrieved using the Crypt_GPG::getKeys() and Crypt_GPG::getFingerprint() methods. Alternatively, the following command may be used to list keys on a console:

$ gpg --list-keys --with-fingerprint --with-fingerprint
    

--with-fingerprint is doubled intentionally.

Key ID

Keys may also be referenced by the key id. The key id is an eight-octal long hexadecimal number. The key id can be obtained using Crypt_GPG::getKeys(). Though rare, it is possible to have two keys with the same key id. The key id may also be obtained using the following command:

$ gpg --list-keys --with-colons
    

The key id is the fifth colon-separated field. A partial key id may also be used to reference a key. The partial key id is the lower four octals of a full key id and may be obtained using the following command:

$ gpg --list-keys
    

User ID

Lastly, keys may be referenced by all or part of the key's user id. For example, Test User (test key) <test@example.com>, Test User <test@example.com> and test@example.com may all be used to refer to the same key. When there is more than on key in the keyring with the same user id (or partial user id), the first key is used. In these cases, it is important to use a more specific identifier to ensure the correct key is used. In general, unless the keyring contains many keys, the less specific but more convenient form of test@example.com is fine to use.

Examples

The following examples assume you have created a key following the instructions for generating a key that can both encrypt and sign data. The example key has a user id of test@example.com and a passphrase of test. All signature data is fictitious, but is formatted like real signature data.

Signing a Package File

<?php
require_once 'Crypt/GPG.php';

$gpg = new Crypt_GPG();
$gpg->addSignKey('test@example.com', 'test');
$signature = $gpg->signFile($filename, Crypt_GPG::SIGN_MODE_DETACHED);

echo "Package signature is: ", $signature, "\n";
?>

Verifying a Signed File

<?php
require_once 'Crypt/GPG.php';

$signature = <<<DATA
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQBIl9Tf6ZWCktsVoskRAoAKAJ9VkbFDTSGY2ygaEGBcMOE8Or9puwCgppYm
0qq0bhtw5vsi0cJF5oC52RY=
=VfxI
-----END PGP SIGNATURE-----
DATA;

$gpg        = new Crypt_GPG();
$signatures = $gpg->verifyFile($filename, $signature);

if ($signatures[0]->isValid()) {
     echo "Package is valid.\n";
} else {
     echo "Package is invalid!\n";
}
?>

Encrypting a File From the Web

<?php

require_once 'Crypt/GPG.php';

$gpg = new Crypt_GPG();
$gpg->addEncryptKey('test@example.com');
// you can use any fopen-able stream
$gpg->encryptFile('http://example.com/file.html', '~/file.html.asc');
?>

Encrypting Credit Card Numbers

<?php
require_once 'Crypt/GPG.php';

// ... connect to database ...

$card_number = '411111111111';
$card_type   = 'visa';

$gpg = new Crypt_GPG();
$gpg->addEncryptKey('test@example.com');
$encrypted = $gpg->encrypt($card_number);

$sql = sprintf('insert into payments (card_type, card_number) ' .
               'values (%s, %s)',
               mysql_real_escape_string($card_type),
               mysql_real_escape_string($encrypted));

mysql_exec($sql);
?>

Decrypting Credit Card Numbers

<?php
require_once 'Crypt/GPG.php';

// ... connect to database ...

$gpg = new Crypt_GPG();
$gpg->addDecryptKey('test@example.com', 'test');

$sql = 'select card_type, card_number from payments';
$rs  = mysql_query($sql);
while ($row = mysql_fetch_object($rs)) {
    echo "Card type:   ", $row->card_type, "\n";
    echo "Card number: ", $gpg->decrypt($row->card_number), "\n";
}
?>

Publishing a Public Key

<?php
require_once 'Crypt/GPG.php';

$gpg = new Crypt_GPG();
echo "My public key is: ", $gpg->exportPublicKey('test@example.com'), "\n";
echo "My key fingerprint is: ",
     $gpg->getFingerprint('test@example.com', Crypt_GPG::FORMAT_CANONICAL), "\n";
?>

Clearsigning a Message

<?php
require_once 'Crypt/GPG.php';

$data = 'Hello, World!';

$gpg = new Crypt_GPG();
$gpg->addSignKey('test@example.com', 'test');
$signedData = $gpg->sign($data, Crypt_GPG::SIGN_MODE_CLEAR);

echo "Clearsigned message is: ", $signedData, "\n";
?>

Verifying a Clearsigned Message

<?php
require_once 'Crypt/GPG.php';

$signedData = <<<DATA
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hello, World!
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFIl9Sb6ZWCktsVoskRArWDAJ9D5mq6p+4JnBy11OaAhnIA+uRSSACgoM5T
WcUHQ9pKf9PvNUn1Izy6c9E=
=k8+7
-----END PGP SIGNATURE-----
DATA;

$gpg        = new Crypt_GPG();
$signatures = $gpg->verify($signedData);

if ($signatures[0]->isValid()) {
     echo "Message is valid.\n";
} else {
     echo "Message is invalid!\n";
}
?>