I have the following decrypted message, which has previously been encrypted using AES-256-CBC
240dcbefc0f82fadc00ef8494488aaa81400000c2def01e79fec6c4d9a822358dd8a910cac606e8afcb607793cb442093a56b7b40b0b0b0b0b0b0b0b0b0b0b0b
I derive the following 20 BYTE HMAC from this message:
dd8a910cac606e8afcb607793cb442093a56b7b4
My goal is to re-create this HMAC using PHP, I attempt with the following code:
$iv = hex2bin('240dcbefc0f82fadc00ef8494488aaa8'); // random iv - first 16 bytes of the message
$message = hex2bin('1400000c2def01e79fec6c4d9a822358'); // the actual message being decrypted - next 16 bytes
$key = hex2bin('b109124b62e2c8b8248e9865990325fddcc61143'); // encryption key
$hmac = hash_hmac('sha1', $iv.$message, $key);
print($hmac); // 03634ba3f4a0c854a0b791d27f331ecdfad1e87e
$attempt2 = hash('sha256', $iv.$message, true);
$hmac = hash_hmac('sha1', $attempt2, $key);
print($hmac); // 39ad1fb94ab251cdaf3f21cf8673e070733f4e16
I know I'm missing something but I'm struggling to understand the HMAC process as it's very confusing to me. Any help or advise is appreciated, thanks.
I found a solution on a random blog post online :D
here is what worked for me:
function check_mac($seq, $type, $msg, $key) {
$SequenceNumber = pack("NN", 0, $seq);
$Type = pack("Cnn", $type, 0x0303, strlen($msg));
$data = $SequenceNumber . $Type . $msg;
$calculated_mac = hash_hmac("sha1", $data, $key, true);
print(bin2hex($calculated_mac) . "\n");
}
Also, the IV does not need to be included, just the message by itself as the $msg variable
Blog Post:
https://adayinthelifeof.nl/2013/12/30/decoding-tls-with-php/
Related
Given the following inputs:
CLIENT RANDOM : 61fc160e0a6e96db43aebcca55da7fbccb97cc04d59fcc105490b4396a915ab9
SERVER RANDOM : f0ecb127db6e353a2985894c123532a8d092fafcf7cdce3a444f574e47524401
PREMASTER KEY : 0303ec4418e91f0abf9a012f524b61c0312008641ebce76d68b2417447e3d9a970d02fd65a20e5e3e98413dbd13b6536
First I use these input to calculate the following key buffers:
CLIENT MAC: 500c7c6e6101cb1693b4cee7db6fb0bc2725deca
SERVER MAC: 41e260280fe213bd5f2c0412c69389cac2e39e4f
CLIENT KEY: c17226212f6195c1515602a0c0864ec90e73e7bcd05ad90b196fd5a5cb2445f2
SERVER KEY: 6b07166b634a7e58e623b969eb0c024a1e866bc2719c054e1a05849b87ccf79b
CLIENT IV: dac71f7b3639cf4d4ec86ee51b9530f6
SERVER IV: c1212631365432c8d5a4c93f6f999a8e
I calculate these values using helper functions I found online:
function p_hash($algo, $secret, $seed, $size) {
$output = "";
$a = $seed;
while (strlen($output) < $size) {
$a = hash_hmac($algo, $a, $secret, true);
$output .= hash_hmac($algo, $a . $seed, $secret, true);
}
return substr($output, 0, $size);
}
function prf_tls12($secret, $label, $seed, $size) {
return p_hash("sha256", $secret, $label . $seed, $size);
}
function generate_master($pre_master_secret, $client_random, $server_random) {
return prf_tls12($pre_master_secret, 'master secret', $client_random . $server_random, 48);
}
$client_random = hex2bin('61fc160e0a6e96db43aebcca55da7fbccb97cc04d59fcc105490b4396a915ab9');
$server_random = hex2bin('f0ecb127db6e353a2985894c123532a8d092fafcf7cdce3a444f574e47524401');
$pre_master_secret = hex2bin('0303ec4418e91f0abf9a012f524b61c0312008641ebce76d68b2417447e3d9a970d02fd65a20e5e3e98413dbd13b6536');
$master_secret = generate_master($pre_master_secret, $client_random, $server_random);
$key_buffer = prf_tls12($master_secret, 'key expansion', $server_random . $client_random, 136);
$client_mac = substr($key_buffer, 0, 20);
$server_mac = substr($key_buffer, 20, 20);
$client_key = substr($key_buffer, 40, 32);
$server_key = substr($key_buffer, 72, 32);
$client_iv = substr($key_buffer, 104, 16);
$server_iv = substr($key_buffer, 120, 16);
Here I have an AES_256_CBC encrypted message sent from the CLIENT ---> SERVER in Hex format
$EncryptedMessage = 'f32da333a3416888d55c583c9796f8fc498895e386616a62aa364a41cd2bfc203c1f296b4afd9c4a9674c993bf0db558de0c0cb2b3dc4b083af3824e0b9a3327';
I run the following code using the PHPSECLIB v3.0 plugin:
$cipher = new AES('cbc');
$cipher->setIV($client_iv);
$cipher->setKey($client_key);
$cipher->disablePadding();
$DecryptedMessage = bin2hex($cipher->decrypt(hex2bin($EncryptedMessage)));
print($DecryptedMessage);
I get the following output in Hex:
f889ff0ce7cc744c2182363e7117ae74e34ccb0510099e7c11133c369f98468de9f20fa6e7207c8121484a9663929d1af4bffb5410da37029aa26b9298411e3b
Basically, I have no idea whether I'm doing this right or not. I dont seem to see anything in my decrypted message. Also, unless I specifically use the $cipher->disablePadding(); I will get a PHP Fatal Error saying that PHP Fatal error: Uncaught phpseclib3\Exception\BadDecryptionException: The ciphertext has an invalid padding length (81) compared to the block size (16)
Note: This is supposed to be an "Encrypted Handshake Message" sent as the first message from a client to server after the Key Exchange using TLS 1.2. I am unable to determine if my decryption is correct, since I cant find much on the structure of value anywhere. Any help is appreciated, thanks!
I have this code in page A:
<?php
$token = // 64 chars alphanumerical token;
$method = "aes128";
$key = "12345678";
$crypto_code = openssl_encrypt($token, $method, $key);
$encoded_crypto_code = base64_encode($crypto_code);
$target_url = "https://mysamplesite.com/use_qrcode.php?code=" . $encoded_crypto_code;
// now the code is put inside a qrcode
QRcode::png($target_url, QR_ECLEVEL_H, 3, 10);
?>
Then I read the code with a smartphone that takes me to page B (use_qrcode.php) where I have the following code:
<?php
$code_flag = 0;
if (isset($_GET["code"])){
$encoded_crypto_code = $_GET["code"];
$crypto_code = base64_decode($encoded_crypto_code);
$method = "aes128";
$key = "12345678";
$code = openssl_decrypt($crypto_code, $method, $key);
if (false === $code) {
echo sprintf("OpenSSL error: %s", openssl_error_string() . "<br>");
} else {
echo "received code: " . $code . "<br>";
$code_flag = 1;
}
}
?>
My if(false === $code) catch an error, which is "OpenSSL error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length".
If I check both the base64 encoded string and the crypto string before and after sending them by QRcode, they are ok, so no problems in QR encoding and decoding nor in base64 encoding and decoding. The problem is somewhere in the OpenSSL. I am surprised since I use openssl_ encrypt and openssl_decrypt in many places all around the site, and this is the first time I face an error. If I do not use base64 encoding and decoding, the OpenSSL error is still there.
Is there any problem with the token I use?
Same result if I use some other similar tokens.
Where am I wrong?
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.
I managed to make a code to encrypt and decrypt data in AES-CCM in PHP language. This code retrieves data, sends it to a server and adds it to a database via post URL. But if someone has access to the URL and sends it several times to the server, it will add data to the database each time.
Is there a simple way to prohibit this URL after one adding data?
Edit : I think I will add date and hour in the encryption part. Then when I will receive data, I will check date and hour with +-1min. And check if I have not already receive this message.
What do you think about this solution?! Is that easy is to crack it?!
This is my code (Fisrt part) :
$algo = 'aes-128-ccm';
$iv = random_bytes(openssl_cipher_iv_length($algo));
$key = "cd9344040aa9f9217871d46ee871c59c";
$data = '00000000010-3b57af';
$ciphertext = openssl_encrypt(
$data,
$algo,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
$ciphertext = bin2hex($ciphertext);
$iv = bin2hex($iv);
$tag = bin2hex($tag);
echo'"decrypte"';
?>
The second part :
$algo = 'aes-128-ccm';
$key = "cd9344040aa9f9217871d46ee871c59c";
$ciphertext = hex2bin($_GET['data']);
$iv = hex2bin($_GET['iv']);
$tag = hex2bin($_GET['tag']);
$decrypt = openssl_decrypt(
$ciphertext,
$algo,
$key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
if (false === $decrypt) {
throw new Exception(sprintf(
"OpenSSL error: %s", openssl_error_string()
));
}
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.