Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I wondered whether anyone can help,
I am using encryption method aes-256-gcm, I receive encrypt data, but cannot decrypt.
Below is my code and the issue encountered, can anyone see where I'm going wrong.
Fatal error: Uncaught Exception: OpenSSL error: in /var/www/vhosts/mvisatest.weblogy.net/httpdocs/visad/pushpayment/transactions/receive/p2m/index.php:106 Stack trace: #0 {main} thrown in /var/www/vhosts/mvisatest.weblogy.net/httpdocs/visad/pushpayment/transactions/receive/p2m/index.php on line 106
//$secret_key = '-1FwMVo7r{}B17iqly51zM3zvOAh0/r9636-NOXS'; //Shared Secret
//$secret_iv = 'f0f0b6f6-972e-d338-56a5-1ffbccc77001'; //Key ID
$textToDecrypt = "eyJhbGciOiJBMjU2R0NNS1ciLCJpdiI6IjFrQ1dpTXZKdFNIX3c1ZEwiLCJ0YWciOiJjT3k2TVhTenhaT3lCYi1WdXBfY1lnIiwiZW5jIjoiQTI1NkdDTSIsInR5cCI6IkpPU0UiLCJraWQiOiJmMGYwYjZmNi05NzJlLWQzMzgtNTZhNS0xZmZiY2NjNzcwMDEiLCJjaGFubmVsU2VjdXJpdHlDb250ZXh0IjoiU0hBUkVEX1NFQ1JFVCIsImlhdCI6IjE1ODc1Nzg4OTkifQ.O0y-pu5knc9IUAfAENoMwT_3LwW-oZzzItVxQD8UhHk.uFmZHB7BU9JlKRWC.7ZoJ0oTI8MWXIr4GtzziTw.6qdmvDbnZdi6zf34nhcnoQ";
$encrypted = base64_decode($textToDecrypt);
$password = "-1FwMVo7r{}B17iqly51zM3zvOAh0/r9636-NOXS";
$key = substr(hash('sha256', $password, true), 0, 32);
//$key = hash('sha256', $password, true);
$cipher = "aes-256-gcm";
$iv_len = openssl_cipher_iv_length($cipher);
$tag_length = 16;
//$iv = substr($encrypted, 0, $iv_len);
//$iv = openssl_random_pseudo_bytes($iv_len);
$iv = random_bytes($iv_len);
//$tag = substr($encrypted, $iv_len, $tag_length);
//$ciphertext = substr($encrypted, $iv_len + $tag_length);
$ciphertext = substr($encrypted, $iv_len);
//$decrypted = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag); //php 7
$decrypted = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_RAW_DATA, $iv); //php < 7
$data = 4957030013948007;
if (false === $decrypted) {
throw new Exception(sprintf(
"OpenSSL error: %s", openssl_error_string()
));
}
printf ("Decryption %s\n", $data === $decrypted ? 'Ok' : 'Failed');
The data are encrypted with JWE as specified in RFC 7516. They consist of five Base64url encoded parts, each separated by a dot. The first part is the Base64url encoded header, which is Base64url decoded:
{"alg":"A256GCMKW","iv":"1kCWiMvJtSH_w5dL","tag":"cOy6MXSzxZOyBb-Vup_cYg","enc":"A256GCM","typ":"JOSE","kid":"f0f0b6f6-972e-d338-56a5-1ffbccc77001","channelSecurityContext":"SHARED_SECRET","iat":"1587578899"}
alg describes the algorithm used to encrypt the key and is A256GCMKW, which means Key wrapping with AES GCM using 256-bit key. iv and tag contain the IV and tag, respectively.
The second part is the Base64url encoded ciphertext, which contains the encrypted key and which must be decrypted in the first step. As in the posted code, the key for this decryption is derived from the password. The following code shows this first step:
// base64url_decode from: https://www.php.net/manual/de/function.base64-encode.php#121767
function base64url_decode( $data ){
return base64_decode(strtr( $data, '-_', '+/') . str_repeat('=', 3 - ( 3 + strlen( $data )) % 4 ));
}
$textToDecrypt = "eyJhbGciOiJBMjU2R0NNS1ciLCJpdiI6IjFrQ1dpTXZKdFNIX3c1ZEwiLCJ0YWciOiJjT3k2TVhTenhaT3lCYi1WdXBfY1lnIiwiZW5jIjoiQTI1NkdDTSIsInR5cCI6IkpPU0UiLCJraWQiOiJmMGYwYjZmNi05NzJlLWQzMzgtNTZhNS0xZmZiY2NjNzcwMDEiLCJjaGFubmVsU2VjdXJpdHlDb250ZXh0IjoiU0hBUkVEX1NFQ1JFVCIsImlhdCI6IjE1ODc1Nzg4OTkifQ.O0y-pu5knc9IUAfAENoMwT_3LwW-oZzzItVxQD8UhHk.uFmZHB7BU9JlKRWC.7ZoJ0oTI8MWXIr4GtzziTw.6qdmvDbnZdi6zf34nhcnoQ";
$parts = explode(".",$textToDecrypt);
// Header, IV, tag
$headerB64 = $parts[0];
$header = base64url_decode($headerB64);
$headerJSON = json_decode($header);
$ivKW = base64url_decode($headerJSON->iv);
$tagKW = base64url_decode($headerJSON->tag);
// Ciphertext
$ciphertextKW = base64url_decode($parts[1]);
// Key derivation via password (as already supplied in the posted code)
$password = "-1FwMVo7r{}B17iqly51zM3zvOAh0/r9636-NOXS";
$keyKW = substr(hash('sha256', $password, true), 0, 32);
// Decrypt key
$decryptedKey = openssl_decrypt($ciphertextKW, "aes-256-gcm", $keyKW, OPENSSL_RAW_DATA, $ivKW, $tagKW);
print("Decrypted key (hex): " . bin2hex($decryptedKey) . "\n");
Now, in the second step, the actual ciphertext can be decrypted using the decrypted key. The third, fourth and fifth part are, each Base64url encoded, the IV, the actual ciphertext and the tag, respectively. The encryption algorithm is specified in the parameter enc of the header and is A256GCM, which means AES GCM using 256-bit key. In this step the Base64url encoded header must be used as AAD:
// IV, ciphertext, tag
$ivCT = base64url_decode($parts[2]);
$ciphertextCT = base64url_decode($parts[3]);
$tagCT = base64url_decode($parts[4]);
// Decrypt ciphertext
$decryptedText = openssl_decrypt($ciphertextCT, "aes-256-gcm", $decryptedKey, OPENSSL_RAW_DATA, $ivCT, $tagCT, $headerB64);
print("Decrypted ciphertext: " . $decryptedText . "\n");
This gives 4957030013948007 as plaintext, matching the expected result.
Related
I have an encrypted string and its key, which is created with SQL Server using "EncryptByPassPhrase", how can i decrypt it in PHP?
I have read the documentation of "EncryptByPassPhrase" which states that this is Triple DES encryption of 128 Length. I tried 3DES decryption of PHP but it is not returning the expected output.
Encryption in MS SQL is done with
declare #encrypt varbinary(200)
select #encrypt = EncryptByPassPhrase('key', 'taskseq=10000&amt=200.5' )
select #encrypt
I am decrypting it in PHP as following:
function decryptECB($encrypted, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
// decrypting
$stringText = mcrypt_decrypt(MCRYPT_3DES, $key, $encrypted,
MCRYPT_MODE_ECB, $iv);
return $stringText;
}
I took the liberty of translating this Stack Overflow answer into PHP.
This is the result:
<?php
// SQL Server's DecryptByPassphrase translated into PHP.
function decrypt(string $data, string $password): ?string {
// SQL Server <2017 uses SHA1 for the key and the DES-EDE-CBC crypto algorithm
// whereas SQL Server >= 2017 uses SHA256 and AES-256-CBC.
// Version 1 is the SHA1 + DES-EDE-CBC version, Version 2 is the AES-256-CBC version.
// Version is stored in the first four bytes as a little endian int32.
$version_bytes = substr($data, 0, 4);
$version = unpack('V', $version_bytes)[1];
// Password must be converted to the UTF-16LE encoding.
$passwordUtf16 = mb_convert_encoding($password, 'UTF-16LE');
if ($version === 1) {
// Key is hashed using SHA1, The first 16 bytes of the hash are used.
$key = substr(hash('sha1', $passwordUtf16, true), 0, 16);
$method = 'des-ede-cbc';
$options = OPENSSL_RAW_DATA;
$iv = substr($data, 4, 8); // initialization vector of 8 bytes
$encrypted_data = substr($data, 12); // actual encrypted data
} else if ($version === 2) {
// Key is hashed using sha256. Key length is always 32 bytes.
$key = hash('sha256', $passwordUtf16, true);
$method = 'aes-256-cbc';
$options = OPENSSL_RAW_DATA;
$iv = substr($data, 4, 16); // iv of 16 bytes
$encrypted_data = substr($data, 20);
} else {
throw new \InvalidArgumentException('Invalid version');
}
$decrypted = openssl_decrypt($encrypted_data, $method, $key, $options, $iv);
if ($decrypted === false) {
return null;
}
// First 8 bytes contain the magic number 0xbaadf00d and the length
// of the decrypted data
$decrypted = substr($decrypted, 8);
// UTF-16 encoding should be converted to UTF-8. Note that
// there might be a better way to accomplish this.
$isUtf16 = strpos($decrypted, 0) !== false;
if ($isUtf16) {
return mb_convert_encoding($decrypted, 'UTF-8', 'UTF-16LE');
}
return $decrypted;
}
// A version 1 encrypted string. Taken directly from the Stack Overflow answer linked above
$s = '010000007854E155CEE338D5E34808BA95367D506B97C63FB5114DD4CE687FE457C1B5D5';
$password = 'banana';
$bin = hex2bin($s);
$d = decrypt($bin, $password);
var_dump($d); // string(6) "turkey"
// A version 2 encrypted string. Taken directly from the Stack Overflow answer linked above
$s = '02000000266AD4F387FA9474E825B013B0232E73A398A5F72B79BC90D63BD1E45AE3AA5518828D187125BECC285D55FA7CAFED61';
$password = 'Radames';
$bin = hex2bin($s);
$d = decrypt($bin, $password);
var_dump($d); // string(16) "LetTheSunShining"
Sidenote: mcrypt is deprecated as it has been abandoned for over a decade.
EncryptByPassPhrase() uses a proprietary format that don't seem to have readily available documentation. Best bet for decrypt is DecryptByPassPhrase().
The purpose of this proprietary format is to be used in the database layer of your application - not cross application / network / languages.
If you are dead set of using this format (which i would recommend not to), you would need to obtain the specification of this format, including what kind of key deviation functions are used to turn passwords into actual encryption keys etc.
When you have this specification, you would then have to implement this on your own.
Use the below in query
DECRYPTBYPASSPHRASE('key', [field] )
Reference
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");
}
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.
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
I have an encrypted bit of text that I need to decrypt. It's encrypted with AES-256-CBC. I have the encrypted text, key, and iv. However, no matter what I try I just can't seem to get it to work.
The internet has suggested that mcrypt's Rijndael cypher should be able to do this, so here's what I have now:
function decrypt_data($data, $iv, $key) {
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
As it stands now I get 2 warnings and the output is gibberish:
Warning: mcrypt_generic_init() [function.mcrypt-generic-init]: Key size too large; supplied length: 64, max: 32 in /var/www/includes/function.decrypt_data.php on line 8
Warning: mcrypt_generic_init() [function.mcrypt-generic-init]: Iv size incorrect; supplied length: 32, needed: 16 in /var/www/includes/function.decrypt_data.php on line 8
Any help would be appreciated.
I'm not terribly familiar with this stuff, but it seems like trying MCRYPT_RIJNDAEL_256 in place of MCRYPT_RIJNDAEL_128 would be an obvious next step...
Edit: You're right -- this isn't what you need. MCRYPT_RIJNDAEL_128 is in fact the right choice. According to the link you provided, your key and IV are twice as long as they should be:
// How do you do 256-bit AES encryption in PHP vs. 128-bit AES encryption???
// The answer is: Give it a key that's 32 bytes long as opposed to 16 bytes long.
// For example:
$key256 = '12345678901234561234567890123456';
$key128 = '1234567890123456';
// Here's our 128-bit IV which is used for both 256-bit and 128-bit keys.
$iv = '1234567890123456';
I send to you one example,
Please, check the code, ok
$data_to_encrypt = "2~1~000024~0910~20130723092446~T~00002000~USD~F~375019001012120~0~0~00000000000~";
$key128 = "abcdef0123456789abcdef0123456789";
$iv = "0000000000000000";
$cc = $data_to_encrypt;
$key = $key128;
$iv = $iv;
$length = strlen($cc);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');
mcrypt_generic_init($cipher, $key, $iv);
$encrypted = base64_encode(mcrypt_generic($cipher,$cc));
mcrypt_generic_deinit($cipher);
mcrypt_generic_init($cipher, $key, $iv);
$decrypted = mdecrypt_generic($cipher,base64_decode($encrypted));
mcrypt_generic_deinit($cipher);
echo "encrypted: " . $encrypted;
echo "<br/>";
echo "length:".strlen($encrypted);
echo "<br/>";
echo "decrypted: " . substr($decrypted, 0, $length);