PHP Simple Encryption

GithubIf you find this tutorial and code useful, please take a moment to star it on Github. If you want to help develop it further, fork it.

Encryption is a complicated topic and one that, when done incorrectly, could result in sensitive data being exposed to bad actors or lost due to an inability to decrypt that data at a later date.

The PHP Simple Encryption library is designed to simplify the process of encrypting and decrypting data while ensuring best practices are followed. By default is uses a secure encryption algorithm and generates a cryptologically strong initialization vector (more on that later) so developers do not need to become experts in encryption to securely store sensitive data.

Requirements

  • PHP 7.2+
  • Openssl PHP extension

Installation

To add PHP Simple Encryption to your project, add a dependency on stymiee/php-simple-encryption to your project’s composer.json file if you use Composer to manage the dependencies of your project. Here is a minimal example of a composer.json file that just defines a dependency on PHP Simple Encryption:

"require": {
    "stymiee/php-simple-encryption": "^1"
}

Including it in your project is then as simple as including your vendor autoload file:

require('./vendor/autoload.php');

Basic Usage

Most encryption ciphers require three items to successfully encrypt text:

  1. A secret key (think of this as the password of the encrypted text)
  2. An initialization vector (IV)
  3. The text to be encrypted

The steps to properly encrypt text is as follows:

  1. Create a secret key
  2. Create a secure, randomly generated initialization vector
  3. Encrypt the text using the secret key and IV
  4. Securely store the secret key and IV for later use to decrypt the encrypted text

The steps to properly decrypt encrypted text is as follows:

  1. Securely retrieve the secret key and IV
  2. Decrypt the text using the secret key and IV

Below is an example showing both the encryption and decryption of text. Normally you would not encrypt data and then immediately decrypt it, but this is for illustration purposes only.

use Encryption\Encryption;
use Encryption\Exception\EncryptionException;
 
$text = 'Testing, testing, 123';
$key  = 'secretkey';
try {
    $encryption = Encryption::getEncryptionObject();
    $iv = $encryption->generateIv();
    $encryptedText = $encryption->encrypt($text, $key, $iv);
    $decryptedText = $encryption->decrypt($encryptedText, $key, $iv);
 
    printf('Cipher   : %s%s', $encryption->getName(), PHP_EOL);
    printf('IV       : %s%s', base64_encode($iv), PHP_EOL);
    printf('Encrypted: %s%s', $encryptedText, PHP_EOL);
    printf('Decrypted: %s%s', $decryptedText, PHP_EOL);
}
catch (EncryptionException $e) {
    echo $e;
}

Outputs

Cipher   : AES-256-CBC
IV       : Cz5BfO8PDgwFTlDNXoFiAQ==
Encrypted: Hj3xYHJnTWq5ZkHRGbnGdh4qRXd3PEzgI0Rbru9GynY=
Decrypted: Testing, testing, 123

You will notice the secret key is in plain text in the example above. This value is something you can create however you want. Randomness is not considered important here. However, your initialization vector (IV) should be as random as possible. A common pitfall developers fall into when creating an IV is to choose a method of generation that is insufficiently random if it is random at all.

PHP Simple Encryption solves that problem by handling IV generation. IVs are generated using openssl_random_pseudo_bytes() which generates a random string of bytes (of a chosen length). The randomness of these bytes will be cryptologically strong and should always be preferred to a home grown solution. Additionally, if a better method should become available, by using PHP Simple Encryption to generate your IV you can gain this benefit simply by updating your version of the library without the need to make any changes in your code.

But what happens if openssl_random_pseudo_bytes() is unable to generate a cryptologically strong string of bytes? If this were to occur, most likely because the application is running on a older system, the library falls back to random_bytes() which is PHP’s built in equivalent to openssl_random_pseudo_bytes().

In the unlikely event that openssl_random_pseudo_bytes() and random_bytes() both fail to produce an IV, the library offers the option to fall back to a built in random string generator. This IV generator does not generate a cryptologically strong value and should not be used in any environment where data security is a requirement. This can be enabled by passing true to generateIv():

$iv = $encryption->generateIv(true);

true should be a boolean and not a string or number.

Unless encryption is an unnecessary additional layer of security, an abundance of caution should be used when enabling this option.

Once you have generated an IV, you can use it to encrypt you value. But to decrypt that value later you will need that IV as part of the decryption process. generateIv() will generate bytes that are not human friendly (but still computer friendly). To make it easier for humans to work with you should consider base64 encoding that string when storing it:

$savedIv = base64_encode($iv);

By default, PHP Simple Encryption uses the AES with 256-bit encryption in CBC (Cipher Blocker Chaining) mode (AES-256-CBC). This form of encryption is considered very secure which is why it is the default encryption method when encrypting data using this library. However, your business requirements may require you to use a different cipher. Perhaps to decrypt text encrypted by a third party or before this method of encryption was available. You can set the encryption cipher to any cipher supported by your system and the PHP Simple Encryption library. To do this you specify the name of the encryption cipher you wish to use when creating your encryption object.

$encryption = Encryption::getEncryptionObject('SM4-CFB');

Not every encryption algorithm requires an IV. Electronic codebook (ECB) mode is one such example.

use Encryption\Encryption;
use Encryption\Exception\EncryptionException;
 
$text = 'Testing, testing, 123';
$key  = 'secretkey';
try {
    $encryption = Encryption::getEncryptionObject('AES-128-ECB');
    $encryptedText = $encryption->encrypt($text, $key);
    $decryptedText = $encryption->decrypt($encryptedText, $key);
 
    printf('Cipher   : %s%s', $encryption->getName(), PHP_EOL);
    printf('Encrypted: %s%s', $encryptedText, PHP_EOL);
    printf('Decrypted: %s%s', $decryptedText, PHP_EOL);
}
catch (EncryptionException $e) {
    echo $e;
}

Outputs

Cipher   : AES-128-ECB
Encrypted: 2d8vGmPYXNIRbsvjnXgyzoIBEq0pAelbfYZSIakP+k8=
Decrypted: Testing, testing, 123

ECB mode is not recommended for use in cryptographic protocols and you should not use them unless you are working with a legacy system that requires its use.

Authenticated encryption with associated data (AEAD) takes encryption a step further and in addition to securing the data through encryption, it ensures the data’s authenticity as well. In addition to outputting encrypted text it also provides an authentication tag. This tag is used to authenticate the encrypted text. In other words it validates that the tag matches the encrypted text generated with it. CCM and GCM mode ciphers use this form of encryption.

use Encryption\Encryption;
use Encryption\Exception\EncryptionException;
 
$text = 'Testing, testing. 123';
$key  = 'secretkey';
try {
    $encryption = Encryption::getEncryptionObject('AES-128-GCM');
    $iv = $encryption->generateIv();
    $encryptedText = $encryption->encrypt($text, $key, $iv, $tag);
    $decryptedText = $encryption->decrypt($encryptedText, $key, $iv, $tag);
 
    printf('Cipher   : %s%s', $encryption->getName(), PHP_EOL);
    printf('IV       : %s%s', base64_encode($iv), PHP_EOL);
    printf('Tag      : %s%s', base64_encode($tag), PHP_EOL);
    printf('Encrypted: %s%s', $encryptedText, PHP_EOL);
    printf('Decrypted: %s%s', $decryptedText, PHP_EOL);
}
catch (EncryptionException $e) {
    echo $e;
}

Outputs

Cipher   : AES-128-GCM
IV       : /x3+e5+XCnjpf5ec
Tag      : yRuma5vRLsIfh/WIevOhEA==
Encrypted: HJr+C5BUgcwf537RDSNcIysULilYP+Rq
Decrypted: Testing, testing. 123

Under the hood of Encryption::encrypt() the $tag parameter is passed by reference to openssl_encrypt(). A value is assigned to it upon a successful function call. You will need to store value. Like the IV, you may want to base64 encode it.

When using the PHP Simple Encryption library, if the text to be encrypted contains trailing null characters they will be removed when decrypting those values.

Supported Ciphers

The PHP Simple Encryption library currently defaults to AES-256-CBC. This may change in future versions and will result in a major version bump when this occurs. To determine what cipher you are using you can call the getName() method on your encryption object:

$encryption = Encryption::getEncryptionObject();
$cipherName = $encryptuion->getName(); // AES-256-CBC

To get a list of ciphers supported by your system and this library you can call Encryption::listAvailableCiphers() to receive an array of available ciphers. This list is an intersection of available ciphers from your system’s installed version of Openssl and ciphers supported by this library.

$availableCiphers = Encryption::listAvailableCiphers();
var_export($availableCiphers);

Below is a list of PHP Simple Encryption’s supported ciphers as of the release of version 1.0.0:

AES Aria Blowfish/Camellia Cast5/DES Idea/RC2/SeedSM4
aes-128-cbc aria-128-cbc bf-cbc cast5-cbc id-aes128-ccm
aes-128-ccm aria-128-ccm bf-cfb cast5-cfb id-aes128-gcm
aes-128-cfb aria-128-cfb bf-ecb cast5-ecb id-aes192-ccm
aes-128-cfb1 aria-128-cfb1 bf-ofb cast5-ofb id-aes192-gcm
aes-128-cfb8 aria-128-cfb8 camellia-128-cbc chacha20 id-aes256-ccm
aes-128-ctr aria-128-ctr camellia-128-cfb chacha20-poly1305 id-aes256-gcm
aes-128-ecb aria-128-ecb camellia-128-cfb des-cbc idea-cbc
aes-128-gcm aria-128-gcm camellia-128-cfb des-cfb idea-cfb
aes-128-ofb aria-128-ofb camellia-128-ctr des-cfb1 idea-ecb
aes-128-xts aria-192-cbc camellia-128-ecb des-cfb8 idea-ofb
aes-192-cbc aria-192-ccm camellia-128-ofb des-ecb rc2-40-cbc
aes-192-ccm aria-192-cfb camellia-192-cbc des-ede-cbc rc2-64-cbc
aes-192-cfb aria-192-cfb camellia-192-cfb des-ede-cfb rc2-cbc
aes-192-cfb1 aria-192-cfb8 camellia-192-cfb des-ede-ofb rc2-cfb
aes-192-cfb8 aria-192-ctr camellia-192-cfb des-ede3-cbc rc2-ecb
aes-192-ctr aria-192-ecb camellia-192-ctr des-ede3-cfb rc2-ofb
aes-192-ecb aria-192-gcm camellia-192-ecb des-ede3-cfb1 seed-cbc
aes-192-gcm aria-192-ofb camellia-192-ofb des-ede3-cfb8 seed-cfb
aes-192-ofb aria-256-cbc camellia-256-cbc des-ede3-ofb seed-ecb
aes-256-cbc aria-256-ccm camellia-256-cfb des-ofb seed-ofb
aes-256-ccm aria-256-cfb camellia-256-cfb desx-cbc sm4-cbc
aes-256-cfb aria-256-cfb1 camellia-256-cfb sm4-cfb
aes-256-cfb1 aria-256-cfb8 camellia-256-ctr sm4-ctr
aes-256-cfb8 aria-256-ctr camellia-256-ecb sm4-ecb
aes-256-ctr aria-256-ecb camellia-256-ofb
aes-256-ecb aria-256-gcm
aes-256-gcm aria-256-ofb
aes-256-ofb
aes-256-xts

Help & Support

If you require assistance using this library start by viewing the HELP.md file included in this package. It includes common problems and their solutions.

If you continue to have difficulty using this code please do not contact me through this website. Since this software is free to the community, the community can assist me in supporting it. I am an active participant at StackOverflow. Others also frequent it who are also capable of assiting you with this code. When posting your question there, or anywhere for that matter, be sure to include the following:

  • A link to either this tutorial and/or the GitHub repository so others can see what software you are using and can download it if necessary so if they attempt to reproduce the problem you are having.
  • Your code as it is implemented in your software. Make sure you format it so it is readable by others. Be sure to include all of the relevant pieces including:
    • Your unencrypted text
    • The key you are using
    • The IV you are using
    • The output of your method calls
  • A description of what you are expecting your code to do (but it is not happening).
  • Any error message you are getting.

Related Posts:

2 thoughts on “PHP Simple Encryption

  1. Hi.

    Thanks for a very interesting article, and a very useful library.

    Data-encryption has always been an interest of mine. I have studied many different ways that data can be “scrambled”, hashed, and also other means of checking data integrity.

    With so much effort, not to mention lots of money, being devoted to breaking into computer systems and data exfiltration, data encryption becomes just as important as the security of the systems themselves. I have always believed in “security through layers” as the best way to approaching an effective solution in securing any system that is connected to any other.

    Any contribution to the already existing, hopefully – good-quality, means of achieving better security – comes at a time when we constantly hear of data breaches and compromised systems, even involving the systems employed by many Fortune 500 companies.

    I will like to experiment with the library you have offered up to the public, to further educate myself on what other applicable methods I may derive from my research. I am by no means a “security professional”, but an avid fan of technology – and how to get the best from its use. The field study of data-security and integrity is an on-going process of learning, and refining what we already knew – or did not know before.

    My hearty congrats on a fine piece of work! 🙂

    – Jim S.

Leave a Reply

Your email address will not be published. Required fields are marked *