Showing posts with label AES-256-CBC. Show all posts
Showing posts with label AES-256-CBC. Show all posts

Sunday, February 16, 2020

PHP - Encryption and Decryption of Large Files with OpenSSL

PHP lacks a build-in function to encrypt and decrypt large files. openssl_encrypt can be used to encrypt strings, but loading a huge file into memory is a bad idea.

This example uses the symmetric AES-256-CBC algorithm to encrypt smaller chunks of a large file and writes them into another file.
<?php
define('FILE_ENCRYPTION_BLOCKS', 10000);
/**
 * Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
 *
 * @param string $source Path to file that should be encrypted
 * @param string $key The key used for the encryption
 * @param string $dest File name where the encryped file should be written to.
 * @return string|false  Returns the file name that has been created or FALSE if an error occured
 */
function encryptFile($source, $key, $dest)
{
    $key = substr(sha1($key, true), 0, 16);
    $iv = openssl_random_pseudo_bytes(16);

    $error = false;
    if ($fpOut = fopen($dest, 'w')) {
        // Put the initialzation vector to the beginning of the file
        fwrite($fpOut, $iv);
        if ($fpIn = fopen($source, 'rb')) {
            while (!feof($fpIn)) {
                $plaintext = fread($fpIn, 16 * FILE_ENCRYPTION_BLOCKS);
                $ciphertext = openssl_encrypt($plaintext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
                // Use the first 16 bytes of the ciphertext as the next initialization vector
                $iv = substr($ciphertext, 0, 16);
                fwrite($fpOut, $ciphertext);
            }
            fclose($fpIn);
        }
        else {
            $error = true;
        }
        fclose($fpOut);
    }
    else {
        $error = true;
    }

    return $error ? null : $dest;
}

/**
 * Dencrypt the passed file and saves the result in a new file, removing the
 * last 4 characters from file name.
 *
 * @param string $source Path to file that should be decrypted
 * @param string $key The key used for the decryption (must be the same as for encryption)
 * @param string $dest File name where the decryped file should be written to.
 * @return string|false  Returns the file name that has been created or FALSE if an error occured
 */
function decryptFile($source, $key, $dest)
{
    $key = substr(sha1($key, true), 0, 16);

    $error = false;
    if ($fpOut = fopen($dest, 'w')) {
        if ($fpIn = fopen($source, 'rb')) {
            // Get the initialzation vector from the beginning of the file
            $iv = fread($fpIn, 16);
            while (!feof($fpIn)) {
                $ciphertext = fread($fpIn, 16 * (FILE_ENCRYPTION_BLOCKS + 1)); // we have to read one block more for decrypting than for encrypting
                $plaintext = openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
                // Use the first 16 bytes of the ciphertext as the next initialization vector
                $iv = substr($ciphertext, 0, 16);
                fwrite($fpOut, $plaintext);
            }
            fclose($fpIn);
        }
        else {
            $error = true;
        }
        fclose($fpOut);
    }
    else {
        $error = true;
    }

    return $error ? null : $dest;
}

$key = 'my secret key';

$fileName = __DIR__ . '/testfile.txt';
file_put_contents($fileName, 'File would be encrypted...');

$result = encryptFile($fileName, $key, $fileName . '.enc');
if ($result) {
    echo "FILE ENCRYPTED TO " . $result;

    $result = decryptFile($result, $key, $fileName . '.dec');
    if ($result) {
        echo "<BR>FILE DECRYPTED TO " . $result;
    }
}
?>