Encryption and Decryption using PHP and GnuPG

March 12, 2000

Uncategorized

Intended audience
Background
How to use GnuPG
Encrypting files from within PHP

Decrypting GnuPG encrypted files with PHP
Pitfalls
Alternatives
Conclusion
About the author



Intended Audience


The purpose of this article is to show you how to encrypt and decrypt information
with GnuPG using PHP. Most of the major Linux distributions will install GnuPG
upon request. Otherwise you can get it by going to http://www.gnupg.org.


Background


Since almost the beginning of time, it seems, man has had a
need to keep information private and, in many situations, needed to decipher
information previously made private by others. In our age of high technology
these needs have grown exponentially and become more complex.

In the past, the ability to encrypt information with relative
strength could be found only in the realms of governments. Thanks to a
gentleman named Phil Zimmerman, this ability has been brought to the masses. In
1991 Mr. Zimmerman invented Pretty Good Privacy or PGP for short. PGP was
designed to be high-grade encryption software available for free to anyone who
wished to use it.

One of the major aspects of PGP is that it utilizes
“Public-Key Encryption”. In a nutshell, that means that you actually have two
keys: a Private Key that only you should have access to and a Public Key that
you give away to anyone you want. When someone wants to send you an encrypted
file, they use your Public Key to encrypt the file. Having done that, the
encrypted file can then only be decrypted by you using your Private
Key.

One way to think of Public-Key Encryption is someone sending
you a snail mail letter in an envelope that only you can open. Anyone can see
the envelope but only you can read its contents.

Because of the US Government’s restrictions on exporting high-grade encryption
technology, giving PGP to people outside the US was illegal. Because of this a
team of programmers lead by Werner Koch in Germany took it upon themselves to
write an Open Source, RFC2440 (OpenPGP) alternative to PGP called GNU Privacy
Guard, or GnuPG. Because GnuPG was developed outside of the US the export restrictions
didn’t apply. It should be noted that PGP and GnuPG are virtually identical (with
a few exceptions) so, in the
interest of Open Source and keeping people out of prison, we will be using GnuPG
in this article.


How to use GnuPG


GnuPG is strictly a command-line utility. There are several
GUI wrappers to GPG but the binary is usually required. Since its core purpose
is to encrypt and decrypt information, we’re first going to have a look at doing
just that, using GPG.

As with most Unix-based command-line utilities you first call the gpg

command followed by switches that affect the output of the utility. For example,
to encrypt a file called “my_secret_data.txt” you would call GnuPG with the -e
command to encrypt followed by the -r NAME to tell GnuPG who
should be able to decrypt the file. “NAME” in this instance is the first name
or email address of the person who will be receiving the encrypted file. (Note
that the user indicated by NAME must be in your public key ring and can be obtained
by typing gpg –list-keys).

Here it is in action:


gpg --r john@doe.com my_secret_file.txt


Once this is done you will a file called
‘my_secret_file.txt.gpg’ in your current directory. Any attempts to view the
contents of this file will prove futile unless you encrypted it using your own
Public Key. Feeling like a secret agent yet?

Now suppose Mr. John Doe has encrypted a file and sent it to you. To decrypt
it you simply use the -d switch followed by the encrypted file.


gpg -d john_doe_secret_file.txt.gpg


Since you have your private key contained within your secret
key ring GnuPG can determine whom ‘john_doe_secret_file.txt.gpg’ was intended
for and will decrypt it after you provide your passphrase.

All in all, this is pretty simple stuff huh? Let’s take the next step of using
GnuPG within a PHP script.


Encrypting files from within PHP


Now things get a little tricky. Quite often your PHP scripts
are written to run automatically within the web server without any intervention
by you. What kind of life can you expect to lead if you have to enter your
GnuPG passphrase every time PHP tries to decrypt a file? But we’re getting a
little ahead of ourselves. Let’s first look at how we can encrypt a file with
GnuPG and PHP.

The following script does just that:

<?php

    $gpg 
'/usr/bin/gpg';

    
$recipient 'john@doe.com';

    $secret_file 'secret_file.txt';

    echo shell_exec("$gpg -e -r $recipient $secret_file");

?>

After running this script you will find ‘secret_file.txt.gpg’
in your directory (Again, make sure ‘john@doe.com’ is in your public key ring!).
This is assuming that GnuPG generated no errors. If it did then they will be
echoed to STDOUT.

From here there are several things you can do. For one, if there are any errors
you probably want to look for them within the script instead of just echoing
them for the entire world to see. You might also want to email the encrypted
file to Mr. Doe using PHP’s mail() command.

But what if you want to encrypt raw data not contained in a
file? This too is possible by piping the data directly to GnuPG:

<?php

    $gpg 
'/usr/bin/gpg';

    
$recipient 'john@doe.com';

    $encrypted_file 'foo.gpg';

    shell_exec("echo $argv[1] | $gpg -e -r $recipient -o $encrypted_file");

?>

This script takes the value of
$argv[1], the first argument after the
script name, and passes it to GnuPG for encrypting. GnuPG, using the
-oswitch, writes the encrypted data
out to $encrypted_file. Again, you
will probably want to check for and deal with any errors generated by
GnuPG.

Another option is to leave off the -o $encrypted_file part and
store the encrypted data inside a variable. That way you can use PHP to do with
the encrypted data as you please, saving valuable file I/O.

<?php

    $gpg 
'/usr/bin/gpg';

    
$recipient 'john@doe.com';

    $encrypted_message =  base64_encode(shell_exec("echo $argv[1] | $gpg -e -r $recipient"));

    mail('john@doe.net'

         
'Your
Encrypted Message'

         
$enrypted_message);

?>

If you do this is especially important that you Base-64 encode the data so
you can play nice with the email client receiving the encrypted message.


Decrypting GnuPG Encrypted Files with PHP


Decrypting an encrypted file with PHP and GnuPG can be a bit more complex than
encrypting, since you are required to provide a GnuPG passphrase. The solution
to having to type the passphrase every time the script is run lies in a handy
little gpg switch called --passphrase-fd. This switch tells GnuPG
to accept the passphrase from a file descriptor, which means that you can echo
the passphrase and pipe the output to gpg, as seen in the following example.

<?php

    $gpg '/usr/bin/gpg';

    
$passphrase 'My
secret pass phrase.'
;
    
$encrypted_file 'foo.gpg';

    $unencrypted_file 'foo.txt';

    echo shell_exec("echo $passphrase | $gpg --passphrase-fd 0 -o $unencrypted_file -d $encrypted_file");

?>

This script tells gpg to accept the passphrase from STDIN
(indicated by the 0 following the switch) and decrypt the information into a
file named “foo.txt”.

As with encrypting information, you can leave off the
-oswitch to gpg and let the decrypted
data be captured inside a variable.

It should be noted that the -o switch should always come before
the -d switch.


Pitfalls


As with anything encryption related, there are pitfalls to the
methods described in this article. The major pitfall is in storing your
passphrase in plaintext within the script. Should your script’s source ever
been seen without having first been processed your supposedly secret passphrase
will no longer be secret.

A second pitfall is in the use of PHP’s shell_exec() statement. Since
you are executing a shell command the passphrase is available for all to see due
to having to echo it. Don’t use this method unless you are sure that only trusted
users are on your server!

These two pitfalls are enough to suggest you probably won’t want to use these
methods in situations of national security, to say the least.


Alternatives


One of the best alternatives is to take advantage of a recent project within GnuPG
called GnuPG Made Easy (GPGME).
This is a project to develop a GnuPG programming library so that GnuPG can be
embedded within other applications and languages such as PHP. Having GnuPG would
be a great addition to PHP because there would no longer be a need to use shell_exec()
when encrypting/decrypting data.

Another alternative, and one which has already been built as a PHP module, is
Mcrypt. However, it
shares a downside with GnuPG in that you must still store the passphrase within
your script or database.


Conclusion


Having the ability to encrypt data can provide a distinct advantage in this world
of script-kiddies and nosey governments. You are encouraged to explore the power
of bringing PHP and GnuPG together and, if you have the coding mojo, contribute
to the PHP project by building a GnuPG module with GPGME!


About The Author


Darrell brogdon is an independent consultant specializing in Web Application development. He can be reached at dbrogdon@brogdon.biz.

,

9 Responses to “Encryption and Decryption using PHP and GnuPG”

  1. _____anonymous_____ Says:

    Got it working for me on Apache. The code doesn’t do anything useful except show that gpg does its stuff.

    Changed .gnupg to mode 777
    changed the .gnupg/* to mode 644

    <?php
    $msg = "12345600987373792219";
    $gpg = ‘/usr/bin/gpg’;

    $recipient = ‘liser@mydomain.com’;
    putenv("GNUPGHOME=/home/service/.gnupg");

    $emsg = shell_exec("echo $msg | $gpg -e -r $recipient ");

    echo $emsg;

  2. _____anonymous_____ Says:

    Hey guys!
    Seems like I have the same problem as some of you while using GPG through PHP. I have a file called test.php:

    <?php
    $gpg = ‘/usr/bin/gpg’;
    $passphrase = ‘ochse’;
    $encrypted_file = ‘foo.gpg’;
    $unencrypted_file = ‘foo.txt’;

    $cmd = "echo $passphrase | $gpg –passphrase-fd 0 -o $unencrypted_file -d $encrypted_file";
    echo "<br/>cmd: ".$cmd;
    echo "<br/>output: ".shell_exec($cmd);
    echo "<br/>file: ".(file_exists($unencrypted_file)?"yes":"no");
    ?>

    The encrypted file foo.gpg is stored in the same directory as the PHP script.
    Calling the Script gives me:

    cmd: echo ochse | /usr/bin/gpg –passphrase-fd 0 -o foo.txt -d foo.gpg
    output:
    file: no

    But if i copy the cmd and paste it on the shell, it works fine… i already tried –batch –no-tty and -q, nothing changes anything in the behaviour…
    I also tried that different keyring file, but then GPG says something about conflicting commands (which is absolutely logical, just tried it to be sure…)

    If anyone knows how to deal with that, please let me know!

    daniel

    PS: The apache user has the rights to create a file within that directory…

  3. rolandec Says:

    If Apache’s error log says

    gpg: cannot open `/dev/tty’: No such device or address

    we had success using

    gpg –no-tty …

  4. _____anonymous_____ Says:

    Hi,

    I noticed a couple of people commenting that they’re having problems with the code, but GPG works fine from the command line. GPG uses a keyring to store your own public and private keys (and other user’s public keys). By default the keyring is stored in ~/.gnupg, i.e. in the home directory of the user creating the key. This causes a problem for PHP because when you log in you’ll likely be logged in as a different user to Apache. There’s two possible solutions: 1) Manually specify a location for the keyring on the command line (check the gpg man page) which will allow you to put it in a location which is accessible to both Apache and your login user. 2) Use the su command to switch to the Apache user before generating your keys.

    Hope that helps anyone who’s having problems with the sample code.

    Jim.

  5. _____anonymous_____ Says:

    I gave up using this method but couldn’t get any other code to work until I made the /home/user/.gnupg directory world writable. Might be the same issue here.

  6. _____anonymous_____ Says:

    Spent the last 12 hours trying to get this working. Still not working and I can’t see straight.

    I think it has something to do with gpg not running under the local user name from the php page since gpg works from the command line.

  7. _____anonymous_____ Says:

    I tried it a few hours. shell_exec() works with normal commands from php and gpg works from console, even "gpg –help" works, but it doesn’t work with gpg and decryption or symmetric encryption

  8. _____anonymous_____ Says:

    A pear package for PHP5 exists to easily use GPG from PHP. It did not exist at the time this article was written.

    See http://pear.php.net/package/Crypt_GPG.

  9. iosicumine Says:

    If the author reaches the conclusion that the best solution around is using GnuPG Made Easy (read the Alternatives section), then why the heck isn’t presenting that?! Merely using a shell command to encrypt/decrypt… anybody knows to do that.
    On short, I wasted time to read the article.