Why is the output from CryptoJS different than in PHP' openssl_encrypt if I'm using the same key and ivector?
openssl_encrypt('test' , 'aes-256-cbc', '17cc0ffd728f34c171e06c47df4227a32ee6ef5d6c60398eeab30cf34306c644', 0 , base64_decode('aROnq5DocMLgrlpAF6yjgw=='));
Output:
oIcizpTDCTgtTGu3lO1cJg==
Each time the same output.
CryptoJS:
var encrypted = CryptoJS.AES.encrypt("test", CryptoJS.enc.Hex.parse('UDOuDk5uxceoFWxtrabuEhamMC1T4Abr'), {
iv: CryptoJS.enc.Hex.parse('HLcM0VZYfsgPo2ye')}).toString();
Output:
VTJGc2RHVmtYMTlrVHh4N0F3V2VySWlFcjBGeWlRRkcyMmNabEtjWmpmYz0=
Why is the output from CryptoJS different each time?
Edit:
After your hints:
var encrypted = CryptoJS.AES.encrypt("test", CryptoJS.enc.Hex.parse('UDOuDk5uxceoFWxtrabuEhamMC1T4Abr'), {
iv: CryptoJS.enc.Hex.parse('HLcM0VZYfsgPo2ye')}).toString();
Output:
CoFpbmd4YzOiVEFzVkeaDQ==
Now each time is the same
But is different than in PHP:
openssl_encrypt("test" , "aes-256-cbc", "UDOuDk5uxceoFWxtrabuEhamMC1T4Abr", 0 , "HLcM0VZYfsgPo2ye");
Output:
oV9OZVYM80p8mlHH5wnzEg==
CryptoJS.AES.encrypt will try to automatically use AES-128, AES-192 or AES-256 depending on the key you pass it. In your case, you pass a secret passphrase ('17cc0f...') instead of a key which will cause it to generate its own AES-256 key.
The documentation states:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the
variant by the size of the key you pass in. If you use a passphrase,
then it will generate a 256-bit key.
In order to use a key, you must parse a Hex key first and pass that.
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var encrypted = CryptoJS.AES.encrypt("test", key, { iv: iv });
Related
I'm attempting to encrypt a message using Javascript's CryptoJS like so:
const encrypted_payload = CryptoJS.AES.encrypt("Hello, world!", "magickey");
console.log(enrypted_payload.toString());
... and then decrypt using Rust's Magic Crypt Library, like so:
#[macro_use] extern crate magic_crypt;
use magic_crypt::MagicCryptTrait;
fn main() {
let message = r#"U2FsdGVkX1+anrxRX8UNTJ8ur9LIf6n2YcmbmDqPSls="#;
let mc = new_magic_crypt!("magickey", 256);
let result = mc.decrypt_base64_to_string(&message)
.expect("Could not decrypt base64 to string");
println!("{}", result)
}
The Cargo.toml file for Rust includes:
[dependencies]
magic-crypt = "3.1.6"
This fails to compile. The error statement is DecryptError(BlockModeError).
If in CryptoJS the key is passed as a string, then it is interpreted as a password and the key/IV is derived using a special derivation function (EVP_BytesToKey). To process the key directly as a key, it must be passed as a WordArray.
In the Rust code the passwords seem to be simply hashed (e.g. MD5 for AES-128, SHA256 for AES-256), which is surprisingly insecure (though I'm not a Rust expert and may be overlooking something).
Anyway this results in incompatible keys. One possibility is to use the hash of the Rust key as WordArray in CryptoJS:
Example: From the magic_crypt documentation, here:
use magic_crypt::MagicCryptTrait;
let mc = new_magic_crypt!("magickey", 256);
let base64 = mc.encrypt_str_to_base64("http://magiclen.org");
assert_eq!("DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=", base64);
assert_eq!("http://magiclen.org", mc.decrypt_base64_to_string(&base64).unwrap());
magic_crypt internally derives a 32 bytes key as SHA256 hash from the password. Then the encryption with CryptoJS is:
var key = CryptoJS.SHA256("magickey");
var ciphertext = "DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=";
var iv = CryptoJS.enc.Base64.parse("AAAAAAAAAAAAAAAAAAAAAA==")
var decrypted = CryptoJS.AES.decrypt(ciphertext, key, {iv: iv});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Both codes apply CBC mode with a zero IV and PKCS7 padding. Note that a static IV is insecure and should only be used for testings.
The other direction is analogous, e.g. encryption with CryptoJS generates the ciphertext from the Rust example (assuming the same key, IV and plaintext):
var key = CryptoJS.SHA256("magickey");
var plaintext = "http://magiclen.org";
var iv = CryptoJS.enc.Base64.parse("AAAAAAAAAAAAAAAAAAAAAA==")
var ciphertext = CryptoJS.AES.encrypt(plaintext, key, {iv: iv});
console.log(ciphertext.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
I have some difficulties reproducing AES encryption and decryption in python.
Context: A year ago, I created a small django based application using this javascript library for client side encryption. Basically, some users' inputs are encrypted with a key and sent as hexadecimal strings to be stored.
For the illustration, I'll focus on bd45bcccd0 (a.k.a 'Masha' encrypted with john's key: 3ed8bd71327aafd855aac37921519767)
Encryption and decryption with the current js library
encryption utf-8 -> bytes -> encrypted bytes -> hex
decryption hex -> encrypted bytes -> bytes -> utf-8
id_password is a MD5 hash of the user's password. It is stored in the session storage and is used as a key
function encrypt(t){
var key = aesjs.utils.hex.toBytes(sessionStorage.getItem("id_password"));
var textBytes = aesjs.utils.utf8.toBytes(t);
var aesCtr = new aesjs.ModeOfOperation.ctr(key);
var encryptedBytes = aesCtr.encrypt(textBytes);
var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
return encryptedHex;
}
function decrypt(t){
var key = aesjs.utils.hex.toBytes(sessionStorage.getItem("id_password"));
var textBytes = aesjs.utils.hex.toBytes(t);
var aesCtr = new aesjs.ModeOfOperation.ctr(key);
var decriptedBytes = aesCtr.decrypt(textBytes);
var decrypted_utf8 = aesjs.utils.utf8.fromBytes(decriptedBytes);
return decrypted_utf8;
}
Once loaded in key, I get a 16 items array (So I guess a AES 128bits CTR is performed):
var key = aesjs.utils.hex.toBytes(sessionStorage.getItem("id_password"));
console.log(key)
Array(16) [ 62, 216, 189, 113, 50, 122, 175, 216, 85, 170, … ]
With the current code, encryption and decryption work
Python implementation
For unit-testing purposes, I wanted to be able to decrypt. I am using this library. To mimic the client side as much as possible, I tried the following:
john_key = "3ed8bd71327aafd855aac37921519767"
cipher = AES.new(codecs.decode(john_key,'hex_codec'), AES.MODE_CTR)
d = cipher.decrypt(codecs.decode('bd45bcccd0', 'hex_codec'))
d.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xac in position 0: invalid start byte
Here is the problem but I am not sure at which stage it occurs. Here is what I checked:
# key's length is correct
k = codecs.decode(john_key,'hex_codec')
k
b'>\xd8\xbdq2z\xaf\xd8U\xaa\xc3y!Q\x97g'
len(k)
16
# decoded message's length is correct
d = cipher.decrypt(codecs.decode('bd45bcccd0', 'hex_codec'))
len(d)
5
Since I can't rely on a library I can't reproduce the results, I wonder whether I misused PyCryptodome or whether the way this javascript library implements AES CTR encryption is reliable. Any insights?
The CTR-mode requires an IV. Since you do not explicitly create the IV, an implicitly created IV is used. However, both codes generate different IVs, so that the decryption fails. In the Python-code, a random IV is generated, in the aes-js-code a fixed IV (1) is used.
So that the decryption is possible with the Python-code, the same IV must be used here as in the aes-js-code (here and here). For this purpose:
cipher = AES.new(codecs.decode(john_key,'hex_codec'), AES.MODE_CTR)
has to be replaced by
counter = Counter.new(128, initial_value = 1)
cipher = AES.new(codecs.decode(john_key,'hex_codec'), AES.MODE_CTR, counter = counter)
which decrypts the ciphertext to Maria (however not Masha).
For security reasons it is mandatory for CTR that key/IV pairs may only be used once, i.e. if the same key is applied, a new IV must be generated for each encryption. The current code has the weakness that key/IV pairs would be repeated when using the same key. A better way would be to generate a random IV for each encryption, send this IV together with the ciphertext to the recipient (the IV isn't secret, so it is usually prepended to the ciphertext), where it can be used for the decryption.
I dealing with cryptojs and want to try a simple example with aes
var encrypted = CryptoJS.AES.encrypt("TEST_TEXT", '9021D105A446', {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var decrypt = CryptoJS.AES.decrypt(encrypted, '9021D105A446', {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log(decrypt.toString(CryptoJS.enc.Utf8));//Yeah! TEST_TEXT output as expected
Now I give a try with encrypted in base64, but not output as expected
var encryptedText = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
var encrypted2 = CryptoJS.enc.Base64.parse(encryptedText);
var decrypt2 = CryptoJS.AES.decrypt(encrypted2, '9021D105A446', {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log(decrypt2.toString(CryptoJS.enc.Utf8));// Ops! empty output
Do you know something went wrong with decryption in 2nd example?
Another question, every I run example 1, the encryptedText was difference from previous running. Is this normal behavior?
Fiddle update : https://jsfiddle.net/n6wL9a40/
You don't need to convert encrypted value to base64, encrypted.toString() returns base64 value.
var base64Value = encrypted.toString();
// base64Value is U2FsdGVkX19s42BDpx8A9I265vm+zGKSk8nEbQwNjfw=
var encryptedText = CryptoJS.enc.Base64.parse(base64Value)
var encrypted2 = encryptedText.toString(CryptoJS.enc.Base64);
var decrypt2 = CryptoJS.AES.decrypt(encrypted2, '9021D105A446', {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log(decrypt2.toString(CryptoJS.enc.Utf8));// TEST_TEXT
First, actually CryptoJS.enc.Base64.parse(encryptedText) doesn't give you back a Base64 string, but an object, you should use it with toString like this: CryptoJS.enc.Base64.parse(encryptedText).toString() to get back your original string that was encoded in Base64
But the real problem is that you use the wrong part of the encrypted object (see linked fiddle, trying to decrypt .ciphertext won't work, because ciphertext is just the last part of the encrypted result needed for decryption, first part is missing). And additionally, there is no need to convert your output to Base64 at all, because it's already in Base64!
console.log(encrypted.toString()); //will already be Base64
See all this in an updated fiddle: https://jsfiddle.net/gqkcvjxo/
EDIT:
For your other question I quote the docs:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key
You can generate a key for AES-128 by using a pass of length 16 (128/8), you'll see in the fiddle that it works, the encrypted text length is halved.
var pass = 'abcdefghijklmnop'; //must be length 16 (because 128/8)
var key = CryptoJS.enc.Utf8.parse(pass);
Updated fiddle: https://jsfiddle.net/o3975jtd/3/
To answer your other question, the encrypted text is not the same each time in first version because of the generated iv (see explanation here). By using a key with AES-128 in ECB mode, the iv is not automatically generated and comes undefined (apparently, when trying to specify one, nothing changes, probably because it's not used as told in the explanation link)
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 want to create two functions encrypt(message, key) and decrypt(ciphertext, key) using the Forge library in javascript, but I dont undestand the example code.
// generate a random key and IV
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(16);
// encrypt some bytes using CBC mode
// (other modes include: CFB, OFB, and CTR)
var cipher = forge.aes.createEncryptionCipher(key, 'CBC');
cipher.start(iv);
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());
// decrypt some bytes using CBC mode
// (other modes include: CFB, OFB, and CTR)
var cipher = forge.aes.createDecryptionCipher(key, 'CBC');
cipher.start(iv);
cipher.update(encrypted);
cipher.finish();
// outputs decrypted hex
console.log(cipher.output.toHex());
// generate a password-based 16-byte key
var salt = forge.random.getBytesSync(128);
var derivedKey = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
Where should I use my own key?
Where can I choose 256 bit mode?
Can you give me an easier example?
Where should I use my own key?
I haven't used that library but it seems pretty straight forward. Take this part at the top:
// generate a random key and IV
var key = forge.random.getBytesSync(16);
And put your key in like this:
// generate a random key and IV
var key = neverGuessMahKeyIs1234;
Do the same for the iv if you want.
Where can I choose 256 bit mode?
Ok, so first of all your dealing with symmetric encryption which has a key length of the desired size. Because it's symmetric, it's used on both the encrypting and decrypting ends, which is what the code that you posted seems to do. I say 'seems' because I'm trusting that the library's native functions are as you posted them.
So, the code as you posted seems to use (as I showed above) 128 bits (16*8=128). If you want a random 256, then just use:
var key = forge.random.getBytesSync(32);
Or just make your own key that 256 bits long.