I'm using Zend2 Crypt module to encrypt a data. Here's my code.
$cipher = BlockCipher::factory('mcrypt', array(
'algorithm' => 'aes',
));
$cipher->setKey('mypassphrase');
$encrypted = $cipher->encrypt('Hey, I am the secret data');
Cool, it works well! Now, I need to decrypt that $encrypted data (Hey, I am the secret data) in Python.
I am using pycrypto to do that. What the steps to decrypt data outside my PHP environment?
from Crypto.Cipher import AES
import base64
import hashlib
password = 'mypassphrase'
key = hashlib.sha256(password).digest()
decoded = base64.standard_b64decode(encrypted)
cipher = AES.new(key, AES.MODE_CBC)
data = cipher.decrypt(decoded)
I need to specify an IV because Zend uses MODE_CBC by default. How can I specify it in my Python code?
Here's the Zend2 documentation:
The output of the encryption is a string, encoded in Base64 (default), that contains the HMAC value, the IV vector, and the encrypted text. The encryption mode used is the CBC (with a random IV by default) and SHA256 as default hash algorithm of the HMAC. The Mcrypt adapter encrypts using the PKCS#7 padding mechanism by default. You can specify a different padding method using a special adapter for that (Zend\Crypt\Symmetric\Padding). The encryption and authentication keys used by the BlockCipher are generated with the PBKDF2 algorithm, used as key derivation function from the user’s key specified using the setKey() method.
Can someone help me to adapt my Python code to decrypt the data?
Thanks
I found a way to decrypt the data encrypted by Zend2. Here's my code:
from base64 import b64decode
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Hash import SHA256, HMAC
from Crypto.Protocol.KDF import PBKDF2
# The hmac starts from 0 to 64 (length).
hmac_size = 64
hmac = data[:hmac_size]
# The cipher text starts after the hmac to the end.
# The cipher text is base64 encoded, so I decoded it.
ciphertext = data[hmac_size:]
ciphertext = b64decode(ciphertext)
# The IV starts from 0 to 16 (length) of the ciphertext.
iv = ciphertext[:16]
# The key size is 256 bits -> 32 bytes.
key_size = 32
# The passphrase of the key.
password = 'mypassphrase'
# The key is generated using PBKDF2 Key Derivation Function.
# In the case of Zend2 Crypt module, the iteration number is 5000,
# the result length is the key_size * 2 (64) and the HMAC is computed
# using the SHA256 algorithm
the_hash = PBKDF2(password, iv, count=5000, dkLen=64, prf=lambda p, s:
HMAC.new(p, s, SHA256).digest())
# The key starts from 0 to key_size (32).
key = the_hash[:key_size]
# The hmac key starts after the key to the end.
key_hmac = the_hash[key_size:]
# HMAC verification
hmac_new = HMAC.new(key_hmac, 'aes%s' % ciphertext, SHA256).hexdigest()
if hmac_new != hmac:
raise Exception('HMAC verification failed.')
# Instanciate the cipher (AES CBC).
cipher = AES.new(key, AES.MODE_CBC, iv)
# It's time to decrypt the data! The ciphertext starts after the IV (so, 16 after).
data = cipher.decrypt(ciphertext[16:])
Mission succeeded!
Related
I have a string encrypted using this command :
echo 'hello world' | openssl enc -pass pass:MYPASSWORD -aes-256-cbc -md sha3-256 -pbkdf2 -iter 10000 -out crypt
that is stored in a crypt file
I would like to know how to decrypt this file in php
I understand I need to use a combination of these two functions
openssl_pbkdf2
openssl_decrypt
However I fail to understand
openssl_pbkdf2 requires a salt parameter and key_length
openssl_decrypt requires a iv parameter
how do i provide these 3 parameters ?
Ok as the information were scatered all accross the internet I gather all it here, hoping it will save somebody times in the future
if you just want the code, jump to the end
First: The salt is stored in the output file by openssl
it took me quite some time to understand this fact. As this question state, the format is not based on any standard.
so you need to extract these values from the file, as of openssl 1.1.1 the format is the following
(you can find the exact implementation here: https://github.com/openssl/openssl/blob/f7d2427ac3404ce1ed555bf61885eeb0432b5789/apps/enc.c )
8 bytes containing Salted__
8 bytes containing the salt itself
Second: The IV and KEY are derivated from PASSWORD + SALT using openssl_pbkdf2
The IV is not a random one, uncorrelated from anything. The IV is derivated from the password
openssl_pbkdf2 returns 1 string, but this is BOTH the IV and the KEY concatenated (it's not explained in the php documentation) but you can see it here in the c code
The code
Once you get this it's actually straitghforward
read the file
extract the salt
give the password + salt to openssl_pbkdf2 which gives you key + iv
split the key and iv in dedicated variable
feed key and iv to openssl_decrypt along with the ciphered text (stripped from the salt header)
profit
Here's the code
<?php
$content = file_get_contents('crypt');
$salt = mb_substr($content, 8, 8, '8bit');
$derivatedKey = openssl_pbkdf2(
'MYPASSOWRD',
$salt,
// key_length is 48 bytes because
// the key itself is 32 bytes (256 bits, because aes 256)
// and the IV is 16 bytes (returned by openssl_cipher_iv_length)
// so 32+16 -> 48
key_length: 48,
// 10000 is a of 2021 the amount recommended by the NIST
// see https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf section 5.1.1.2
// quote:
// For PBKDF2, the cost factor is an iteration count: the more times the PBKDF2 function is
// iterated, the longer it takes to compute the password hash. Therefore, the iteration count
// SHOULD be as large as verification server performance will allow, typically at least 10,000
// iterations.
iterations: 10000,
// for the same reason we use sha3-256
// quote:
// A memory-hard function SHOULD be used because it increases the cost of an attack.
// The key derivation function SHALL use an approved one-way function such as
// Keyed Hash Message Authentication Code (HMAC) [FIPS 198-1],
// any approved hash function in SP 800-107, Secure Hash Algorithm 3 (SHA-3)
digest_algo: 'sha3-256'
);
// the key itself is 32 bytes (i.e 256 bits, because aes *256*)
$key = mb_substr($derivatedKey, 0, 32, '8bit');
$iv = mb_substr($derivatedKey, 32, openssl_cipher_iv_length('aes-256-cbc'), '8bit');
// 16 is the 8 bytes of `Salted__` and 8 bytes of salt itself
$cypherText = mb_substr($content, 16, encoding: '8bit');
echo openssl_decrypt($cypherText, 'aes-256-cbc', $key, iv: $iv);
I was given to use the below credentials to decrypt the incoming source in php:
128-bit AES/CBC/PKCS5Padding
Secret key (hex): ec313f73c50b8fcb3ecc473107946b358ddf7a04894be9f6eb3bb0781a6d0d2d
IV (hex, initialization vector): 00000000000000000000000000000000
Mode: CBC
This is what I tried with the warning error:
function decrypt($encrypted_data){
$ciphering = 'aes-128-cbc';
$iv_length = openssl_cipher_iv_length($ciphering);
$options = 0;
$decrypt_iv = '00000000000000000000000000000000';
$decrypt_key = 'ec313f73c50b8fcb3ecc473107946b358ddf7a04894be9f6eb3bb0781a6d0d2d';
$decryption = openssl_decrypt($encryption, $ciphering, $decrypt_key, $options, $decrypt_iv);
return $decryption;
}
Warning: openssl_decrypt(): IV passed is 32 bytes long which is longer
than the 16 expected by selected cipher
would it require extra conversion for the hex secret key and IV?
Yes, use hex2bin() to convert the hex-encoded pieces to raw binary.
The cipherText (encrypted data) is likely somehow encoded as well. That's usually done with Base64, but it might be hex too ... apply appropriate decoding there too.
Also, I seriously hope you didn't just share the real encryption key here.
I have acquired a public key from a service provider and i now want to encrypt a password.
I have tried to encrypt this way
$publicKey = file_get_contents('cert.cer');
//$plaintext = "Safaricom132!";
$plaintext = "safaricom323!";
openssl_public_encrypt($plaintext, $encrypted, $publicKey, OPENSSL_PKCS1_PADDING);
echo base64_encode($encrypted);
I get this result
k97YxoV0uS08ok0n99id8PPp9kfSdebhdHaVroTmGF16hR70+U30xSrsRgIaVvHrzu535oH7Mz2TxkgQktq4TmVnxIhWbwBYDlVfnNoAUaHAW/oOYbF6bRR+vCHvMAI8TrQLaMK8XhVMjHqr27pmoqEyzEYC0sop+rp2T81CcWFlyJDr57GHuSoeka8pHMFSsGGvU7S+K0ZZ7xNQGOUpvT8skDMo3u7RUjbDk466dEtR1SUWfYTwVcrBCAFWI9ElfzFzgrWSr+ECelbU7n4D9dJegt9HvPhI2Nnc2q0V7DvHdwO+lHt7tKlM9xbxCTewiFHWjn9+lCAzwqnorHwN3g==
Should i specify the cipher i want to use or is the key i have generated a validly encrypted text?>
You have specified a cipher. Your certificate undoubtedly contains an RSA public key - so only RSA encryption is possible using this certificate. And you've specified PKCS#1 padding, which determines the encryption scheme used for RSA encryption.
No doubt you've generated a validly encrypted text. However, the only way to be 100% sure is to decrypt said ciphertext with the private key (after base 64 decoding the ciphertext, of course). If that returns your plaintext message - "safari323!" - then you know that decryption succeeded.
Note that PKCS#1 is not as secure as it was once thought to be. The current best of breed is OAEP padding or RSA-KEM.
I got the following requirements for the encryption for the API i am currently trying to access:
PKCS7 padding method
CBC encryption mode
AES key size 256, block size 128
Everytime i submit to the API with the encryption, there seems to be something wrong with the API (unfortunately no error is produced).
$Data = "GOOD!";
$aesKey = "1234567812345678";
$EncryptedData = encrypt($aesKey,$Data);
$DecryptedData = decrypt($aesKey,$EncryptedData);
echo "Orignal Data : ". $Data;
echo "<br/>";
echo "After encryption = ". $EncryptedData;
echo "<br/>";
echo "After decryption = " .$DecryptedData;
function encrypt($aesKey, $dataToEncrypt) {
$output = false;
$iv = '{{{{{{{{{{{{{{{{';
$output = openssl_encrypt($dataToEncrypt, 'AES-128-CBC', $aesKey,
OPENSSL_RAW_DATA, $iv);
$output = base64_encode($output);
return $output;
}
function decrypt($aesKey, $dataTodecrypt) {
$output = false;
$iv = '{{{{{{{{{{{{{{{{';
$dataTodecrypt = base64_decode ($dataTodecrypt);
$dataTodecrypt = $output = openssl_decrypt($dataTodecrypt, 'AES-128-CBC',
$aesKey, OPENSSL_RAW_DATA, $iv);
return $output;
}
Questions:
What exactly is PKCS7 padding method and can be implemented with php?
AES 256 is fine but what exactly does block size mean?
What exactly does IV do?
AES 256 is fine but what exactly does block size mean?
AES has a fixed block size of 128 bit. A block cipher only works on one block of a specific size. A mode operation extends a block cipher with the ability to work on multiple blocks and a padding enables it to work on plaintexts that are not a multiple of the block size.
AES-128-CBC means AES with key size of 128 bit and the CBC mode of operation. If you want to use AES-256, then you need to tell OpenSSL that: AES-256-CBC. Additionally, you need to use a key that is actually 256 bit long. Your current key is only 128 bit long.
What exactly is PKCS7 padding method and can be implemented with php?
openssl_encrypt() already does PKCS#7 padding for you and openssl_decrypt() removes it for you.
What exactly does IV do?
A random IV randomizes the ciphertext which means that encrypting the same plaintext with the same key, but a different IV produces a different ciphertext which is indistinguishable from random noise or other the same encryption with a different IV. Wikipedia has a good description what this actually does.
Keep in mind that an IV must be randomly generated for each iteration. Otherwise, an attacker who observes only the ciphertext may discover that you encrypted the same plaintext multiple times.
Keep in mind that an AES key is supposed to be quite noisy with high entropy. "12345..." looks more like a password. If you want to use passwords, then you need to derive a key from that password. PBKDF2 is a good idea with a random salt and a lot of iterations.
What exactly is PKCS7 padding method and can be implemented with php?
I am not really certain 'padding' is the phrase you mean here. While the PKCS#7 format does rely on padding, the example you provide has absolutely nothing to do with asymmetric encryption and the ASN.1 format for the PKCS#7 messaging syntax mentioned.
AES 256 is fine but what exactly does block size mean?
Block size is the bit size an encryption cipher, like AES-256, operates on per permutation.
What exactly does IV do?
An IV is short for initialization vector or for some symmetric encryption cipher implementations it can also be referred to as an nonce.
Both are used to help strengthen the resulting cipher text. You can think of them as being similar to a salt for a non-reversible hashing algorithm.
You should avoid re-using the same IV.
In regards to your example; the documentation for openssl_encrypt() states the following function usage:
string openssl_encrypt ( string $data , string $method , string $password [, int $options = 0 [, string $iv = "" ]] )
Your encryption of the plain text looks accurate (while I would choose AES-256-GCM vs. AES-128-CBC as the algorithm, blocksize & mode):
$output = openssl_encrypt($dataToEncrypt, 'AES-128-CBC', $aesKey,
OPENSSL_RAW_DATA, $iv);
Without testing I am assuming you are getting a valid base64 encoded value.
The manual for the openssl_decrypt() method states the following usage:
string openssl_decrypt ( string $data , string $method , string $password [, int $options = 0 [, string $iv = "" ]] )
While technically your implementation is correct; I would suggest the following (note the double assignment to $dataTodecrypt = $output = openssl_decrypt()):
$output = openssl_decrypt(base64_decode($dataTodecrypt), 'AES-128-CBC', $aesKey, OPENSSL_RAW_DATA, $iv);
I have been trying to encrypt a string in PHP and Ruby using the same key and iv but I always got different results.
Below is the PHP Code
$data = "This string needs to be encrypted";
$key = "1234567887654321abcdefghabcdefgh";
$iv = "1234567887654321abcdefghabcdefgh";
echo $encrypted_data = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
Below is the Ruby Code
data = "This string needs to be encrypted"
key = "1234567887654321abcdefghabcdefgh"
iv = "1234567887654321abcdefghabcdefgh"
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
aes.encrypt
aes.key = key
aes.iv = iv
encrypted_data = aes.update(data) + aes.final
Could somebody please help me get the same encrypted data in PHP and Ruby? I encrypted some data in PHP and then decrypted in Ruby but didn't get the data back. So I think the issue is PHP and Ruby encryption and decryption mechanism work differently. Please correct me if I am wrong. Thanks
Don't hard code IV's , it is insecure. IVs must be random but can be public , so just use
mcrypt_create_iv and prepend it to the front of the ciphtertext and then extract it before
decrypting
You likely have three problems
MCRYPT_RIJNDAEL_256 is nott AES. AES is a specific version RIJNDAEL that was standardized with a 128 bit block size and either 128 or 256 bit keys. MCRYPT_RIJNDAEL_256 is RIJNDAEL with a 256 bit block size. You want to use MCRYPT_RIJNDAEL_128 which is actually AES. For php, key length is just determined by the length of the key. So just give it a 256 bit ( 32 character) key and you will be fine. Note block size does not effect security really, so don't worry about the deference and just use AES with a 256 bit key: its good enough for the NSA and top secret data.
Padding. AES only takes fixed 128 bit chunks, so you must pad out the text to be a multiple of that size. PHP doesn't really bad and i believe SSL uses pkcs7 padding. Note that even with different padding schemes, for most of them the start of the cipher text should the the same there just may be garbage at the end.
String encoding. AES is defined with bit inputs, in c typically this is a byte array. Ruby and PHP use strings. I'd be willing to bet your string encodings are different.