1
votes

I have this application to encrypt a MP3 audio clip using a public key (public.pem) and then decrypt using a private key (private.key). I tried the following method but the encrypted file returns as a 0 byte file. No errors returned.

exec("openssl genrsa -out private.key 2048");
exec("openssl rsa -in private.key -out public.pem -outform PEM -pubout");

$public_key = file_get_contents('./public.pem');
$private_key = file_get_contents('./private.key');

$source = "./sample.mp3";
$dest = "./sample.mp3.enc";

$data = file_get_contents($source);
openssl_public_encrypt($data, $output, $public_key, OPENSSL_NO_PADDING);
file_put_contents($dest, $output);

How can I do this? There are PHP examples to encrypt text but not files using this private/public key way.

2
You cannot encrypt data that is longer than the key size with RSA. Encrypt symmetrically with a random key, then encrypt the symmetric key with RSA.Peter
@Peter Can you please guide me how to do that with an example? I couldn't find supportive PHP resources on web pertaining to this.Teshan Nanayakkara
Note that regarding RSA, a padding must be applied for security reasons (e.g. OPENSSL_PKCS1_PADDING or OPENSSL_PKCS1_OAEP_PADDING). For symmetric encryption, the AES algorithm can be used, which can be carried out in PHP e.g. with openssl_encrypt() (documentation including use cases).Topaco

2 Answers

3
votes

As commented by @Peter the datasize using RSA encryption is limited (depends on RSA keysize) to plaintext lengths of about 60 to 400 bytes only. To solve the problem of encrypting a file (here a MP3-file of about 5 mb) you need to switch to a hybrid encryption as recommended by @Topaco.

Indeed, there are fewer examples for this in public so I wrote a very simple program that does what it promises to do.

The program generates a random encryption key (length 32 bytes for AES-256), a random initialization vector ("iv", 16 bytes long for AES CBC mode) and then encrypts the key with the public key with RSA padding PKCS1_OAEP_PADDING. It writes the encrypted key, the iv and the ciphertext to a file.

For decryption the (encrypted) key is read (256 bytes long) from the file, followed by the iv and the encrypted data. Then the encrypted key is decrypted with the private key and used for the final decryption with AES. In the end the decrypted data is written to a file and voilà - the file is playable.

NOTICE: The main problem in my code could be the loading of the complete files (e.g. original mp3-file on encryption and encrypted file on decryption side) into memory before they get encrypted or decrypted - e.g. having an uncut mp3-file of hundreds of mb size may cause an ""out of memory error" - beware of this.

Security warning: this file comes with absolute NO exception handling and is for educational purpose only!.

The code will load the private and public keys from actual directory as well as the mp3-file processing takes place in this memory.

The output is short:

PHP OpenSSL RSA & AES CBC 256 hybrid encryption
encryption finished
decryption finished

here is the - beware of the limitations and security warning:

<?php
// hybrid encryption
// https://stackguides.com/questions/64693606/php-encrypt-a-file-using-public-key-and-decrypt-using-private-key
echo 'PHP OpenSSL RSA & AES CBC 256 hybrid encryption' . PHP_EOL;

// get filenames
$plainfile = "./whateverittakes.mp3";
$cipherfile = "./whateverittakes.mp3.enc";
$decryptedfile = "./whateverittakesdec.mp3";

// attention: it is a RSA PRIVATE KEY !
// key generation:
// openssl genrsa -out private.key 2048
// openssl rsa -in private.key -out public.pem -outform PEM -pubout
// load private & public key
$public_key = openssl_pkey_get_public(file_get_contents('./public.pem'));
$private_key = openssl_pkey_get_private(file_get_contents('./private.key'));

// generate random aes encryption key
$key = openssl_random_pseudo_bytes(32); // 32 bytes = 256 bit aes key

// now encrypt the aes encryption key with the public key
openssl_public_encrypt($key, $encryptedKey, $public_key, OPENSSL_PKCS1_OAEP_PADDING);

// save 256 bytes long encrypted key
file_put_contents($cipherfile, $encryptedKey);
// aes cbc encryption
// generate random iv
$iv = openssl_random_pseudo_bytes(16);
// save 16 bytes long iv
file_put_contents($cipherfile, $iv, FILE_APPEND);
$data = file_get_contents($plainfile);
$cipher = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
// save cipher
file_put_contents($cipherfile, $cipher, FILE_APPEND);
echo 'encryption finished' . PHP_EOL;

// decryption
// read the data
$handle = fopen($cipherfile, "rb");
// read 256 bytes long encryptedKey
$decryptionkeyLoad = fread($handle, 256);
// decrypt the encrypted key with private key
openssl_private_decrypt($decryptionkeyLoad, $decryptionkey, $private_key, OPENSSL_PKCS1_OAEP_PADDING);
// read 16 bytes long iv
$ivLoad = fread($handle, 16);
// read ciphertext
$dataLoad = fread($handle, filesize($cipherfile));
fclose($handle);
$decrypt = openssl_decrypt($dataLoad, 'AES-256-CBC', $decryptionkey, OPENSSL_RAW_DATA, $ivLoad);
file_put_contents($decryptedfile, $decrypt);
echo 'decryption finished' . PHP_EOL;
?>
-1
votes

I think good way is convert you'r file to byte after that encrypt byte and where you need it decrypt it.

Try :

How to convert base64 string into an audio mp3 file?

and after that use OpenSSL function of php for encrypt and decrypt string(byte) file with private and public key ! I think this is good resource:

https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-a-php-string/