I found PHP’s documentation on the GnuPG functions to be pretty sparse, so thought I would share some specific steps that I went though in order to get everything working.
Prerequisites
First off, you have to install the GnuPG PHP libraries through pecl. It requires the GnuPG Made Easy (gpgme) packages to get working. The following shell commands will install the OS packages, install the GnuPG PHP libraries, then enable the PHP extension and restart Apache:
# apt-get install gnupg gpgme gpgme-devel # pecl install gnupg # echo extension=gnupg.so > /etc/php.d/gnupg.ini # apachectl restart
Creating GnuPG Keys
Next, you need to create a set of keys to encrypt and decrypt your data. You’ll need to put the keys somewhere where the webserver can read and write to a directory. I’ll use /var/www/.gnupg since that is the default home directory for many Apache installations. After running the gpg command, answer the questions as prompted. User input is red in the output shown below.
# mkdir -p /var/www/.gnupg # gpg --homedir /var/www/.gnupg --gen-keygpg
WARNING: unsafe permissions on homedir `/tmp/keys' gpg (GnuPG) 1.4.5; 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. gpg: keyring `/tmp/keys/secring.gpg' created gpg: keyring `/tmp/keys/pubring.gpg' created 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. ELG-E keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 2048 Requested keysize is 2048 bits 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) 10y Key expires at Fri Feb 23 16:35:14 2018 PST Is this correct? (y/N) y 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: Some User Email address: some@user.com Comment: This is a key for Some User You selected this USER-ID: "Some User (This is a key for Some User) <some@user.com>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o You need a Passphrase to protect your secret key. Enter your passphrase here 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. gpg: /tmp/keys/trustdb.gpg: trustdb created gpg: key 21CCC3D6 marked as ultimately trusted public and secret key created and signed. .... a bunch of random characters here.... 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 gpg: next trustdb check due at 2018-02-24 pub 1024D/21CCC3D6 2008-02-27 [expires: 2018-02-24] Key fingerprint = FA45 1EE9 8772 70EF 1CFA 99CE 048A 6139 21CC C3D6 uid Some User (This is a key for Some User) <some@user.com> sub 2048g/A83E754B 2008-02-27 [expires: 2018-02-24]
#chown -R apache:apache /var/www/.gnupg
Make note of the key fingerprint in the 4th from the bottom line. You’ll need this in your PHP code when referencing the key. Also, make sure that you write down your pass phrase somewhere. Your encrypted data will be useless if you don’t have the pass phrase.
Your Application
Now you can write your PHP code that will do the encryption. Here is a sample that encrypts, then decrypts something:
<?php $CONFIG['gnupg_home'] = '/var/www/.gnupg'; $CONFIG['gnupg_fingerprint'] = 'FA451EE9877270EF1CFA99CE048A613921CCC3D6'; $data = 'this is some confidential information'; $gpg = new gnupg(); putenv("GNUPGHOME={$CONFIG['gnupg_home']}"); $gpg->seterrormode(GNUPG_ERROR_SILENT); $gpg->addencryptkey($CONFIG['gnupg_fingerprint']); $encrypted = $this->gpg->encrypt($data); echo "Encrypted text: \n<pre>$encrypted</pre>\n"; // Now you can store $encrypted somewhere.. perhaps in a MySQL text or blob field. // Then use something like this to decrypt the data. $passphrase = 'Your_secret_passphrase'; $gpg->adddecryptkey($CONFIG['gnugp_fingerprint'], $passphrase); $decrypted = $gpg->decrypt($encrypted); echo "Decrypted text: $decrypted"; ?>
It would be best to store $passphrase somewhere completely separate from your application configuration. Perhaps an admin user would be required to enter the passphrase when looking up this information. That way your passphrase is not stored in your config file or anywhere that an attacker could potentially gain access to it.
Troubleshooting
Make sure that the web server can write to the GnuPG Home directory. This obviously is not ideal, but seems to be required in the testing that I have done. I’ve been able to set ‘secring.gpg’ to be owned by root, but that does little good since the directory it is in has to be writable.
You can raise the error mode to GNUPG_ERROR_WARNING to generate PHP warnings on GnuPG errors. That might help to track down where errors are occurring
Brandon, thanks for writing a great tutorial! Unfortunately for me I came across it after setting up gnu/php on my server… This would have saved me countless hours of guess-work.
Great help instructions but left out critical piece of info.
It didn’t work until I discovered the /home/user/.gnupg directory needs to be world writable.
I have a step in there that chown’s the directory to be owned by Apache (assuming your web server runs as the user ‘apache’).
#chown -R apache:apache /var/www/.gnupg
It shouldn’t have to be world writable, just writable by the user that your web server is running as.
Those who can encrypt and decrypt via commandline but can’t decrypt via php called from a webbrowser want to try out the –no-tty parameter for gpg:
php-script to decrypt encrypted.gpg to decrypted.txt
I’m having a problem getting keys as well:
[root@mirage enrollment]# php encrypt.php
PHP Warning: gnupg_addencryptkey(): get_key failed in /var/www/vhosts/madeupdomain.com/httpdocs/enrollment/encrypt.php on line 10
PHP Warning: gnupg_encrypt(): no key for encryption set in /var/www/vhosts/madeupdomain.com/httpdocs/enrollment/encrypt.php on line 13
GNUPGHOME set to: /var/www/.gnupg
[root@mirage enrollment]# gpg –homedir /var/www/.gnupg –fingerprint
gpg: WARNING: unsafe ownership on homedir `/var/www/.gnupg’
/var/www/.gnupg/pubring.gpg
—————————
pub 1024D/24F0C25E 2009-10-29
Key fingerprint = 61EC B598 C01B 3DD4 2543 FB1A 1A16 8972 24F0 C25E
uid dummy
sub 2048g/3F9A5055 2009-10-29
[root@mirage enrollment]# cat encrypt.php
seterrormode(gnupg::ERROR_EXCEPTION); // throw an exception in case of an error
// set the environment so gnupg can find the keyring
putenv(“GNUPGHOME=/var/www/.gnupg”);
$res = gnupg_init();
gnupg_seterrormode($res,GNUPG_ERROR_WARNING); // raise a PHP-Warning in case of an error
gnupg_addencryptkey($res,”61ECB598C01B3DD42543FB1A1A16897224F0C25E”);
#gnupg_addencryptkey($res,”61EC B598 C01B 3DD4 2543 FB1A 1A16 8972 24F0 C25E”);
#gnupg_addencryptkey($res,”5C798B98314176C041DD66324A83C80EF1817BFB”);
$enc = gnupg_encrypt($res, “just a test”);
echo $enc;
echo getenv(“GNUPGHOME”). “\n”;
?>
[root@mirage enrollment]# ls -al /var/www/.gnupg
total 32
drwxrwxrwx 2 apache apache 4096 Oct 29 11:39 .
drwxr-xr-x 11 root root 4096 Oct 28 20:42 ..
-rwxrwxrwx 1 apache apache 1155 Oct 28 20:47 pubring.gpg
-rwxrwxrwx 1 apache apache 1155 Oct 28 20:47 pubring.gpg~
-rwxrwxrwx 1 apache apache 600 Oct 28 20:47 random_seed
-rwxrwxrwx 1 apache apache 1304 Oct 28 20:47 secring.gpg
-rwxrwxrwx 1 apache apache 1280 Oct 28 20:47 trustdb.gpg
[root@mirage enrollment]# php -m | grep gnugp
[root@mirage enrollment]# php -m
[root@mirage enrollment]# php -m | grep gnupg
gnupg
The GnuPG php module is loaded, permissions are wide open, and I’ve triple checked the key fingerprints… Any idea what I’m doing wrong??? Many thanks your help.
@Troy,
Permissions may be too open. GPG will complain if your secret key is publicly readable. Try setting everything to be owned by apache, with 600 permissions.
chmod -R 600 /var/www/.gnupg
[root@mirage enrollment]# php encrypt.php
PHP Warning: gnupg_addencryptkey(): get_key failed in /var/www/vhosts/personalizedprevention.com/httpdocs/enrollment/encrypt.php on line 10
PHP Warning: gnupg_encrypt(): no key for encryption set in /var/www/vhosts/personalizedprevention.com/httpdocs/enrollment/encrypt.php on line 13
/var/www/.gnupg
[root@mirage enrollment]# php test.php
PHP Warning: require_once(Crypt/GPG/VerifyStatusHandler.php): failed to open stream: No such file or directory in /usr/share/pear/Crypt/GPG.php on line 61
PHP Fatal error: require_once(): Failed opening required ‘Crypt/GPG/VerifyStatusHandler.php’ (include_path=’.:’) in /usr/share/pear/Crypt/GPG.php on line 61
Thanks Brandon, same result though.
Hi Brandon,
thx for this nice post. It was very useful for me.
I found 2 errors in Your Application code:
on Line 9
dont use $encrypted = $this->gpg->encrypt($data);
use $encrypted = $gpg->encrypt($data);
on Line 21 change gnugp_fingerprint to gnupg_fingerprint
Greetings from Germany, Sven
Hello,
I have problems with decrypt in php. With command line i haven’t any problems.
I don’t know what to do. I always got decrypt failed error.
@Bartek, if it works from the command line, but not when accessed through the web server, my guess would be some permissions problem. Try watching the web server’s error log for indications of the failure. Make sure that the user which the web server runs under has permission to read the private key file and write to the temporary directory.
works really well.
appreciate it.
–gen–keygpg
has now been changed to –gen–key
tks,
d.
This helped us a lot. Thank you for all the great work and also some of your inputs in the posts too.
Thank you,
Atul
Thank you for this post! Had to apply fixes mentioned in the comments, but due to some permissions issues I’ve been trying to gnupg-php work for hours and this post solved it for me.
Thanks for the tutorial, Brandon!
I can’t seem to figure out why, but the decrypted text is showing up blank for me. The encrypted text shows up just fine – but there is nothing after “Decrypted Text”. There aren’t any errors either. Any ideas about what could be happening or tips on how to troubleshoot some more? (Been looking high and low for any possible reason for the issue I’m facing.
Thanks!
I changed the way errors are handled and now I am showing a “get_key failed” error. So this would be a permission issue correct? I’m not TOO savvy working in shell, so how would I go about checking permissions (and on what folders/files) and then change them as I need to? Any guidance is appreciated!
Ubuntu packages are now libgpgme11 and libgpgme11-dev
Hi All ,
I got error while page running from web browser. Please check below error :
Crypt_GPG_FileException: Error reading GnuPG data file ‘/var/www/.gnupg/trustdb.gpg’. Check to make sure it is readable by the current user. in /var/www/testCrypt/GPG.php on line 454.
We have configuration Server Ubuntu : 12.04 ,PHP 5.3. also set the permission to /var/www/.gnupg check below :
dwr——- 2 root root .gnupg
I have checked the web user is like www-data.
Could somebody help me on this ?
Thanks,
Swapnil