I'm using the openssl_encrypt / decrypt method in my website but i'm having some troubles with the $tag option
openssl_encrypt ( $data, $method, $key, $options, $iv, $tag )
openssl_decrypt ( $data, $method, $key, $options, $iv, $tag )
from http://php.net/manual/en/function.openssl-encrypt.php, the definition of tag is: The authentication tag passed by reference when using AEAD cipher mode (GCM or CCM). But i didn't understand it.
I tried it in my codes
$data = "text to be encrypted";
$cipher = "aes-128-gcm";
$key = "0123456789abcdefghijklmnob123456";
$option = 0;
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
if (in_array($cipher, openssl_get_cipher_methods())){
$encryptedData = openssl_encrypt($data,$cipher,$key,$option,$iv,$tag);
echo $encryptedData;
$decryptedData = openssl_decrypt($encryptedData,$cipher,$key,$option,$iv,$tag);
echo $decryptedData;
}
i got this result:
encrypted text: Vlx/yKkPhg0DpD0YKvnFKRiCh/I=
decrypted text: text to be encrypted
which is correct. but if i directly decrypt the encrypted text this way:
$data = "text to be encrypted";
$cipher = "aes-128-gcm";
$key = "0123456789abcdefghijklmnob123456";
$option = 0;
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
if (in_array($cipher, openssl_get_cipher_methods())){
$encryptedData = "Vlx/yKkPhg0DpD0YKvnFKRiCh/I=";
$decryptedData = openssl_decrypt($encryptedData,$cipher,$key,$option,$iv,$tag);
echo $decryptedData;
}
i'm getting:
Notice: Undefined variable: tag
if someone could explain to me why this is happening and what should be the value of $tags. thanks
The tag that PHP is complaining about is an essential aspect of AES when using GCM mode of operation. In this mode, not only does the AES block cipher get applied, but an authentication tag gets calculated as well. It is an array of bytes that represents a MAC (Message Authentication Code) that can be used to verify the integrity of the data and wen decrypting. That same tag needs to be provided to do that verification. See the Wikipedia page about Galois/Counter Mode for more details.
So in order to successfully decrypt that ciphertext, you need to capture the $tag variable resulting from the openssl_encrypt() invocation and feed it into the openssl_decrypt() invocation. You did not do that, hence the complaint about the missing tag. Note that the tag (typically) contains non-readable characters so it is more convenient to store it in a base64 encoded format.
In addition to the $tag variable, you should also provide the same value for the $iv variable to the openssl_decrypt() method as you used in the openssl_encrypt() invocation. Again, base64 encoding makes that easier.
A quick test below demonstrates all this, where I first modified your script to print more stuff and then used the provided script to decrypt:
$ php test1.php
iv base64-ed: vBKbi8c6vCyvWonV
plaintext: text to be encrypted
ciphertext base64-ed: z28spOd3UEDmj+3a8n/WK11ls7w=
GCM tag base64-ed: OIAggQCGUbPgmPN6lFjQ8g==
$ php test2.php
decrypted ciphertext: text to be encrypted
where the code for test2.php is the following:
$cipher = "aes-128-gcm";
$key = "0123456789abcdefghijklmnob123456";
$option = 0;
$iv = base64_decode("vBKbi8c6vCyvWonV");
if (in_array($cipher, openssl_get_cipher_methods())){
$encryptedData = "z28spOd3UEDmj+3a8n/WK11ls7w=";
$tag = base64_decode("OIAggQCGUbPgmPN6lFjQ8g==");
$decryptedData = openssl_decrypt($encryptedData,$cipher,$key,$option,$iv,$tag);
echo("decrypted ciphertext: ".$decryptedData."\n");
}
Related
I am trying to encrypt the payload with the AES encryption like below for SSG-WSG API. But I keep getting
Failed to parse JSON request content
I think something is wrong with my way of encryption. I am doing this in PHP.
<pre>
$cipher = "aes-256-cbc";
$ekey = "encryption key provided to SSG"
//Generate a 256-bit encryption key
$encryption_key = $ekey;
// Generate an initialization vector
$iv_size = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($iv_size);
//Data to encrypt
$data = $f; // payload in f
$encrypted_data = openssl_encrypt($data, $cipher, $encryption_key, 0, $iv);
$x = base64_encode($encrypted_data);
</pre>
Where is the SSGAPIInitVector initialization vector to be used, and how?
Thanks
Based on the openssl_encrypt library documentation, the 5th paramenter $iv is for the initialisation vector value. So, I believe you have to replace what you are doing which randomly generates a random Initialisation Vector to the fixed value provided.
I was using "mcrypt-*" for decoding the response in previous PHP 5.6 version but now in PHP 7.2 version as it is deprecated I am using openSSL method. But it is not working properly hopefully I am missing something.
$value="###lllljG5ZOibDGtlL gcQLAtTQUnCJ/bE2glWsL1WKVPdC22c9GtGe/Npx9Uv9IYaszOAVXB4T9s7Hsss/2XpZ9oisx5M4jeV7RK2S/JrBt2E4GEcDGwuJs6NhkKV8hdOcU tmkJLxO3OJ OgVbqrT6a4v5RE7w eP zvQwZyAR5cYCKUYomou9mL/pvfLbe RrBe5ZnMQmUrD6cwUxEE/inikMvIb4K7HI fVPid N B3iPnIYQna6/v9W5A0kslBj6BBDjVXJabwmCSDVxbArm0GDNseWoQAEa4BMxYitqP6cVTxL5Kri8xbAKCW5/unnYnudkHQjNJWW7LuiwDxsBqwQv8D/R/Ff/joFW6q0 muI16/CfIoFnYAyAJWNlKCX9";
$value = urldecode($value);
$value = str_replace(" ", "+", $value);
$abc = triple_decrypt($value);
print_r($abc);
PHP 5.6 working fine
function triple_decrypt($input){
$key = "thisis87658748639testkey";
$input = base64_decode($input);
$td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, "");
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$pwd = trim(mdecrypt_generic($td, $input), "\x00..\x0F");
mcrypt_generic_end($td);
return $pwd;
}
PHP 7.2
function triple_decrypt($input){
$key = "thisis87658748639testkey";
$cipher = "des-ede3";
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$pwd = openssl_decrypt($input, $cipher, $key, $options=0, $iv);
return $pwd;
}
openssl uses PKCS7-padding and mcrypt Zero-padding [0][1][2]. To decrypt the ciphertext with openssl, openssl's padding must be disabled and mcrypt's Zero-padding bytes must be removed:
function triple_decrypt($input){
$key = "thisis87658748639testkey";
$cipher = "des-ede3";
$decrypted = openssl_decrypt($input, $cipher, $key, $options=OPENSSL_ZERO_PADDING); // Disable openssl's PKCS7-padding
$unpadded = trim($decrypted, "\x00..\x0F"); // Remove mcrypt's Zero-padding bytes
return $unpadded;
}
However, note the following with regard to a reimplementation of encryption and decryption: ECB is an insecure mode [3]. Instead, CBC or even better GCM should be used [4][5]. Instead of Triple-DES the modern and faster todays standard AES is recommended [6]. Zero-padding is unreliable, PKCS7-padding should be applied instead.
Furthermore, the mcrypt code is to some extent inconsistent:
The ECB mode doesn't use an IV (this is also the reason why openssl_cipher_iv_length returns 0 in the openssl code [7]). mcrypt_generic_init ignores the IV in case of the ECB mode [8], so it's not used in the mcrypt code and therefore not needed in the openssl code.
And if a mode would be used that requires an IV, then the following would have to be considered: The IV is always needed for encryption and decryption. Therefore, a random IV is generated (and used) during encryption and then passed on to the recipient together with the ciphertext, where it's used for decryption. Since the IV isn't secret, it's usually prefixed to the ciphertext. The generation of a random IV during decryption therefore makes no sense.
You can do using openssl()
function encryptIt($q) {
$cryptKey = 'YourProjectname'; //any string
$encryptionMethod = "AES-256-CBC";
$secretHash = "25c6c7rr35b9979b151f0205cd13b0vv"; // any hash
//To encrypt
$qEncoded = openssl_encrypt($q, $encryptionMethod, $secretHash);
return $qEncoded;
}
function decryptIt($q) {
$cryptKey = 'YourProjectname'; //any string
$encryptionMethod = "AES-256-CBC";
$secretHash = "25c6c7rr35b9979b151f0205cd13b0vv"; // any hash
//To Decrypt
$qDecoded = openssl_decrypt($q, $encryptionMethod, $secretHash);
return $qDecoded;
}
$encryptedstring = encryptIt('TEST');
echo "<br/>";
echo decryptIt($encryptedstring);
I use the following PHP code to decrypt my AES 128 string.
function aes128_cbc_encrypt($key, $data, $iv) {
if(16 !== strlen($key)) $key = hash('MD5', $key, true);
if(16 !== strlen($iv)) $iv = hash('MD5', $iv, true);
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
}
$password = "mypasswordonthefirstrunoftheprogram";
$salt = "mysalthereitisfinallyitturnedoutok";
$encrypted = "BxGi119ltnYVNikXSP8jMJtSNIDKoMsPfd/nUEwlgSviVRM50/UgMF36j6Cqe+I/";
echo aes128_cbc_decrypt($key, $encrypted, $iv);
RIGHT RESULT =
this is my test sentence
RESULT RETURNED =
º±h©MM®StOfthis is my test sentenceok
Furthermore I decoded the string in C# with the right key and IV succesfully, so there is no error in that.
I wonder how this come ? I did the right padding and also tried the some other methods in php but all returning garbage in front of the right answer.
$encrypted length is not correct for encrypting "this is my test sentence".
The plain text is 26 characters
The padding would be 6-bytes
The string encrypted would be 32-bytes
The encrypted data would be 32-bytes
The Base64 encrypted data would be 44-bytes
The provided Base64 encrypted data is be 64-bytes
The provided encrypted data is be 48-bytes
There are an extra 16 bytes in the encrypted data
Examing the encrypted data there are an additional 16 bytes prepended to the encrypted bata prior to Base64 encoding.
Additionally the padding is not being removed from the decrypted data.
According to the documentation of OpenSSL ( https://www.openssl.org/docs/apps/enc.html#OPTIONS ) they expect a hex-digit value for key and iv; does that mean only numbers? or will a md5 hash do? (Because a md5 doesn't seem reversible)
Note i'm mentioning key and iv because $password in the PHP function openssl_encrypt is actually the key.
(Almost) straight from PHP comments ( http://php.net/manual/en/function.openssl-encrypt.php )
function strtohex($x)
{
$s='';
foreach (str_split($x) as $c) $s.=sprintf("%02X",ord($c));
return($s);
}
$source = 'It works !';
$iv = substr( md5( "123sdfsdf4567812345678" ), 0, 16 );
$pass = '1234567812345678';
$method = 'aes-256-cbc';
echo "\niv in hex to use: ".$iv;
echo "\nkey in hex to use: ".strtohex($pass);
echo "\n";
file_put_contents ('./file.encrypted',openssl_encrypt ($source, $method, $pass, true, $iv));
$exec = "openssl enc -".$method." -d -in file.encrypted -nosalt -nopad -K ".strtohex($pass)." -iv ".$iv;
echo 'executing: '.$exec."\n\n";
echo exec ($exec);
echo "\n";
Your first link is about the command-line tools, not the PHP functions. You'd have a hard time throwing binary data in a terminal, hence why the key there has to be hex-encoded.
In PHP however, openssl_encrypt() and openssl_decrypt() expect a raw binary string.
The documentation is also misleading in that it mentions a 'password' instead of 'key'. You've noticed that, but an encryption key is not something that you should just type in via your keyboard and md5()-ing anything is also never the answer for an encryption key.
The key has to be randomly generated via openssl_random_pseudo_bytes() (or at least that's the most convenient way for your case):
$key = openssl_random_pseudo_bytes(32);
(the same goes for IVs as well)
If you need to hex-encode the resulting $key, just pass it to bin2hex(), but the example that you gave is a bit broken ... you're doing double encryption. Encrypting the file contents via PHP is enough, you don't need to deal with the command line.
Please note that my answer is far from the whole story about doing encryption. You should also add authentication, proper padding, think carefully of how to manage & store your keys, etc.
If you want to learn about it, here's a fairly short, but still descriptive blog post that gives the right answers to key points that you should cover: http://timoh6.github.io/2014/06/16/PHP-data-encryption-cheatsheet.html
If what you need is to simply get the job done - use a popular encryption library, don't write your own.
It took me some time to work with the openssl documentation. Finally I had the solution to return encoded and decoded as ASCII text with base64_encode():
//Return encrypted string
public function stringEncrypt ($plainText, $cryptKey = '7R7zX2Urc7qvjhkr') {
$length = 8;
$cstrong = true;
$cipher = 'aes-128-cbc';
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt(
$plainText, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $cryptKey, $as_binary=true);
$encodedText = base64_encode( $iv.$hmac.$ciphertext_raw );
}
return $encodedText;
}
//Return decrypted string
public function stringDecrypt ($encodedText, $cryptKey = '7R7zX2Urc7qvjhkr') {
$c = base64_decode($encodedText);
$cipher = 'aes-128-cbc';
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ivlenSha2len = $ivlen+$sha2len;
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$plainText = openssl_decrypt(
$ciphertext_raw, $cipher, $cryptKey, $options=OPENSSL_RAW_DATA, $iv);
}
return $plainText;
}
I need to communicate with a asp platform that uses the aspEncrypt from persits.
Can anyone provide an example how to decode a string with PHP and mcrypt that was created via the aspEncrypt routines.
An example page of aspEncrypt is available at this link:
http://support.persits.com/encrypt/demo_text.asp
So if I use the text "Test" and the key "test" it provides an base64 encoded string. I need a php example that convert this encoded string back to the text "Test" with usage of key "test".
This is how i finally solved it:
Expectation:
Key is known
IV is known (in my case, first 32 characters of encoded data)
Encrypted Text is known
In my special case all received data hex encoded.
This means IV and encrypted text.
function decrypt($sString, $sIv, $sKey, $iCipherAlg) {
$sDecrypted = mcrypt_decrypt($iCipherAlg, $sKey, $sString, MCRYPT_MODE_CBC, $sIv);
return trim($sDecrypted);
}
function hex2bin($sData) {
$iLen = strlen($sData);
$sNewData = '';
for($iCount=0;$iCount<$iLen;$iCount+=2) {
$sNewData .= pack("C",hexdec(substr($sData,$iCount,2)));
}
return $sNewData;
}
$sKey = 'this is my key';
// first 32 chars are IV
$sIv = hex2bin(substr($sEncodedData, 0, 32));
$sEncodedData = substr($sEncodedData, 32);
$sEncodedRaw = hex2bin($sEncodedData);
$sDecrypted = decrypt($sEncodedRaw, $sIv, $sKey, MCRYPT_RIJNDAEL_128);
A corresponding encryption works like that:
$sIv = mcrypt_create_iv(mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$sKey = 'this is my key';
$sContent = 'a lot of content';
$sEncrypted = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $sKey, $sContent, MCRYPT_MODE_CBC, $sIv));
$sFullEncodedText = bin2hex($sIv) . $sEncrypted;
I encountered an old VBScript project which was encrypting strings with AspEncrypt like this:
Function EncryptString(data, base64Iv)
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContextEx("Microsoft Enhanced RSA and AES Cryptographic Provider", "", True)
Set Key = Context.GenerateKeyFromPassword("secret encryption password", calgSHA512, calgAES256)
Set IVblob = CM.CreateBlob
IVblob.Base64 = base64Iv
Key.SetIV IVblob
Set Blob = Key.EncryptText(data)
EncryptString = Blob.Base64 & ":" & base64Iv
End Function
Based on the arguments to GenerateKeyFromPassword, a binary key is created by hashing the password with SHA-512, and data is encrypted with the aes-256-cbc algorithm. The random Base64-encoded initialization vector is appended to the encrypted value after a colon.
This can be replicated in PHP using the OpenSSL extension:
class Aes256Cbc
{
private string $algo = 'aes-256-cbc';
private string $key;
private int $ivLen;
public function __construct(string $password)
{
$this->key = hash('sha512', $password, true);
$this->ivLen = openssl_cipher_iv_length($this->algo);
}
public function encrypt(string $data): string
{
$iv = random_bytes($this->ivLen);
$ciphertext = openssl_encrypt($data, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
return base64_encode($ciphertext) . ':' . base64_encode($iv);
}
public function decrypt(string $encrypted): string
{
[$ctPart, $ivPart] = explode(':', $encrypted);
$iv = base64_decode($ivPart);
$ciphertext = base64_decode($ctPart);
return openssl_decrypt($ciphertext, $this->algo, $this->key, OPENSSL_RAW_DATA, $iv);
}
}
Example usage:
$aes = new Aes256Cbc("secret encryption password");
$decrypted = $aes->decrypt($someValue);
Note: if AspEncrypt was used without setting an initialization vector, the IV will be sequence of null bytes. This fixed IV could be generated in the above PHP class as follows:
$iv = str_repeat("\0", $this->ivLen);
It depends on which cipher it uses, take a look at mcrypt as long as you know the cipher and key it should be easy to decrypt.
If you know the cipher and mode used by the encryption, the function mcrypt_decrypt can decrypt it.
http://uk3.php.net/manual/en/function.mcrypt-decrypt.php