Sander
Sander

Reputation: 53

Encrypting (large) files in PHP with openSSL

I'm trying to encrypt (big) files in PHP using AES and have looked into using Mcrypt and OpenSSL, the problem is all solutions I have found so far only encrypt strings, and the files I'm trying to encrypt would trigger the max memory limit for PHP (which unfortunately can't be set higher), how would I go about achieving this?

Upvotes: 5

Views: 9550

Answers (5)

Nicolas Chevallier
Nicolas Chevallier

Reputation: 71

I create a simple Github project available via composer https://github.com/bigb06/PHPCypherFile

PHPCypherFile provides a robust solution for encrypting large files securely without significant memory overhead. It combines the power of RSA for public/private key encryption and AES-256-CBC for symmetric encryption, ensuring both performance and security.

function encryptFile($inputFile, $outputFile, $publicKey) {
     if(!exenter code heretension_loaded('openssl')) {
          throw new Error('OpenSSL is not installed');
     }

     if ($publicKey === false) {
          throw new Error('Invalid public key');
     }

     $fpSource = fopen($inputFile, 'rb');
     if (!$fpSource) {
          throw new Error('Error opening input file '.$inputFile);
     }

     $fpDest = fopen($outputFile, 'wb');
     if (!$fpDest) {
          throw new Error('Error opening output file '.$outputFile);
     }
     
     $ivLength = openssl_cipher_iv_length(self::CYPHER);
     $iv = openssl_random_pseudo_bytes($ivLength);

     $keyLength = 32; // 256-bit key
     $symmetricKey = openssl_random_pseudo_bytes($keyLength);

     // Encrypt the symmetric key with the public key
     openssl_public_encrypt($symmetricKey, $encryptedSymmetricKey, $publicKey);


     // Write the encrypted symmetric key to the destination file
     if(fwrite($fpDest, $encryptedSymmetricKey) === FALSE){
          throw new Error('Error writing encrypted symmetric key to the destination file '.$outputFile);
     }

     // Write the IV to the destination file
     fwrite($fpDest, $iv);

     while (!feof($fpSource)) {
          $plaintext = fread($fpSource, $ivLength * self::FILE_ENCRYPTION_BLOCKS);
          $ciphertext = openssl_encrypt($plaintext, self::CYPHER, $symmetricKey, OPENSSL_RAW_DATA, $iv);
          $iv = substr($ciphertext, 0, $ivLength);
          fwrite($fpDest, $ciphertext);
     }

     fclose($fpSource);
     fclose($fpDest);
}

Upvotes: 0

exussum
exussum

Reputation: 18550

Updated answer

Encrypting big files is a hard problem as its hard to verify that no "chunk" has been tampered with.

Use a library such as https://github.com/defuse/php-encryption

\Defuse\Crypto\File::encryptFile( 'in.file', 'out.file', $key );

-- Old answer --

http://www.shellhacks.com/en/Encrypt-And-Decrypt-Files-With-A-Password-Using-OpenSSL

$ openssl enc -aes-256-cbc -salt -in file.txt -out file.txt.enc

to not use too much memory, you want a stream cipher. Call this in PHP with backticks `

or with shell_exec

Make sure that the variables are NOT user input (eg user cant control file.txt). generate them yourself.

Edit

As shell exec is not available

http://jeremycook.ca/2011/03/20/easy-file-encryption/

There is a solution there. Though and I can't stress this enough. Stream ciphers are hard I have not reviewed the code there fully nor do I think I am capable. Using open SSL directly is a much better option

http://php.net/manual/en/filters.encryption.php

Is the example code

Upvotes: 1

stollr
stollr

Reputation: 7183

I have published two functions which encrypt and decrypt even large files with the help of openssl_encrypt() using the AES-128-CBC algorithm.

Please refer to this openssl_encrypt().

Upvotes: 2

Maarten Bodewes
Maarten Bodewes

Reputation: 93938

You could use CBC encryption using Mcrypt and then encrypt a segment of data at a time. Make sure that the segment is x times the block size of the used cipher (e.g. 16 bytes for AES). Encrypt the segment and take the last block of the generated ciphertext and use it as IV for the next segment. The final segment should be PKCS#7 padded (plenty of examples out there including in the mcrypt_encrypt comments).

By chaining the segments together you get a ciphertext indistinguishable from a single encrypt (test your code using this information). Decryption is identical, using the ciphertext as IV. To see how it works, look at the CBC encryption method:

enter image description here


EDIT: if possible you should use the OpenSSL equivalent functionality. That's not (well) documented, but you should be able to do the same using the code found in the link within the comment that Scott mentioned. Note that you should first perform everything without padding, and then for the final segment with padding.

Upvotes: 4

Justin Workman
Justin Workman

Reputation: 388

Using SSL on your website would take care of it for you. Any files that are transmitted are encrypted by the client browser, and server using the HTTPS protocol.

As far as storing the encrypted versions of the files I would not recommend.

Upvotes: -6

Related Questions