PHP: Encrypt a file using public key and decrypt using private key - php

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.

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://stackoverflow.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;
?>

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/

Related

why does openssl_x509_fingerprint gives different value from original?

I'm working on a php program that will verify a digital signature. I'm testing with an example (valid) certificate, and decrypting the digital signature, this gives me the sha256 digest which I am trying to match:
$Cert1 = file_get_contents('CERT1/cert_array.json'); // TEST CERT DATA
$Cert1 = json_decode($Cert1, true);
$PublicKey = file_get_contents('CERT2/public_key_rsa.pem'); // CA CERT PUBLIC KEY
openssl_public_decrypt(hex2bin($Cert1['DigitalSignature']), $DecryptedDigitalSignature, $PublicKey, OPENSSL_PKCS1_PADDING); // DECRYPT SIGNATURE USING CA PUBLIC KEY
print('decrypted digital signature:' . bin2hex($DecryptedDigitalSignature) . "\n\n"); // PRINT RESULTS
This outputs the following:
decrypted digital signature:
3031300d0609608648016503040201050004200bf3dcf2340b972e97fe3c8493e11eeee01f298939734690d0b4e79e1f5701b4
There is some padding on the left, I now split this up to get the sha256 digest:
3031300d060960864801650304020105000420 // PADDING
0bf3dcf2340b972e97fe3c8493e11eeee01f298939734690d0b4e79e1f5701b4 // SHA256 DIGEST
Ok, so now I have my sha 256 digest, so I now wish to use the openssl_x509_fingerprint function on the same cert (CERT1) to create my own sha256 digest. This should match the digest that I already extracted from the cert from above. I use this code:
$Cert1 = file_get_contents('CERT1/cert.crt'); // THE CERT IN VALID x509 FORMAT
print(openssl_x509_fingerprint($Cert1, 'sha256'));
I get the following result:
d74157547fb287694b95b2533588c71f8706b0960e023fc4317f4f9a49ad2721
So, my question is, why am I getting "d74157547fb287694b95b2533588c71f8706b0960e023fc4317f4f9a49ad2721" and not "0bf3dcf2340b972e97fe3c8493e11eeee01f298939734690d0b4e79e1f5701b4"?

phpseclib - set IV modify images data URI

I'm using phpseclib to encrypt/decrypt data uri of some images. I've noticed that when I'm using the IV the data:image/png;base64,data:image/jpg;base64,data:image/jpeg;base64 part of the passed data uri will be lost, only the rest of the base64 string will remain and I will be unable to display the images after decryption operations. Is possible to use the IV without loosing the that part of each data uri encrypted?
//data URI creation from uploaded image using PHP-dataURI https://github.com/alchemy-fr/PHP-dataURI
$dataObject = DataURI\Data::buildFromFile('myimage.jpg');
//data URI encrypt
$cipher = new AES();
//set password for encryption
$cipher->setPassword($password);
//set the IV - this will corrupt data uri generated
$cipher->setIV(Random::string($cipher->getBlockLength() >> 3));
//encrypting the data
$output = $cipher->encrypt(DataURI\Dumper::dump($dataObject));
This is the way I've used to solve this issue. I'm new to phplibsec so I was using $cipher->setIV(Random::string($cipher->getBlockLength() >> 3)) method in a wrong way to set and read the IV. The phpseclib documentations are not very useful and lack of examples on how to implement correctly encrypt and decrypt methods, in particular no example of how to manage the IV is provided. After some research here on SO and thanks to the help of community, I've figured out how to manage the IV.
Encryption of the data uri:
//data URI creation from uploaded image using PHP-dataURI https://github.com/alchemy-fr/PHP-dataURI
$dataObject = DataURI\Data::buildFromFile('myimage.jpg');
//data URI encrypt
$cipher = new AES();
//set password for encryption
$cipher->setPassword($password);
//random IV creation
$iv = Random::string($cipher->getBlockSize() >> 3);
//set the IV
$cipher->setIV($iv);
//encrypting the data
$encrypted = $cipher->encrypt(DataURI\Dumper::dump($dataObject));
//output
$output = $iv.$encrypted;
Into the encryption script I've assigned the random generated IV to a variable that after encryption is prepended to the encrypted data. This because the IV is needed to decrypt correctly the data and this imply that it need to be stored in a database or appended/prepended to the data (no, there is no security risk by doing this). The prepended IV then can be extracted from the encrypted data using the substr() function in this way:
//data URI decrypt
$cipher = new AES();
//set previously selected password for encryption
$cipher->setPassword($password);
//extract the IV from encrypted data
$ivLength = $cipher->getBlockLength() >> 3;
$iv = substr($encrypted, 0, $ivLength);
//set the IV
$cipher->setIV($iv);
//removing the IV from the data before decrypt
$data = substr($encrypted, $ivLength);
//decrypting the data
$output = $cipher->decrypt($data);
After the decrypt the original base64 data uri will be returned back as expected.

Encrypt / Decrypt openSSL between Windows PC and PHP running on Linux Web Server

I'm trying to learn how to perform a simple encrypt/decrypt between a Windows 64 machine (my PC) and PHP running on a Linux web server using openSSL.
On my Windows machine, I've installed OpenSSL v1.0.2k for Win64 and I'm using the following command to create an encrypted string using a simple password and a simple key located in secretkey.txt.
enc -aes-256-cbc -A -base64 -nopad -nosalt -pass pass:hello -in secretkey.txt
When I run the command above, I get the following string:
3WE7cuBFhuLCn3/ZBnUrBn68nn3tVn0NKKz63B3uvoc=
Using the string above, I would expect PHP on my Linux web server to be able to decrypt it like this:
$encrypted = '3WE7cuBFhuLCn3/ZBnUrBn68nn3tVn0NKKz63B3uvoc=';
$enc = 'aes-256-cbc';
$password = 'hello';
$key = openssl_decrypt($encrypted, $enc, $password, OPENSSL_ZERO_PADDING);
echo $key .' should equal this-1234-is-4567-my-8910-secret';
But, instead I get this:
9(j���T]��$�W�Ma��S��zz�>.( should equal this-1234-is-4567-my-8910-secret
I've reviewed the following sites and tried multiple versions, but I can't get it to decrypt properly:
Open SSL Enc
Open SSL Command Line
PHP Decrypt
AES requires a key to work. This is a sequence of bytes of exactly the exact length (32 in the case of AES-256). You can’t directly use a password (unless the bytes that make up the password happen to be the right length).
Furthermore, CBC mode requires an initialization vector (IV) of 16 bytes.
The OpenSSL enc command derives the key and the IV to use from the password provided using an internal function EVP_BytesToKey. In order to decrypt a file that has been encrypted with enc you need to reproduce this function. The details of the algorithm are in the docs. In PHP, it might look like this:
$password = 'hello';
$bytes = "";
$last = "";
// 32 bytes key + 16 bytes IV = 48 bytes.
while(strlen($bytes) < 48) {
$last = md5($last . $password, true);
$bytes.= $last;
}
// First 32 bytes are the key, next 16 are the IV.
$key = substr($bytes, 0, 32);
$iv = substr($bytes, 32, 16);
You can then use these values in the openssl_decrypt command:
$enc = 'aes-256-cbc';
$result = openssl_decrypt($encrypted, $enc, $key, OPENSSL_ZERO_PADDING, $iv);

encrypt and decrypt with phpseclib using rsa public and private key , getting decryption error

phpseclib:
php code :
include('Crypt/RSA_XML.php');
$rsa = new Crypt_RSA_XML();
$rsa->loadKeyfromXML($public_key_xml);
$data = "invoice_number=1,100.00&customer_tin=674858994885&serial=ONLYPEMPSERIAL&tin=ONLYPUMPTIN&vat_1_net=2,0000&vat_1_value=3600&vat_2_net=0&vat_2_value=0&vat_3_net=0&vat_3_value=0&vat_4_net=0&vat_4_value=0&vat_5_net=0&vat_5_value=0&vat_6_net=0&vat_6_value=0&vat_7_net=0&vat_7_value=0&vat_8_net=0&vat_8_value=0&payment_mode=3&discount=200&total_gross=9562";
$plaintext = $data;
//define('CRYPT_RSA_PKCS15_COMPAT', true);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$ciphertext = base64_encode(strrev($rsa->encrypt($plaintext)));
echo "data";
echo $ciphertext; // there is encoded value getting
and
then how to decrypt encrypted data using private key:
echo $rsa->decrypt($ciphertext); // this is giving error , decryption error
actually , i want to encrypt in php and decrypt in c# ,
data encrypted successfully and when c# rsa crypto service decrypt this encrypted data then giving error message : invalid base-64 string length
$public_key_xml and $private_key_xml is defined below...
public key :
<RSAKeyValue>
<Modulus>uidt3bPfWozkIkC6nHnRDbXrvjqplfCslV2zP4hKJ6sVjVnPfjMM0ueCuEDFZ9NK+kCWaPNAVhOKKwL8HmoX/7KcFLWkwSoatnrncHTH5STey+bqR1xTFY+Rubj8BZt7D9JJYyLQC46wn4ySVnLWkCZZ9+aaTriEBzGTpUzeRiUTWVprp3oXsA7ZKyn+lhZfMx1ILhcD8dnX7xFHB57jIKvPBxAdT4K7GxdgENeS76I/zmVmlF//JnmtZ/RM1WmRkx8mFmcK/Ky8gLsmIpPPltoyBWIKIf2NQH9kHqHa2gwoPg34LTutV9AACTWuiVOjqU7Gq2BHQcjovXMF8t3Wiw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
private key :
<RSAKeyValue>
<Modulus>uidt3bPfWozkIkC6nHnRDbXrvjqplfCslV2zP4hKJ6sVjVnPfjMM0ueCuEDFZ9NK+kCWaPNAVhOKKwL8HmoX/7KcFLWkwSoatnrncHTH5STey+bqR1xTFY+Rubj8BZt7D9JJYyLQC46wn4ySVnLWkCZZ9+aaTriEBzGTpUzeRiUTWVprp3oXsA7ZKyn+lhZfMx1ILhcD8dnX7xFHB57jIKvPBxAdT4K7GxdgENeS76I/zmVmlF//JnmtZ/RM1WmRkx8mFmcK/Ky8gLsmIpPPltoyBWIKIf2NQH9kHqHa2gwoPg34LTutV9AACTWuiVOjqU7Gq2BHQcjovXMF8t3Wiw==</Modulus><Exponent>AQAB</Exponent><P>9NiLuI9TjNvpAPQqD9ySdMX37OmEDCF02isoovt8hwPpiXcZYH4FeasNZoydRrBUOHTTRrW3xdUYGsCZI0H9tSg+gIjo/k/JhmECT7RuSgjEL7mLpusAhi1RFv81TNERGvWP8V9HtB4oZONgOpdTuNqJwhyZ3+aA3zyy7k1mKJc=</P><Q>wqJndWnlZ2i8sW8zhX9SPUddyf8E+wHek3SYynUNQ3T7zJbk+woqyjMuSImXXuZO47uBJlwskYwR+mJr/AuCR7Y0+jtByJF8RoqkY7ttdhS8CpJ9J2o5YMGcGw0JoJ16L0W6QvhY5Zxb5IAG5nuiPLDlgZYNo7+oKRcDyF+fbC0=</Q><DP>hwimhjMFsTnXV19Rk03it+Q2x8JBwS7ycyA6WSi5lPzjX5z/sefOvTtJOLV0R/gXestzehveLo1Hrflqe7d5ZN+9GMZpOVhnnGUEEVFBQjNzf56lFnmk4Fs9zaESlDr5ZBTqPgR+VygKvxlniOjMk7ZzI0sC0ikeCgA/7o1hOiE=</DP><DQ>Fpj2fBpcaSIu7kbj63b53GWBzScPs/jml6Ys6yyl6pQVfA507XSjvTAuCnv3GCyTMjud5g9DmW5y0+hDc1L+wEa2ZeslWx0RGbuVFIM5VUFZB700TLQ3jzVLY5Si7oP6IKzu0EG3SIlb3e7DXlUyY+uB6ga69K0W4BZs+QGpJ80=</DQ><InverseQ>nob08brDfoswDV8JAkGJIg5T9ktMBRzn5djbAfSorOCCVwW+iRz/hkzSs4LaeMuoC3V5AnLeTg1T7J3op67KGerRwwjXSgCKO4crs2pODcZuIMkaE8e/5Ti1O40yKl05mQaxLk/SgSAhy97HhHoiteg/ttLcrvsCcSfyyxzHT3M=</InverseQ><D>bwqYEbh7EjOa3gfIiRBtMIWFExtBD6zZ9dtH0i0FNvZpy8B38iqXirMImcohNxal0fN3BTGc/ft33sJQDABzQlaTnhLgLU1lU9aqeb1fhANjVzPuKhUbhm/2mFeNFfcyCDUDC7y3Zz19hB7BKAomjSQjZKnNAAo2z2e2T9Mzf5kV8uuYsnoum6LEvEfluQ3q3+9Ua64P0E4D2j2iaOnvpBzTCpeaBMDfWZEe19MaS40d/OrZOwlyAVPCW9RkT3948fC5KDvE0KetYDsrVApRSKzvBUQCVNmcO2o+rhMO1qKvS+zkw2VW5OxGDk/QuHuXIkCyipUEMa/DmK74hoxCaQ==</D></RSAKeyValue>
You must be using a really old version of phpseclib since phpseclib has had built in support for the XML format for quite a few years. So my first recommendation would be to upgrade to the latest version. After that you should be able to do something like this:
<?php
include('Crypt/RSA.php');
$plaintext = 'zzz';
$rsa = new Crypt_RSA();
$rsa->loadKey('<RSAKeyValue>
<Modulus>uidt3bPfWozkIkC6nHnRDbXrvjqplfCslV2zP4hKJ6sVjVnPfjMM0ueCuEDFZ9NK+kCWaPNAVhOKKwL8HmoX/7KcFLWkwSoatnrncHTH5STey+bqR1xTFY+Rubj8BZt7D9JJYyLQC46wn4ySVnLWkCZZ9+aaTriEBzGTpUzeRiUTWVprp3oXsA7ZKyn+lhZfMx1ILhcD8dnX7xFHB57jIKvPBxAdT4K7GxdgENeS76I/zmVmlF//JnmtZ/RM1WmRkx8mFmcK/Ky8gLsmIpPPltoyBWIKIf2NQH9kHqHa2gwoPg34LTutV9AACTWuiVOjqU7Gq2BHQcjovXMF8t3Wiw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>');
define('CRYPT_RSA_PKCS15_COMPAT', true);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
echo base64_encode($rsa->encrypt($plaintext));
If that doesn't work it'd help to see the .NET code you're using to decrypt. Also, if you could encrypt a string in .NET and post the public key you used to do the encryption and the ciphertext itself that'd be useful. From that I could play around with the phpseclib options until I found some combination of options that let me decrypt the string, which would, presumably, in turn, give us insight in how to encrypt the string.

can't decrypt encrypted RSA data by OpenSSL

I generate public and private keys for RSA by
openssl genrsa -des3 -out private.pem 2048
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
and use following php code to encrypt and decrypt data.
<?php
$plaintext = 'Hello';
$publicKey = openssl_pkey_get_public('file://public.pem');
$encrypted = '';
if (!openssl_public_encrypt($plaintext, $encrypted, $publicKey))
die('Failed to encrypt data');
var_dump($encrypted);
if (!$privateKey = openssl_pkey_get_private('file://private.pem', '123456789'))
die('Private Key failed');
$decrypted = '';
if (openssl_open($encrypted, $decrypted, $envelope, $privateKey) === FALSE)
die('Failed to decrypt data');
?>
it will encrypt data ,but to decrypt data it doesn't work and show Failed to decrypt data
it will show some thing like following resultstring(256) "y)ù¿5_÷q$çD±!‘­[’ÓcÜC$Gèïü*ÞEÇGm>ËÂïQÄ„ð­½i=é¨Zs€© |T²»Z”k( ráëµ1,r]o –Òll'T¾i¹Bò}Æ1sËèÝwœÒ„Ä–È‹\1{S'ÆY³Ïà^hŽ™©XO%f7‘Bþ®Ra.ªÜäÆô¼'©³#Ý.H9…ŒÏ\6°ÆýþÆJµ^ðŠØÆr£Ô&ü—Ý*ýÄq ƒÝcÓÚAçOmœi\Ê¿›ãB~ZP1ŒÑÔâ¨S…6—êQ–²x¥^0´Íº(d?G•ÚIWå¡Ä" Failed to decrypt data
If you're using openssl_public_encrypt() to encrypt your data, you need to use openssl_private_decrypt() to decrypt the data.
openssl_open() is for use with openssl_seal().
I'd recommend using the _open() and _seal() variants instead of the _public_encrypt() and _private_decrypt() variants -- the public key mechanisms are intended solely for session key transport and digital signatures. Public-key algorithms are intended for use on random data (or nearly-random data of message digests). Using non-random data with public key algorithms is definitely a mis-use of the algorithms.

Categories