Storing sensitive information using public key encryption in PHP

To encrypt your sensitive information using public key encryption (also known as asymetric encryption), first you will need to install OpenSSL and generate a key pair in PEM format. I will not cover how to install OpenSSL, details on installing OpenSSL can be found at: http://www.openssl.org/

Once you have installed OpenSSL you will want to use it to generate both a private and public key.

To generate your private key use the following command:

openssl genrsa -aes256 -out private.pem 2048

This will genarate a private key named “private.pem”.  Make sure to keep this key in a secure location, ideally not on the web, application or database server. Keeping the private key on a couple usb drives would be a good idea so you can simply use it off the usb drive when needed to decrypt data and have a few extra usb keys as backups.  If you lose the private key, you will not be able to retrieve the encrypted information. While generating your public key, make sure to use a strong pass-phrase as it will be the last line of defence in protecting your private key. Your private key is the key to the kingdom, once someone has access to it, they will be able to decrypt any information that was encrypted using your public key.

Next generate a public key using the following command:

openssl rsa -in private.pem -out public.pem -outform PEM -pubout

This will generate the public key in a file named public.pem. This public key will be used to encrypt your sensitive information. This public key is not sensitive information and can be shared with anyone and put on your web server. Only the corresponding private key will be able to decrypt information that was encrypted with this public key. Once you have generated the public key you will want to put it on your web server so you can encrypt your sensitive information with it.

To encrypt information in php using your newly generated public key you will want to use the openssl_pkey_get_public() and openssl_public_encrypt() functions. Simply, the first function loads the pem file containing your public key and the second function encrypt your data into a variable using your public key. Here is an example on how to do so:


<?php
$string = "data to encrypt";

// public.pem key needs to point to a valid path or url where your public key is located
$publickey = openssl_pkey_get_public (file_get_contents("public.pem")); 

// this encrypts input $string into $crypt_output using $publickey
openssl_public_encrypt($string, $crypt_output, $publickey); 

// this outputs the encrypted data in binary format
echo $crypt_output; 
?>
$crypt_output is in a binary format, if you plan on storing it in a database text field such as char or varchar then you will want to base64 encode the data first using base64_encode(). For optimal performance, you should load the public key once and use it across request but this code snippet is simplified for demonstration  purposes.
Finally to decrypt your data (here we are assuming the data is in a file named “cipher”),  you would issue the following command to decrypt it using your private key:

openssl rsautl -decrypt -inkey private.pem -in cipher

If you decided to base64 encode your data, make sure to base64 decode it before feeding it to openssl. Ideally you would decrypt that data somewhere outside your web server or database server for further processing using openssl but if you would also like to do it in php (web or cli), it’s also possible to use openssl_pkey_get_private() and openssl_private_decrypt() from within php (pretty much just like the public key encryption example) to decrypt information.

This is the general idea on how public key encryption can be used in php to securely store data on your servers and later decrypt it at another location using the private key. Even if your server gets compromised and the public key and encrypted data is available to attackers they would not be able to decrypt the information using today’s technology if they do not have access to your private key.
Bookmark and Share

Tags: , , , , ,

5 Responses to “Storing sensitive information using public key encryption in PHP”

  1. [...] This post was Twitted by cioa [...]

  2. I found this helpful, and the decrypting a little difficult at first. There were no good examples on decrypting so here’s my input. I had base64 encoded my data to store in my database, hence the “Base64_decode” on my $dataStr:

    ###################################################################
    function DeCrypt($dataStr) {

    $cert = “private.pem”;
    $pass = “passphrase”;

    $fp=fopen($cert,”r”);
    $priv_key=fread($fp,8192);
    fclose($fp);
    $res = openssl_get_privatekey($priv_key,$pass);

    openssl_private_decrypt(base64_decode($dataStr),$newsource,$res);
    return $newsource;

    }
    ####################################################################

  3. adriaan says:

    I created the following code:

    ############################################

    function EnCrypt($dataStr) {
    $cert = “public.pem”;
    $fp = fopen($cert,”r”);
    $pub_key = fread($fp,8192);
    fclose($fp);
    $publickey = openssl_pkey_get_public ($pub_key);
    openssl_public_encrypt($dataStr, $output, $publickey);
    return $output;
    }

    function DeCrypt($dataStr) {
    $cert = “private.pem”;
    $pass = “?????????”;
    $fp = fopen($cert,”r”);
    $priv_key = fread($fp,8192);
    fclose($fp);
    $privatekey = openssl_pkey_get_private ($priv_key, $pass);
    openssl_private_decrypt($dataStr, $output, $privatekey);
    return $output;
    }
    ############################################
    This works.

    But when I add the base64, so I can save the encrypted data to a database, my result from DeCrypt is an empty string.

    here is the code that doesnt work:
    ############################################
    function EnCrypt($dataStr) {
    $cert = “public.pem”;
    $fp = fopen($cert,”r”);
    $pub_key = fread($fp,8192);
    fclose($fp);
    $publickey = openssl_pkey_get_public ($pub_key);
    openssl_public_encrypt(base64_encode($dataStr), $output, $publickey);
    return $output;
    }

    function DeCrypt($dataStr) {
    $cert = “private.pem”;
    $pass = “4dr144r0n”;
    $fp = fopen($cert,”r”);
    $priv_key = fread($fp,8192);
    fclose($fp);
    $privatekey = openssl_pkey_get_private ($priv_key, $pass);
    openssl_private_decrypt(base64_decode($dataStr), $output, $privatekey);
    return $output;
    }
    ############################################

    What am I doing wrong?

  4. cchamberland says:

    Your decrypt is fine, however your problem is that you have reversed your base64 encoding and encryption in your encrypt function. First, you need to encrypt then base64 encode in your encrypt function; that should fix your problem. Let me know if that doesn’t work, i may have sobered up by the time you read this lol :)

  5. localcents says:

    Personally, I find phpseclib’s Crypt_RSA better:

    http://phpseclib.sourceforge.net/

    It’s supposed to support PKCS#1 v2.1 whereas OpenSSL only supports PKCS#1 v1.5. As such, where OpenSSL doesn’t support multi-prime RSA or OAEP padding, phpseclib does.

Leave a Reply