Flo
Flo

Reputation: 137

How to encrypt/decrypt files with php (mcrypt)?

I'm surprised that I didn't find any code snippet, advice or tutorial in the web that explains how to encrypt a file using just standard php components.

So I'm asking your advice: how to encrypt/decrypt files using just mcrypt and php standard functions? I do not have the option to use gnupg. No, actually, my question is: how to do the above without messing up my files? Because I'm already encrypting/decrypting the hell out of these files (with mcrypt/AES), and it works well for jpegs, PDF, some .doc files and interestingly password-secured .docx files. It does not work for non-secured .docx files and numerous other filetypes.

My current code is this. Basically, I really just open the file, whisk the data around with mcrypt/AES, and write it on server/let user download it.

To encode after upload:

// using codeigniter's encryption library, which uses mcrypt and the AES cypher
$this->load->library('encrypt');
$pathandname = $config['upload_path'].$output['content'][$a]['file_name']; 
$theFile = file_get_contents($pathandname);
$fh = fopen($pathandname,'w');
fwrite($fh,$this->encrypt->encode($theFile));
fclose($fh); 

To decode & download:

$this->load->library('encrypt');
$pathandname = $filelocation.$results[0]['encryptedfile']; 
$theFile = file_get_contents($pathandname);
$decrypted = $this->encrypt->decode($theFile);
force_download($filename, $decrypted); // a codeigniter function to force download via headers

Upvotes: 1

Views: 16687

Answers (1)

Scott Arciszewski
Scott Arciszewski

Reputation: 34093

how to encrypt/decrypt files using just mcrypt and php standard functions?

Well, you don't want to use mcrypt. You want to use sodium these days.

Encrypting a File with Sodium (PHP 7.2+, PECL ext/sodium, or sodium_compat)

The relevant API you're looking for is crypto_secretstream.

const CUSTOM_CHUNK_SIZE = 8192;

/**
 * @ref https://stackoverflow.com/q/11716047
 */
function encryptFile(string $inputFilename, string $outputFilename, string $key): bool
{
    $iFP = fopen($inputFilename, 'rb');
    $oFP = fopen($outputFilename, 'wb');

    [$state, $header] = sodium_crypto_secretstream_xchacha20poly1305_init_push($key);

    fwrite($oFP, $header, 24); // Write the header first:
    $size = fstat($iFP)['size'];
    for ($pos = 0; $pos < $size; $pos += CUSTOM_CHUNK_SIZE) {
        $chunk = fread($iFP, CUSTOM_CHUNK_SIZE);
        $encrypted = sodium_crypto_secretstream_xchacha20poly1305_push($state, $chunk);
        fwrite($oFP, $encrypted, CUSTOM_CHUNK_SIZE + 17);
        sodium_memzero($chunk);
    }

    fclose($iFP);
    fclose($oFP);
    return true;
}

Decryption looks like this:

/**
 * @ref https://stackoverflow.com/q/11716047
 */
function decryptFile(string $inputFilename, string $outputFilename, string $key): bool
{
    $iFP = fopen($inputFilename, 'rb');
    $oFP = fopen($outputFilename, 'wb');

    $header = fread($iFP, 24);
    $state = sodium_crypto_secretstream_xchacha20poly1305_init_pull($header, $key);
    $size = fstat($iFP)['size'];
    $readChunkSize = CUSTOM_CHUNK_SIZE + 17;
    for ($pos = 24; $pos < $size; $pos += $readChunkSize) {
        $chunk = fread($iFP, $readChunkSize);
        [$plain, $tag] = sodium_crypto_secretstream_xchacha20poly1305_pull($state, $chunk);
        fwrite($oFP, $plain, CUSTOM_CHUNK_SIZE);
        sodium_memzero($plain);
    }
    fclose($iFP);
    fclose($oFP);
    return true;
}

Using the two functions together:

$key = random_bytes(32);
encryptFile('input.txt', 'cipher.txt', $key);
decryptFile('cipher.txt', 'decrypt.txt', $key);

Upvotes: 2

Related Questions