Encrypting in PHP with mcrypt
<?php
$string = 'Secret Message';
$key = 'd4b494e4502a62edd695a903a94c2701';
$iv = '02f30dffbb0d084755f438f7d8be4a7d';
$encrypted = base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$key,
$string,
MCRYPT_MODE_CBC,
$iv
)
);
//$encrypted results in 'nYoFAiyDARVSI09lH/IPdim5TvE51izVjk6sc2AK9Rg='
?>
Decrypting in Javascript with CryptoJS
<script>
var encrypted = 'nYoFAiyDARVSI09lH/IPdim5TvE51izVjk6sc2AK9Rg=';
var key = CryptoJS.enc.Hex.parse('d4b494e4502a62edd695a903a94c2701');
var iv = CryptoJS.enc.Hex.parse('02f30dffbb0d084755f438f7d8be4a7d');
var decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});
console.log(decrypted.toString(CryptoJS.enc.Utf8)); //prints an empty string
</script>
I can't figure out how to get the Javascript side to spit out the original text.
SOLVED
Note: I found out that "MCRYPT_RIJNDAEL_256" in PHP's mcrypt is NOT included in AES. It uses the Rijndael method, BUT with a 256-bit block size (not included in AES). So, CryptoJS does not handle 256-bit block sizes for Rijndael. Thanks, GregS
Nonetheless, I found an implementation that successfully decrypted the ciphertext I get from running my PHP mcrypt function above, using MCRYPT_RIJNDAEL_256 (Rijndael, 256-bit block size, with 256-bit key/32-byte key).
Here it is:
https://code.google.com/p/js-mcrypt/
<script>
var encrypted = 'nYoFAiyDARVSI09lH/IPdim5TvE51izVjk6sc2AK9Rg=';
var key = 'd4b494e4502a62edd695a903a94c2701';
var iv = '02f30dffbb0d084755f438f7d8be4a7d';
var decrypted = mcrypt.Decrypt(atob(encrypted), iv, key, 'rijndael-256', 'cbc');
</script>
I hope this helps someone as I spent a week of my spare time trying to figure this out and almost giving up.
Related
I'm trying to encrypt a message in javascript (using crypto-js library) and to decrypt it in java.
This is the javascript code:
var key = CryptoJS.enc.Utf8.parse(aesPassword);
var ive = CryptoJS.enc.Utf8.parse(aesIv);
var encryptedData = CryptoJS.AES.encrypt(dataToEncrypt, key, {mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: ive});
And this is the java code:
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final SecretKeySpec key = new SecretKeySpec(aesPassword().getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(aesIv().getBytes("UTF-8")));
byte[] decrypted = cipher.doFinal(DatatypeConverter.parseBase64Binary(message));
But when I try to decrypt in Java this exception is thrown:
javax.crypto.BadPaddingException: Given final block not properly padded
password: 6h2faBePVxpgyFSN
iv: NKOzRKrmEMKs1kE4
data to encrypt: "{token: cMGOIrYlJm9lPhPW}"
Any help?
Thanks in advance
I may be wrong, but I think BadPaddingException in this case means that you don't possess the correct key to successfully perform the decryption.The exception essentially means that the key is either too short or too long (I think).
Try{
String decrypted = aes.decrypt(...);
System.out.println(decryted);
}catch(Exception e){
}
Something like the code above may work, as System.out is only reached when the BadPaddingException isn't caught, this could be used in a loop when trying possible keys for decryption, for example, if you were trying to calculate all possible keys for the decryption.
Im trying to convert the java library - AESCrypt-Java
to javascript.
This is my implementation so far for the decrypt function. Im not able to decrypt the text. Can someone figure out where I'm going wrong?
function decrypt(password, base64text) {
key = generateKey(password);
var decodedCipherText = new Buffer(base64text, 'base64')
var iv = new Buffer(16);
iv.fill(0);
var decipher = crypto.createDecipheriv("aes-256-cbc", key, iv)
let decrypted = decipher.update(decodedCipherText, 'base64', 'utf-8');
decrypted += decipher.final('utf-8')
return decryptedBytes
}
function generateKey(password) {
return crypto.createHash('sha256').update(usr_id).digest();
}
var encryptedText = '1+2yFMDH1C/uIc1huwezbrsQ==';
var password = '8AVrWtyabQ';
decrypt(password, encryptedText)
The expected plaintext output is Wordpress.
You are making a few decisions that will adversely affect the security of your sensitive values:
You are using a static, all-zero IV. The IV must be unique and non-predictable for every message encrypted with a specific key. The IV can then be prepended to the cipher text and transmitted unprotected to the recipient, where it is sliced and used for decryption.
Your key derivation function (KDF) is weak -- SHA-256 can be cracked at 23 billion attempts per second on commodity hardware. Use a key-stretching algorithm like PBKDF2 with a high iteration count, or bcrypt or scrypt for memory hardness.
Your cipher text is not authenticated -- AES/CBC provides confidentiality, but not integrity or authentication. An interceptor can manipulate the cipher text in transmission and attempt to decrypt it. This can result in unauthorized decryption (i.e. injecting malicious plaintext into your application) or a padding oracle attack, and eventually cipher text recovery. Use an authenticated encryption (with associated data) (AE or AEAD) cipher mode to mitigate this, or add a strong HMAC construction using a separate key over the cipher text and verify prior to decryption with a constant-time equals method.
new Buffer(string, encoding) and new Buffer(size) are deprecated and Buffer.from(string, encoding) and Buffer.alloc(size) should be used instead. You create a Buffer containing the provided cipher text which is encoded in Base64. I have a feeling there is an issue occurring with your encoding (you don't provide any example output for us to see). Here is an example of encrypting and decrypting with Buffer objects.
function encrypt(buffer){
var cipher = crypto.createCipher(algorithm,password)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
function decrypt(buffer){
var decipher = crypto.createDecipher(algorithm,password)
var dec = Buffer.concat([decipher.update(buffer) , decipher.final()]);
return dec;
}
var hw = encrypt(new Buffer("hello world", "utf8"))
// outputs hello world
console.log(decrypt(hw).toString('utf8'));
As you can see, cipher.update(buffer) handles the encoding internally so you don't need to.
I would like to AES encode in Delphi XE4 and decode in JavaScript.
My Delphi code:
(I use DCPcrypt Cryptographic Component Library v2 Beta 3)
procedure TForm1.Button5Click(Sender: TObject);
var
Cipher : TDCP_rijndael;
key: Ansistring;
data: Ansistring;
iv: Ansistring;
begin
Key := SHA256('password');
IV := 'cd6f6eea9a2a59f2';
Data := '12345678901234567890';
Cipher := TDCP_rijndael.Create(Self);
if Length(Key) <= 16 then
Cipher.Init(Key[1], 128, #IV[1])
else
if Length(Key) <= 24 then
Cipher.Init(Key[1], 192, #IV[1])
else
Cipher.Init(Key[1], 256, #IV[1]);
Cipher.EncryptCBC(Data[1],Data[1],Length(Data));
memo1.Lines.Add('DATA_ENC:'+DATA);
memo1.Lines.Add('DATA_BASE64_ENC: '+Base64encode(DATA));
end;
My JavaScript code (I use CryptoJS):
encypted = 'Pz8/yw0/ck+4tTY/Pn8zPz/f9D8='; //input base64 text from Delphi routine
var key = CryptoJS.SHA256(CryptoJS.enc.Base64.parse("password"));
var iv = CryptoJS.enc.Base64.parse('cd6f6eea9a2a59f2');
var decrypted = CryptoJS.AES.decrypt(encrypted,key,
keySize: 256,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
console.log('DECRYPTED: '+decrypted.toString(CryptoJS.enc.Utf8));
I do not get back the original text, please help me. What is the matter?
I have no idea about Delphi, so I can't help you there, but I can say, that your Delphi code is wrong, because if you parse the Base64 ciphertext and encode it as Hex, you will see this:
3f3f3fcb0d3f724fb8b5363f3e7f333f3fdff43f
A ciphertext of a modern cipher is supposed to be indistinguishable from random noise, but this ciphertext looks rather regular (there are a lot of 0x3f bytes).
Your JavaScript code is rather all over the place. Almost every string that you use, has a wrong encoding.
run.onclick = function(){
var encrypted = CryptoJS.enc.Base64.parse(inVal.value);
var key = CryptoJS.SHA256(CryptoJS.enc.Utf8.parse("password"));
var iv = CryptoJS.enc.Utf8.parse('cd6f6eea9a2a59f2');
var decrypted = CryptoJS.AES.decrypt({
ciphertext: encrypted
}, key, {
iv: iv,
padding: CryptoJS.pad.ZeroPadding
});
outHex.innerHTML = decrypted.toString();
outUtf8.innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
};
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/sha256.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/pad-zeropadding-min.js"></script>
<div>Base64 input: <input id="inVal" value="Pz8/yw0/ck+4tTY/Pn8zPz/f9D8="></div>
<div>Decrypted Hex: <span id="outHex">-</span></div>
<div>Decrypted Utf8: <span id="outUtf8">-</span></div>
<div><button id="run">Decrypt</button></div>
When you have fixed your Delphi code, you can include the Base64 in the above runnable snippet and see that decrypts correctly.
Security considerations:
You need to use a random IV, if you're sending multiple ciphertexts with the same key. If you send the same message again, an attacker can see that only by observing ciphertexts. The IV doesn't have to be secret, so you can send it along with the ciphertext. A common way is to prepend it to the ciphertext and remove it before decryption.
SHA-256 is not sufficient for key derivation from a low-entropy password. You should use an iterated key derivation function (KDF) such as PBKDF2, bcrypt, scrypt or Argon2. See more: How to securely hash passwords?
It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme.
I am having some trouble decrypting data using CryptoJS that was encrypted in PHP. Maybe somebody can advise me on where I am going wrong?
I am encrypting as follows:
Get hashed password
Take substring of (0,16) as the key
Encrypt (MCRYPT_RIJNDAEL_128)
Encode ciphertext as base64
When decrypting I do the same:
Get hashed password
Take substring of (0,16) as the key
Base64 decode the ciphertext
Decrypt
PHP:
public function encrypt($input, $key) {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$input = $this->_pkcs5_pad($input, $size);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, $input);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$data = base64_encode($data);
return $data;
}
JavaScript:
function decrypt(ciphertext, hashedPsw) {
var key = hashedPsw.substring(0, 16);
var key = CryptoJS.enc.Hex.parse(key);
var options = { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, keySize:128 / 32 };
ciphertext = CryptoJS.enc.Base64.parse(ciphertext);
var decrypted = CryptoJS.AES.decrypt(ciphertext, key);
return decrypted;
}
The CryptoJS decrypt function expects an object that contains a WordArray and not the WordArray itself, so you need to use:
var decrypted = CryptoJS.AES.decrypt({ ciphertext: ciphertext }, key, options);
You also need to pass the options to the decrypt function. Otherwise, CryptoJS won't know that you wanted to use ECB mode.
Security
Don't use ECB mode! It's not semantically secure. You should at the very least use CBC mode with a random IV. The IV doesn't need to be secret, so you can simply prepend it to the ciphertext.
Then you should authenticate your ciphertexts. This can be done with authenticated modes like GCM or EAX, but they are not provided by mcrypt or CryptoJS. The next best thing is to use an encrypt-then-MAC scheme where you use a strong keyed hash function like HMAC-SHA256 over the ciphertext to make it infeasible for an attacker to change ciphertexts without you knowing it.
I just discovered the answer in a previous thread: Turns out that the problem was the key encoding.
I have a js script which encrypts data which is 'Blader'. If I encrypt it it returns an output of
JS-SCRIPT RESULT
uqnOrevjCc2YCvY3uKNjzA==
Now, being this answer as a base for comparison, I wrote or rather say searched for a equivalent script in PHP similar to my JS script. What confuses me is that the logic is fine and correct but the answer is different. On my php script which is by the uses mcrypt, I have this result in
mcrypt RESULT
HzfWFNKcAmkO6zJEYjbG4Q==
If you notice, the length are the same which means that the logic/modification on the code I did was correct. Now As i have said before I copied the script over some posts here.
Here's the JS Script which i think uses the crypto-JS
function crypto_encrypt(text) { //This is for JS
var keyBase64 = CryptoJS.enc.Base64.parse("ITU2NjNhI0tOc2FmZExOTQ==");
var iv = CryptoJS.enc.Base64.parse('AAAAAAAAAAAAAAAAAAAAAA==');
var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(text), keyBase64,
{
keySize: 128 / 8,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
//padding: CryptoJS.pad.ZeroPadding
});
// Returns a Base64 encoded string.
return encrypted;
}
And here's the code I found in mcrypt/mycrypt
<?php
$encrypted = "Blader";
$iv = "0000000000000000"; // iv_base64 from JS
$key = hexdec("213536363361234b4e736166644c4e4d"); // key_base64 from JS
$plaintext = mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv );
echo base64_encode($plaintext);
?>
Now the question is, I tried from UTF8_encode upto everything base64_encode and decode but still can't find what's wrong and I'm curios that is this attainable or not since I notice that the IV from JS-Script is different from the IV in mcryp(PHP) everything I tried to echo it. Any advice, comments and suggestion will be highly be thanked.
Peace out.
First, you have to use exactly the same key and IV in PHP as you do in CryptoJS, or it's just not going to work. Did you compare values of the key and IV? They don't match.
Second, you have to use the same padding on each side. Did you check how MCrypt pads? It uses zero-padding. Your two plaintexts are different, because padding is part of the plaintext.
Finally, don't you want to use mcrypt_encrypt instead of mcrypt_decrypt here?
If you match the key and IV, and the padding, and encrypt in PHP, you'll get the same result (I've manually padded with \x0a -- 10 -- to match your PKCS#7 padding):
$encrypted = "Blader\x0a\x0a\x0a\x0a\x0a\x0a\x0a\x0a\x0a\x0a";
$iv = base64_decode('AAAAAAAAAAAAAAAAAAAAAA==');
$key = base64_decode('ITU2NjNhI0tOc2FmZExOTQ==');
$plaintext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv );
echo base64_encode($plaintext);
uqnOrevjCc2YCvY3uKNjzA==