Encryption using PHP and OpenSSL

In this post we will see how to encrypt and decrypt data using PHP OpenSSL. We will be using asymmetric (public/private key) encryption. In this encryption a user generates a pair of public / private keys and gives the public key to anyone who wants to send the data. The sender of the data will encrypt the data using the public key of the receiver. The receiver will then decrypt the received data using his own private key. The data encrypted using the public key can only be decrypted using the corresponding private key.

The amount of data you can encrypt and the size of the resulting encrypted data are both determined by the size of the key. The size of the encrypted data is the number of bytes in the key (rounded up). So for a 1024-bit key this will be 128 bytes (1024 divided by 8). Even if you were to encrypt a string with a single byte in it, the resulting encrypted data would still be 128 bytes long. The maximum amount of data that can be encrypted is 11 bytes less than this, since OPENSSL uses a padding scheme (if no padding scheme is used by using OPENSSL_NO_PADDING flag then we can use the entire length). So for a 1024-bit key, up to 117 bytes can be encrypted.

We cannot encrypt and send large amount of data at once, so we need to divide it into smaller chunks. In the following example we divide the data into multiple chunks before encrypting it and then combine the encrypted data and send it. The receiver then breaks the encrypted data into chunks and decrypts it.

Generating public / private Keys

We will first need a pair of public / private keys. Here is a sample PHP code to generate the public / private Keys.

<?php
$privateKey = openssl_pkey_new(array(
    'private_key_bits' => 2048,      // Size of Key.
    'private_key_type' => OPENSSL_KEYTYPE_RSA,
));
// Save the private key to private.key file. Never share this file with anyone.
openssl_pkey_export_to_file($privateKey, 'private.key');

// Generate the public key for the private key
$a_key = openssl_pkey_get_details($privateKey);
// Save the public key in public.key file. Send this file to anyone who want to send you the encrypted data.
file_put_contents('public.key', $a_key['key']);

// Free the private Key.
openssl_free_key($privateKey);

The above code will generate a pair of public / private keys. Never share the private key with anyone. Give the public key to anyone who will send you encrypted data.

Encrypting data

Here is the code that can be used to encrypt the data. This code assumes that we already have the recipient’s public key.

<?php
// Data to be sent
$plaintext = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eleifend vestibulum nunc sit amet mattis. Nulla at volutpat nulla. Pellentesque sodales vel ligula quis consequat. Suspendisse dapibus dolor nec viverra venenatis. Pellentesque blandit vehicula eleifend. Duis eget fermentum velit. Vivamus varius ut dui vel malesuada. Ut adipiscing est non magna posuere ullamcorper. Proin pretium nibh nec elementum tincidunt. Vestibulum leo urna, porttitor et aliquet id, ornare at nibh. Maecenas placerat justo nunc, varius condimentum diam fringilla sed. Donec auctor tellus vitae justo venenatis, sit amet vulputate felis accumsan. Aenean aliquet bibendum magna, ac adipiscing orci venenatis vitae.';

echo 'Plain text: ' . $plaintext;
// Compress the data to be sent
$plaintext = gzcompress($plaintext);

// Get the public Key of the recipient
$publicKey = openssl_pkey_get_public('file:///path/to/public.key');
$a_key = openssl_pkey_get_details($publicKey);

// Encrypt the data in small chunks and then combine and send it.
$chunkSize = ceil($a_key['bits'] / 8) - 11;
$output = '';

while ($plaintext)
{
	$chunk = substr($plaintext, 0, $chunkSize);
	$plaintext = substr($plaintext, $chunkSize);
	$encrypted = '';
	if (!openssl_public_encrypt($chunk, $encrypted, $publicKey))
	{
		die('Failed to encrypt data');
	}
	$output .= $encrypted;
}
openssl_free_key($publicKey);

// This is the final encrypted data to be sent to the recipient
$encrypted = $output;

Decrypting data

Once a user receives encrypted data using his public key, the user can decrypt it using his own private key. Here is sample code to decrypt the encrypted data.

<?php
// Get the private Key
if (!$privateKey = openssl_pkey_get_private('file:///path/to/private.key'))
{
	die('Private Key failed');
}
$a_key = openssl_pkey_get_details($privateKey);

// Decrypt the data in the small chunks
$chunkSize = ceil($a_key['bits'] / 8);
$output = '';

while ($encrypted)
{
	$chunk = substr($encrypted, 0, $chunkSize);
	$encrypted = substr($encrypted, $chunkSize);
	$decrypted = '';
	if (!openssl_private_decrypt($chunk, $decrypted, $privateKey))
	{
		die('Failed to decrypt data');
	}
	$output .= $decrypted;
}
openssl_free_key($privateKey);

// Uncompress the unencrypted data.
$output = gzuncompress($output);

echo '<br /><br /> Unencrypted Data: ' . $output;

List of all PHP functions for OpenSSL can be found at OpenSSL Functions

Related posts:

  1. How to create CSV file using PHP
  2. How to sort a multi-dimension array by value in PHP
  3. How to generate passwords for .htpasswd using PHP
  4. Generating bcrypt .htpasswd passwords using PHP

10 thoughts on “Encryption using PHP and OpenSSL”

  1. hi thank you for the lovely explanation but i am getting an error ….i am a starter.This are the errors that i am getting–

    Warning: openssl_pkey_export_to_file() [function.openssl-pkey-export-to-file]: cannot get key from parameter 1 in J:\wamp\www\rsa\new one\pp.php on line 7

    Warning: openssl_pkey_get_details() expects parameter 1 to be resource, boolean given in J:\wamp\www\rsa\new one\pp.php on line 10

  2. Hi, thanks for such nice explanation, I worked on these code.
    Getting the correct result while work in only one page but while using the two page (mean first.php page is creating a public and private key and passing the encrypted text to second.php page) not giving the output.

  3. it is not working, I always get “Failed to encrypt data”. File path is correct. var_dump chunkSize gives me float(245). The chunk parameter given to openssl_public_encrypt() also looks good.. I don’t know where it comes from.. Appreciate any help or idea.

    1. This is because openssl_public_encrypt uses a padding scheme by default which takes a minimum of 11 bytes (it will be longer if the value you’re encrypting is smaller). If you don’t use any padding scheme by specifying OPENSSL_NO_PADDING flag, then you don’t need to subtract 11.

  4. First thank you for this information. I read that RSA is not suitable for large data encryption, and this should be used for decrypting keys only that can be used to decrypt large data that should have been encrypted with symetric encryption AES or other rather than the nonsymetric ones. I have been struggeling with it all day testing, till i got it now working.

  5. Thanx – this article war really helpful for me.

    Just one thing to consider:

    Your Code in “encrypting”, line 10 is:
    $publicKey = openssl_pkey_get_public(‘file:///path/to/public.key’);

    This assumes, the reader knows to get the file-content – this part ist missing.
    The complete code is this:
    $publicKey = openssl_pkey_get_public( file_get_contents(‘public.key’) );

    Also in “decrypting”, line 03.

    Thanx and greetings,
    Anton

Leave a Reply