php openssl_encrypt generates null value when using openssl_decrypt - php

I want to encrypt a number using openssl_encrypt in Yii Framework;
I managed to encrypt the number;
When trying to decrypt the number, i get a null value;
class Utils {
/*
* variables and values used to encrypt and decrypt the cnp
*/
public static $textToEncrypt = "My super secret information.";
public static $encryptionMethod = "AES-256-CBC"; // AES is used by the U.S. gov't to encrypt top secret documents.
public static $secretHash = "25c6c7ff35b9979b151f2136cd13b0ff";
public static $options = false;//options can be one of OPENSSL_RAW_DATA, OPENSSL_ZERO_PADDING or false
public static $iv = '1234567890123456';
}
this is how I encrypt the number, and this part works, as i get a result:
$this->user->cnp = openssl_encrypt($this->user->cnp, Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
This is how I decrypt, but I get a null value for the number:
$a = openssl_decrypt($model->cnp, Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
echo 'cnp decripted: ' . $a;
why don't i get the original value when using these function:
echo 'cnp encrypted: ' . openssl_encrypt('1850302260089', Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
echo ' cnp decripted' . openssl_decrypt('1850302260089', Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);

This has nothing to do with openssl but the value you are trying to decrypt
Where did you get $model->cnp from ??? and that is where the problem is
Try
$cnpData = openssl_encrypt($this->user->cnp, Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
$a = openssl_decrypt($cnpData , Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
echo 'cnp decripted: ' . $a
Just tested it and it works perfectly

Related

WSSE - XML SOAP security and key encryption and storing (EncryptedData/EncryptedKey)

I have now spent the last couple of days to find documentation about this..
I need to send a XML via SOAP with the WSSE security header, but don't know how to encrypt and store the encrypted keys
Here is an example
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-1B758D26C51BFCD86614340101135741">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference>
<wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIIDODCCAiCgAwIBAgIGAU0FlCVCMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxCYW5rIENvbm5lY3QxFTATBgNVBAsTDEJhbmsgQ29ubmVjdDEdMBsGA1UEAxMUQmFuayBDb25uZWN0IElBLXRlc3QwHhcNMTUwNDI5MTQyODI0WhcNMTgwNDI5MTQyODI0WjAcMRowGAYDVQQDExFiYW5rIGNvbm5lY3QtdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI23KdtaRKPTFTe/A1PnsF9dpSlTiXurKmio0OCgTP9wClHwync3JsInRwGTooA20P9zWobUnEFbEiAgRVYCxuYoldRE6NLhSC854/YTjMBeevH1TNa38lpavGiI4UwFhg70U9/JuYs21hoFyzVfaWlVfOkAMm1U/n4wHq6FZW461S5PY4A/UI1Mr8WgeIHU9GqMBtFvjynzq3SLenOPgdmKtyJ3V8EOU+DlgwKmDbxMVMtYNDZtoQvOWnuvlJ6ICDcqcW7OUkmwCKodjxxPvrdaPxyZDhT7h4FgRtrAOS8qR6L7x9D4ZIoxOMPudGvr99OSb4KVtaAEt/R7hKxG3OsCAwEAAaNCMEAwHwYDVR0jBBgwFoAU680YSkZnx1IaJAmI49LlTGiia0wwHQYDVR0OBBYEFMaWOY7Vf/iB3WVA96j5kRtbF8prMA0GCSqGSIb3DQEBCwUAA4IBAQAJ+bssSFWE6KsYT7HSDKag4Eot7yNGMY4Don/MilDnOREdu20QUS131DKrSkpBQiCXbyRUQjUoun4yue0EG+rlG3QUIlNNdJ4KZJB+dTYdLUV7XTYJNPimKAmoZ+PFNvT1eGgWcMT+MbTfpk0mw0V8IprYGa8UPchd6vtSVwpbTcPc/F4bgUTlm/V+FG4bQS61gF0koj0DEZjzat7CBHpozRgfRlXgwu26vnhWGc99uKH4GAKN4JpqPi/6Yz+7iQNJUC3yeezgBxFrIXuLpkBZSP4zunf9VxsICnxkFUXOTuYBdcbhPNzqMknD5ijFcFRZITwdv7x3uJGLkM7iUfBp</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>af9+FhA91ytLwjeRvTYJsRCkhjHmAQGwqYwMBoNZBn7BZhF/a6EUpM9ByarVhx1SRCpjW5fb8tBVuJO1ZkjfTUZ5EAh/oDLbkmwPdSAAVzmAURHwCq3XQgMZV3lAczlLnPamxjjZBCGqxvAmBo1CvFFPC4AcBedqY92mP8XGyVHpS7JYKOxqXK2vUA1by7371x+Mu0aoS2zJPyPLa1IPwOYgR9qicmWz1RNPiEVA8ZBCN0NRyg7FLJxdUcE81z+1SjButBo2j3qcwkNcecHzZAnweY+LSWp3H5JA3WNzUHUuvFHEaPzT5jd7fUI16xo8NLK8/Rd8Eq/zDD+T3baeVQ==</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference URI="#ED-1B758D26C51BFCD86614340101135852"/>
</xenc:ReferenceList>
</xenc:EncryptedKey>
</wsse:Security>
<technicalAddress xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"/>
<activationHeader xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<organisationIdentification>
<mainRegistrationNumber>8079</mainRegistrationNumber>
<isoCountryCode>DK</isoCountryCode>
</organisationIdentification>
<functionIdentification>112233445566778899</functionIdentification>
<erpInformation/>
<endToEndMessageId>d28b6a7dad414014a59029ef1a7e84d4</endToEndMessageId>
<createDateTime>2015-06-11T10:08:33.258+02:00</createDateTime>
</activationHeader>
</soap:Header>
<soap:Body>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-1B758D26C51BFCD86614340101135852" Type="http://www.w3.org/2001/04/xmlenc#Content">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
<wsse:Reference URI="#EK-1B758D26C51BFCD86614340101135741"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>dTSVuEJ90OYguQOsOz2ZtcE2mybwuvVl19pp7/e5yuvNygx3w5v+prpEvbjYLauiIAB3lrVDK2astJeYJGnDbaVJVeU0YqH5ItYVn7Wz36jJM52KB+UNbYo8EdTKYjsZuADzH+tAoA+pwYxGBXMEQctNI+C711HgP2hbpHNYOG7nAMOIrP/0B3FCy+st+9CbYlwAEENreTYunEEA41hciFnWCsIx0el7OeuiA6V51fAmvrF19RPNKwaptvbvmVdKj//RQ/0U1kRny16mDnFfX92bI3HBQm4XJA0nEfSvio7EUAAdhe77GMfu7+JELqXNowPGPLlvrbCFYnQhxGRITHtTIEbtJA6MKtBzHgjtw5pt7oWxKgGUnaJTfOPOSv43RLFGggkT/+gTjnZOagu8hhXp0x5HXJuZzw90aIS3jAfSPDc2ivct4WhWk0wcuQyC2rAh4I7gtiR+LqJJGqvucw4S+NR95FunKHKEW4yasKW1oU31/rRbp4Bmwo6BPsQlxnaSHPtk68IVkYDBslz1A5gOP+M/Iam2WI02y6sE/7aAH1ruN3pZlVuYFc3JDNHOPOvevP110d60lroknGdc9vxcFfj48OCKw/8Ed6tiXtAvk0Qu9Qt4ZyLUoPKIWEqjdLjwVadTDJQFAxRptNgiCos7s0czadUu7FNCRxfndjDxhA7trvys44ufEyK++YzZIgNu3r4dywNI22Nm+JZtLj+rX8ARE6FTPlxGBD0SSdXsfCfY2N1ytBBHQRnPsVaHK1p7KOhwQVbqEupcGyvaRolnymOzDLGFdS06OGYFrYXdgIbuqYtZP8QerXtUl0sWNAvvqHSPCQcpKecpMEecar+FUVwLEA+H1wzOprCMbRR+EgIboeDqQ7GxXqugkuFyvnlLDgxnaWhEhQb/5kAcQmnyUZ57MhDcUJqqQ4Cdmwrcxho1P+YqWY9yn0E86F+hl5976a/gH5KBobB84OWmgcX42eAmqpJf+8c8SuBv+7NctbQOk21aYlFEpkwSme/kG1/edtyoHQH/hF0RB1cT8g+u9S9AK2rs3s2G+Ap0U5oyY8pqJalGdZSBudE0sU4mhOV8trtx0FrN9A7pNkTcGPH25nCtyIz6rzR+DP8Mtgw5385s5ivVlDb+z74Wbh6iu7ZkVAogNTpUYU/1BxDXWJqFMkFmfziNxQ5AQqm1vGlBzXifoQkUFX1riutNphmu0Hs+7KMmMLvtW2cXmQDpkHFKVheeN4w7pBCEZ8KhZ0VTOwRZcdvrNcpYfXM13/QdTHQmCqqwgS/VvlUFz7PDn0/OKo6moUic8W6b1iEvd3kfc7QkunxoOUoJr4RwJ+PqCzN6PxQivAFA2tmDPc8qEa1PAdxTeNFoR/6dNQRojouuJq3C1LrbmGf6lQPvKi3KeKHXyjmDr7Tve+al2tcWJVr+1qEM3/XuthoiZbuTDxYUjZ2nf2fhHrmNcfvrfNxSNHVdQPp2R9Rf3eGxlRJsmRpef66VbYhOpmiH4xmq45EWiyBZmYm+tZtjsP51EDMIvdFbVRSGO/hMqURrDSsJXJeot27Iup2s0P2n/6a9k0c4SVvf/WXNN5x9JNvjU97bQNDQRfonJmo9pRYYHl1tSqNIYBK7KsMH+qr1vmiJuhrXUuL/RtOKvE9KXQ8kGoC9oF5rFn21z40ElxG5XRTASg==</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</soap:Body>
</soap:Envelope>
First of all I have never worked with SOAP before so chances I do things wrong has pretty good odds :)
Have found something here, but I need more details https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#aes256-cbc
How are the iv and the key stored in CipherValue in the header?
When sending the XML request to the webservice I get this error
23-08-2018 12:50:02 General exception:Padding is invalid and cannot be removed.
23-08-2018 12:50:02 Stack trace: at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)
at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.Xml.EncryptedXml.DecryptData(EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm)
at SomeClassCore.XmlSecurity.Decryptor.DecryptData(Byte[] symmetricKey)
at SomeClassCore.SecurityServiceImpl.UnwrapRequest(ServiceRequest serviceRequest)
at BD.BCA.MessageHandler.MessageHandler.ProcessRequest(HttpContext context)
Have searched a bit more.. Maybe the iv must be a part of the stored data. But it's still not working? Same error as above
class Encryption {
const AES256_CBC = 'AES-256-CBC';
public function data_encrypt(string $data, string $cipher): Array{
switch($cipher){
case self::AES256_CBC:
$key_length = 32;
$block_length = 16;
break;
}
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
$key = openssl_random_pseudo_bytes($key_length);
$encrypted_data = $iv.openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);
return [
'data' => base64_encode($this->pkcs7_padding($encrypted_data, $block_length)),
'key' => $key
];
}
public function key_encrypt(string $key): string{
$public_cert = openssl_pkey_get_public('contents of public cert');
openssl_public_encrypt($key, $data, $public_cert, OPENSSL_PKCS1_OAEP_PADDING);
openssl_free_key($public_cert);
return base64_encode($data);
}
private function pkcs7_padding(string $data, int $block_length): string{
$pad = $block_length - (strlen($data) % $block_length);
return $data.str_repeat(chr($pad), $pad);
}
}
$Enc = new Encryption;
$data_encrypted = $Enc->data_encrypt('The message I want to encrypt', Encryption::AES256_CBC);
// This base64 encoded string goes to <EncryptedData>
$data_encrypted['data'];
// This base64 encoded string goes to <EncryptedKey> in the header
$Enc->key_encrypt($data_encrypted['key']);
update
Have been in contact with the maintainer of the webservice and OAEP padding is used with the RSA encryption and PKCS7 padding is used with AES chipher..
As I can see this is also what I do?
*TESTED AND WORKING CODE *
I would suggest that you separate the different parts involved. The most likely cause to your problems is the order of execution (i.e. you should do padding before encryption). I am also surprised that there is no signature, but that might not be required in your case. However, I prepared the suggested code for you to test and also added decrypt/decode functions to make testing easier. Good luck.
<?php
class Encryption {
const AES256_CBC = 'AES-256-CBC';
const IV_BYTES = 16;
protected $binary_security_token = null;
protected $private_key = null;
protected $public_key = null;
public function data_encrypt(string $data, string $password): Array {
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(self::IV_BYTES);
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
$encrypted_data = openssl_encrypt($data, self::AES256_CBC, $key, OPENSSL_RAW_DATA, $iv);
$encoded_data = base64_encode($iv . $encrypted_data);
return [
'data' => $encoded_data,
'key' => $key
];
}
public function data_decrypt(string $data, string $password): Array {
$decoded_data = base64_decode($data);
$key = hash('sha256', $password, true);
$iv = substr($decoded_data, 0, self::IV_BYTES);
$encrypted_data = substr($decoded_data, self::IV_BYTES);
$decrypted_data = openssl_decrypt($encrypted_data, self::AES256_CBC, $key, OPENSSL_RAW_DATA, $iv);
$padding = ord($decrypted_data[strlen($decrypted_data) - 1]);
return [
'data' => substr($decrypted_data, 0, -$padding)
];
}
public function key_encrypt(string $key): ?string {
$encoded_data = null;
if ($this->public_key && openssl_public_encrypt($key, $data, $this->public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
$encoded_data = base64_encode($data);
}
// openssl_free_key($this->public_key);
return $encoded_data;
}
public function key_decrypt(string $data): ?string {
$decrypted_data = null;
$decoded_data = base64_decode($data, true);
if ($this->private_key && openssl_private_decrypt($decoded_data, $decrypted, $this->private_key, OPENSSL_PKCS1_OAEP_PADDING)) {
$decrypted_data = $decrypted;
}
// openssl_free_key($decrypted);
return $decrypted_data;
}
public function generate_keys(): void {
$config = [ "private_key_bits" => 2048, "private_key_type" => OPENSSL_KEYTYPE_RSA ];
$resource = openssl_pkey_new($config);
if (openssl_pkey_export($resource, $this->private_key)) {
echo "private_key:\n" . $this->private_key . "\n";
$private_key_file = "private_key.pem";
file_put_contents("private_key.pem" , $this->private_key);
}
$this->public_key = openssl_pkey_get_details($resource);
$this->public_key = $this->public_key["key"];
$this->binary_security_token = preg_replace("#-.+-|[\r\n]| #", "", $this->public_key);
echo "public_key:\n" . $this->public_key . "\n";
file_put_contents("public_key.pem", $this->public_key);
}
public function load_keys(): void {
$private_key_path = realpath(dirname(__FILE__) . "/private_key.pem");
if (!$private_key_path) {
$this->generate_keys();
return;
}
$private_key_contents = file_get_contents($private_key_path);
if (!$private_key_contents) {
$this->generate_keys();
return;
}
$public_key_path = realpath(dirname(__FILE__) . "/public_key.pem");
if (!$public_key_path) {
$this->generate_keys();
return;
}
$public_key_contents = file_get_contents($public_key_path);
if (!$public_key_contents) {
$this->generate_keys();
return;
}
// Signature to see that data is not manipulated, could be performed on an encrypted body. The spec says you only make a signature for what you can see.
// Is it important to "hide data", "detect manipulated data" or both ...
$this->binary_security_token = preg_replace("#-.+-|[\r\n]| #", "", $public_key_contents); // BinarySecurityToken for securityToken in Security header
// ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
// EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
if (openssl_pkey_export($private_key_contents, $this->private_key)) {
echo "private_key:\n" . $this->private_key . "\n";
}
$public_resource = openssl_pkey_get_public($public_key_contents);
if ($public_resource) {
$this->public_key = openssl_pkey_get_details($public_resource);
$this->public_key = $this->public_key["key"];
echo "public_key:\n" . $this->public_key . "\n";
}
}
}
$enc = new Encryption();
$encrypted = $enc->data_encrypt("The message I want to encrypt", "password");
// This base64 encoded string goes to <EncryptedData>
// $encrypted['data']
// Test that data_encrypt / data_decrypt works (from a terminal)
echo "encrypted data:\n" . $encrypted["data"] . "\n";
$decrypted = $enc->data_decrypt($encrypted["data"], "password");
echo "decrypted data:\n" . $decrypted["data"] . "\n";
// This base64 encoded string goes to <EncryptedKey> in the header
// $enc->key_encrypt($encrypted['key']);
if (version_compare(phpversion(), "7.1.0", ">=")) {
$enc->load_keys();
$pwd_hash_pre = bin2hex($encrypted["key"]);
echo "hex key:" . $pwd_hash_pre . "\n";
$encrypted_key = $enc->key_encrypt($encrypted["key"]);
echo "\nencrypted and base64encoded key:" . $encrypted_key . "\n";
$decrypted_key = $enc->key_decrypt($encrypted_key);
$pwd_hash_post = bin2hex($decrypted_key);
echo "\ndecrypted and decoded key:" . $pwd_hash_post . "\n";
$equal_hashes = $pwd_hash_pre === $pwd_hash_post ? 'true' : 'false';
echo "password hashes equal:" . $equal_hashes . "\n";
}
Your error suggest that the API failed to decrypt the openssl AES-256-CBC data.
I think the reason is because in your class you are routing the encryption through your pkcs7_padding() function. I believe that by default, as long as you don't specify OPENSSL_ZERO_PADDING in your openssl_encrypt() function that the padding is pkcs7. The block size for all AES encryption is 128bits or 16 bytes.
So in essence you are padding your already padded encryption. So basically I just removed your pkcs7_padding() from your class.
I did test your public key encryption. I was able to use a 2048b rsa key from 2048b certificate and generate an encrypted public key using a PEM formatted certificate. Whether or not it's padded correctly I have no idea. But the OPENSSL_PKCS1_OAEP_PADDING is probably correct.
My guess is that the RSA encryption worked if the API made it to the AES portion.
As far as how you are assembling the data into the XML I don't have a clue.
But is seems reasonable that in the <xenc:EncryptedKey> tag in the cipher value would be the RSA encrypted key and for the <xenc:EncryptedData> tag the cipher value would be the AES ecrypted data. You just have to figure out how the API is getting the IV.
Read your API docs for how they are expecting the IV to be delivered. I will keep looking too if this does not work for you.
I will research on that later. But for know try without manually padding your encryption. Hope it helps.
Another thing to consider is that in your case example you don't need to use an IV. In your AES encryption you are generating a new key for every encryption and then encrypting the AES key via the RSA public key obtained by your certificate.
If you were using the same AES key I would see a need to implement an IV but in this case I don't. Regardless... We need to know if the API expects an IV and if it does, how it is expected to be sent?
class Encryption {
const AES256_CBC = 'AES-256-CBC';
public function __construct(){
}
public function data_encrypt($data, $cipher){
switch($cipher){
case self::AES256_CBC:
$key_length = 32;
$block_length = 16;
break;
}
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
$key = openssl_random_pseudo_bytes($key_length);
$encrypted_data = $iv . openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);
return [
'data' => base64_encode($encrypted_data),
'key' => $key //Does this need to be encoded?
];
}
//***Important***
//Make sure you certificate is 1.) a x.509 certificate resource, 2.)A file path that leads to a PEM encoded certificate, or 3.) a PEM formatted key.
public function key_encrypt($text){
$keyResource = openssl_pkey_get_public(file_get_contents('path/to/myCert.pem')); //This returns a resource or FALSE.
if(!$keyResource){
echo 'Something wrong with certificate.';
}
openssl_public_encrypt($text, $cipherText, $keyResource, OPENSSL_PKCS1_OAEP_PADDING);
openssl_free_key($keyResource);
return base64_encode($cipherText);
}
}
$Enc = new Encryption;
$cipherText = $Enc->data_encrypt('The message I want to encrypt', Encryption::AES256_CBC);
// This base64 encoded string goes to <EncryptedData>
echo 'AES Data: ' . $cipherText['data'] . '<br><br>';
echo 'AES Key: ' . $cipherText['key'] . '<br><br>';
// This base64 encoded string goes to <EncryptedKey> in the header
$key = $Enc->key_encrypt($cipherText['key']);
echo 'RSA OAEP Padded Key: ' . $key;

php - Encryption/decryption of strings and files using mcrypt

From time to time I come around the task of creating functions for encrypting/decrypting strings and files in PHP.
I decided to finally nail those functions and did some searching but I couldn't find enough resources to confirm the security of these functions.
Please note that I don't want to use another full-blown library unless necessary and I don't see why PHP provides OpenSSL & mcrypt functions but nobody really implements them.
I was able to find these functions but they are not commented and some steps were unclear (also they do not generate a key but use a predefined one).
Following these functions I also found this stackoverflow question but the first answer uses another library while the second one uses ECB.
edit: I updated the code sample which previously utilized mcrypt to using only OpenSSL as suggested in the comments:
function generate_key($cipher = 'AES-256-CBC')
{
return base64_encode(openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher))); // Generate a random key - currently using the function for the vector length
}
function encrypt($data, $key, $cipher = 'AES-256-CBC')
{
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher)); // Generate a random initialization vector
return base64_encode($iv) . '$' . openssl_encrypt($data, $cipher, base64_decode($key), false, $iv); // Return a base64 encoded string containing the iv and the encrypted data
}
function decrypt($data, $key, $cipher = 'AES-256-CBC')
{
$data = explode('$', $data); // Explode the previously encoded string
if(count($data) == 2)
return openssl_decrypt($data[1], $cipher, base64_decode($key), false, base64_decode($data[0])); // Decrypt the data given key and the iv
else
return false;
}
I tested encryption and decryption using these function like this:
$input = 'Hello world!';
echo 'Original data: ' . $input . '<br /><br />';
$key = generate_key();
$encrypted = encrypt($input, $key);
echo 'Key used for encryption: ' . $key . '<br />';
echo 'Encrypted data: ' . $encrypted . '<br /><br />';
$decrypted = decrypt($encrypted, $key);
echo 'Decrypted data: ' . $decrypted . '<br />';
The question: Is OpenSSL properly implemented as shown above? Can they be used for files too?
These are the old functions using mcrypt. Don't use them anymore.
function generate_key($cipher = MCRYPT_RIJNDAEL_256)
{
return bin2hex(openssl_random_pseudo_bytes(mcrypt_get_key_size($cipher, MCRYPT_MODE_CBC))); // Generate a random key using OpenSSL with size given from mcrypt depending on cipher
}
function encrypt($data, $key, $cipher = MCRYPT_RIJNDAEL_256)
{
$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher, MCRYPT_MODE_CBC)); // Generate random initialization vector with size given from mcrypt depending on cipher
return bin2hex($iv) . '$' . bin2hex(mcrypt_encrypt($cipher, pack('H*', $key), $data, MCRYPT_MODE_CBC, $iv)); // Return the initialization vector and encrypted data as ASCII string
}
function decrypt($data, $key, $cipher = MCRYPT_RIJNDAEL_256)
{
$data = explode('$', $data); // Split the input data by $ to retrieve the initialization vector and the encrypted data
if(count($data) == 2) // Check if there are 2 parts after splitting by $
return mcrypt_decrypt($cipher, pack('H*', $key), pack('H*', $data[1]), MCRYPT_MODE_CBC, pack('H*', $data[0])); // Return the decrypted string
else
return false; // Return false if the given data was not properly formatted (no $)
}
Current best practise is to avoid using mcrypt in favor of openssl.
And the speed benchmark, where openssl is pretty much faster.

Unable to decrypt data - openssl_private_decrypt(): key parameter is not a valid private key

<?php
ini_set('display_errors', 1);
$config = array(
"config" => "C:\wamp\bin\apache\Apache2.4.4\conf\openssl.cnf",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
// Create the private and public key
$res = openssl_pkey_new($config);
if ($res === false) die('Failed to generate key pair.'."\n");
if (!openssl_pkey_export($res, $privKey, "phrase", $config)) die('Failed to retrieve private key.'."\n");
// Extract the private key from $res to $privKey
openssl_pkey_export($res, $privKey, "phrase", $config);
echo "<br/>";
echo "Private Key = ".$privKey;
echo "<br/>";
// Extract the public key from $res to $pubKey
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
echo "<br/>";
echo "Public Key = ".$pubKey;
echo "<br/>";
$data = 'plaintext data goes here';
// Encrypt the data to $encrypted using the public key
openssl_public_encrypt($data, $encrypted, $pubKey);
echo "<br/>";
echo "Encrypted Data = ".$encrypted;
echo "<br/>";
// Decrypt the data using the private key and store the results in $decrypted
openssl_private_decrypt($encrypted, $decrypted, $privKey);
echo "<br/>";
echo "Decrypted Data = ".$decrypted;
echo "<br/>";
?>
LOGS
Private Key = -----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIr2acPfh8YYQCAggA
MBQGCCqGSIb3DQMHBAiCvohdiWAZ4QSCBMjKJUXF5ShKfW3TazpKYTxEV8JmGYLf
AJWXzxdi0GrDuddz4aW1FeGwvUm2t/41CTxFsWtgoQJrzCgAQETn54majdrDeF4u
zCmvFMKSoVP4xsZKke15e1K1LPmFNNuKKyCqMwL+tpQJ7zquvDTKHapUnNzfNXpZ
D2K1r2qZWeDN1d36DA9wkN5GbpZYAjuHqHUNzorhxIbHGp2WOg8YKdemoTuKIqYC
DUKncWtxRUOx6IIZuey+uTBzH7Bn9K9a71QTjUdeWgQZFzy9yVpetB+XrJA92IWt
vMeKXCXNhOgkOvkUPNXSuMOVrECNcbKDAKxmK3EQWqb+8zlYFqjmaL/sCep8ihio
1ZWpRaOd5HxnG5rpmz/BYzcF354mM8B4wAIk7MmFq/pHSKLjpr+2Ef1BpMmXfRpG
Pj1jYDClSIQF6ovKOqhevFwfYrtl2jEOISyAggm/sbD750VBkwhbVAyQcarckAiI
GlNcQPOC+JYZOV7o/9o+Tg24zwtAQ8y3hNvYyHjqYI5naVS9yuXEqB6zYoGivs/k
bIblqTFebLEFtihjsa9wpXkyNzKD2NvdSa2oNC7IkCNi8TRNjy7MLylSmCIdhWAV
YgL1hxShMgbnfiGjFQyYnKzZto9RqRlQBIoBOCfwP1EFnZjCJm02CCeGR+GHKXf7
rJ0n6lIUEvVnENirAPtOuiE2ccbzmyjWQ9f2vwBSUea5nPTMG4uTVHrQjrgNYIyU
+vLV6tL+MDKF4JGQGgzBUeqTMobmrOK+V20QIasYaAWHJrL8itBwZ++C8lo7kySa SImMXakI4rjgEmj+HmUJygT1EZWz5yQqOiwAYLhQZg+m6+32Pvt6mIrAXbznrdHP
JxHb/9HV88mQdRKPBTkSTl71Ics+3oybYPbhSQByXOdtsw6VLYNo4ikgj3tXCz01
DwVQqeQ5tLD4LY8/QaAHkOUq9K24yfkcN+aQh7cvR/HX53Ls6LsdUwkwSOWVj2na
Wl4xn+j3ZaPhpgdzcMgknU7BAI2kZP83MxyKnNcnneyX4hTaM5PRZJXKd+onvhff
nQ3zHDSYmRDKmTXBjCob3Vjg91LcMjg9dEH7aIFWit5dHK4ll/v1IiOFx8d4d/mV
Oll0c0ujJuPjtyqesM2Bz3Ah5YkIT2Z7kxvRy7rTyytQG7hLNENAki9wW79fcEo7
ln/OvEpjdWZngkL/UrMOX8DBrs0PLEH9jyDoCQx/LSqxMAXOwVXILfsfsUFu0M0o
21YbeC33jOlocJ4Q6pwfRVz8lCQOuIVs1jEpvSmvHgvmHmXUI4Y6nZD4Roi0jIjS
VvI73eULzc3j0jIptWxzrHWM6iHx1zRxkLMJSZOx0A27ngtSo7g6+aJnMO5FDfdR
90vnr+bX4ki+X/N4wVF7eppyapLe/tQ54vAxsyIBrCXPjwBMehiFjOMhzSLW7xQj
Qg2KcilfW6oKFzDQQ5nKPEXvQYMhQ1MeWKyNv6BMoc4EEpIGhtziUXWhgT4sN1ES
5sxVcGVoIe1viO/kk3Zq55hETlZbNWs3V511BcEZCiQNrntnbYv6pwKpoB21ZV2E
slVhYcslEGliIQKQsWSl5cfc+pqjLteiPrwk14WKJGXl9zX3YH6H7KKB/7SIRZk7 wq8=
-----END ENCRYPTED PRIVATE KEY-----
Public Key = -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAriS8qflAjYSYhH2qgC5T
yf98X1qoLMXIW9mMkhV8LcApBKOfNjUMc9xjD3a8CR/LYwa4MYhevoKcVfPG8XoE
sDGyHh+h/vtYP0rORB1T3RULVUzDLjX558e2KqPrSN+rV+Jl1NB0SO5Of3JA+AKa
0Q3botcjOM3WuFa/s+RzsiCrMMhzOZSTBj+GTP/VcDipF5PM7+/Lxr+edjRXccT2
WQjsq0sUrtsmpzBE8Niph361RjfIisxKoksQGs7hC/Iv4yhBzZZIpRaZuvDj4ImP
+4sUQgbdVVCso122kg34UtY5qchuSCcJfsGbD2zMw+8ZftIsJ7dfX1FxujgggDyn 0wIDAQAB -----END PUBLIC KEY-----
Encrypted Data = Uš6/ùÅËæÝmL4²G¾'gr¨Ñ­Ä‰ï‚zêbÀ)[îR0s‹yÝ`t™õ°Þe­Ïd>×o¯rß9ÌÔÅAü!-†D·¨ÎVZ¼?¶éžäýöaØT~=‚Fan¢ºq{M”ƒ¹Cû5N3¹.Ð(·#*ÏRƹñß÷õƒ_ò9c-Ÿ% ×óè2Ꙃõ“ÂÐgNÈ-ˆd«…ºt§¼Ô}yŠ"7èPš(¶R¤ßJÚ_h¶ðÞK(Cj“7‘Y ÀŠþrôZƒ4)JU•˜„üˆ k0â§Êë^ÚºGÚªúVKø†ë8ÏLÚó „Ÿ¦¿¤
( ! ) Warning: openssl_private_decrypt(): key parameter is not a valid private key in C:\wamp\www\android\pki_example.php on line 41
Call Stack
# Time Memory Function Location
1 0.0020 252696 {main}( ) ..\pki_example.php:0
2 0.2043 258032 openssl_private_decrypt ( ) ..\pki_example.php:41
Decrypted Data =
// Decrypt the data using the private key and store the results in $decrypted
openssl_private_decrypt($encrypted, $decrypted, openssl_pkey_get_private($privKey, "phrase"));
echo "<br/>";
echo "Decrypted Data = ".$decrypted;
echo "<br/>";
openssl_private_decrypt function is capable to use PEM formatted private key but your key is encrypted and this function does not have an argument for password. You have to use openssl_pkey_get_private instead.
Don't know what is the reason. I think someone can edit this with exact reason.
When I use private key in one line, it gives the above error mentioned in this question.
But when I use the same private key by splitting it into several lines (64 characters per line) it works. In this way no need to use openssl_pkey_get_private($privKey, "phrase") function. Actually I had no pass phrase for my private key.
Please use like below code
// Create the keypair
$privatefile = "data/privatekey.pem";
$publicfile = "data/publickey.pem";
$cleartextfile = "data/cleartext";
$res = openssl_pkey_new();
if (!$res) {
echo "\nopenssl_pkey_new: " . openssl_error_string();
}
// Get private key
openssl_pkey_export($res, $privatekey);
if (!$privatekey) {
echo "\nopenssl_pkey_export: " . openssl_error_string();
}
file_put_contents($privatefile, $privatekey);
// Get public key
$pkeydetails = openssl_pkey_get_details($res);
if (!$pkeydetails) {
echo "\nopenssl_pkey_get_details: " . openssl_error_string();
}
$publickey = $pkeydetails["key"];
file_put_contents($publicfile, $publickey);
// For getting Decrypted password
$postedPassord= $_POST['password'];
openssl_private_decrypt(base64_decode($postedPassord),
$decryptPassword, $privatekey);

PHP OpenSSL Public Key Encryption With String Public Key

I have a public key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwKMDEDjbP5v/9kcvpQKf
IG3nU5Yid/tUNIeXBSDlxqhTEOKs8iQHXk0T17C4g7KHmrT2hxUomaAa2wwbfL+Z
4ppqvZZ4cu7CO6jaA0HyoBCU96siSuE0mPt8kU/PRA9+nAwu9lu9oYZUiVVJ3D4f
o2bc+jWWL4GGY+PdSlz81ZW5cW/LOmNs9D0jJIxbwNocHxGgJ+xTZ3JKp6AO4MvL
zXyipXu562N8wVc7UIgYYnvr63zFU8vzRL180X5x5MiJbjTYbfLH3z7qINPMZZLv
A5vzJ0HX3J8rG96tmFuUzopCnvf+WVLvaS2T5uMxieK9dfA32CFQp4i3cj39c2b7
NwIDAQAB
-----END PUBLIC KEY-----
stored as a base64 encoded string (in MySQL, set as $row['public_key'])
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3S01ERURqYlA1di85a2N2cFFLZgpJRzNuVTVZaWQvdFVOSWVYQlNEbHhxaFRFT0tzOGlRSFhrMFQxN0M0ZzdLSG1yVDJoeFVvbWFBYTJ3d2JmTCtaCjRwcHF2Wlo0Y3U3Q082amFBMEh5b0JDVTk2c2lTdUUwbVB0OGtVL1BSQTkrbkF3dTlsdTlvWVpVaVZWSjNENGYKbzJiYytqV1dMNEdHWStQZFNsejgxWlc1Y1cvTE9tTnM5RDBqSkl4YndOb2NIeEdnSit4VFozSktwNkFPNE12TAp6WHlpcFh1NTYyTjh3VmM3VUlnWVludnI2M3pGVTh2elJMMTgwWDV4NU1pSmJqVFliZkxIM3o3cUlOUE1aWkx2CkE1dnpKMEhYM0o4ckc5NnRtRnVVem9wQ252ZitXVkx2YVMyVDV1TXhpZUs5ZGZBMzJDRlFwNGkzY2ozOWMyYjcKTndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
in PHP. I'm trying to use this string to encrypt string data, but it just fails without telling me why when I use the following code:
$success = openssl_public_encrypt($data, $encrypted, base64_decode($row['public_key']));
When the above is run, $success is always false and $encrypted is blank. I've tried running the public key through openssl_pkey_get_public() and sending the resource to openssl_public_encrypt() with no luck either. Writing to a file is not an option, nor is storing the private key.
Does anyone know how to get this to work? (or does it work for everyone else and it's just a weird server issue?)
EDIT: Since there seems to be some confusion about my issue, here is the testing code I'm using and its output. (Note: I've removed the base64 encoding, just to see if that made any difference)
$pubkey = openssl_get_publickey($row['public_key']);
$publicKey = openssl_pkey_get_details($res);
$encrypted = null;
$success = openssl_public_encrypt($data, $encrypted, $pubkey);
print "\npubkey1 " . $row['public_key'];
print "\npubkey2 " . $pubkey;
print "\npubkey3 " . $publicKey;
print "\npubkey4 " . $publicKey["key"];
print "\nencryption " . ($success ? 'true' : 'false') . ' "' . $encrypted . '"';
$success = openssl_public_encrypt($data, $encrypted, $row['public_key']);
print "\nencryption2 " . ($success ? 'true' : 'false') . ' "' . $encrypted . '"';
Output:
pubkey1 -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA89FgfElm36q7iAf8frqa
o58naoROsAljaFbmztfnqlmzXfyijK5CNJFdkUCgsu2zGxN7UlGiBOassTd4ijWm
1rz6/ad9fGXplfMGxZxyPCz31VreSWXmTG/PeSIYs1Co+dibV3imYt5jTxfLs7BZ
WsT8nuLxGPw/o/gyKut0Ru+jXI2GgT4s3SylXinn/IbIA497SohqYA7/ViQnBwSL
ZKUysOx2QgBmc9m0viRqDSKNUtDw7+L7bjhlwgZUGr6fxfTuNj9PWo97aPSE74CD
owYYl2ToTboKSjZUszeNwQKpUnlHY/DBkwmYUJ7SAYDY70VNooadN5dZ4ehjdaka
6QIDAQAB
-----END PUBLIC KEY-----
pubkey2 Resource id #50
pubkey3
pubkey4
encryption false ""
encryption2 false ""
The key that you should pass to this function is a PHP resource object, and not the string representation of the key itself.
$pubkey = openssl_get_publickey(base64_decode($row['public_key']));
$success = openssl_public_encrypt($data, $encrypted, $pubkey);
So the issue actually relates to the data. PKI for php has a really small length restriction and can't be used to encrypt large amounts of data. Instead you're supposed to use PKI to encrypt a symmetric key which is then used to encrypt/decrypt data.
See more # How to encrypt long strings in PHP?

php encrypt and decrypt

can anyone tell me how to encrypt and decrypt a URL string ?
I want to encrypt a hyperlink ...
If you can use database,you could create a table to map a file to an id.
Create a 'mapping_table'
id - integer
file_location - string
Your URL would look something like localhost/waterwell/e_book.php?id=12 .
make links that return to your server with querystring GET params identifying the file. the server can then do echo file_get_contents() after you figure out which file from the inputs
In your example it's trivial. simply omit the portion of the url you don't want shown and fill it back in on the server.
$confirmpassword = $_POST['confirmpassword'];
$value_check = true;
$ciphering = "AES-128-CTR";
$options = 0;
$encryption_iv = '1234567891011121';
$encryption_key = "GeeksforGeeks";
$confirmpasswordencryption = openssl_encrypt($confirmpassword, $ciphering,$encryption_key, $options, $encryption_iv);
$encryption = "pABqPJhobIMHzqai"
$ciphering = "AES-128-CTR";
$options = 0;
$decryption_iv = '1234567891011121';
// Store the decryption key
$decryption_key = "GeeksforGeeks";
// Use openssl_decrypt() function to decrypt the data
$decryption=openssl_decrypt ($encryption, $ciphering,
$decryption_key, $options, $decryption_iv);
// Display the decrypted string
echo "Decrypted String: " . $decryption;

Categories