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)
Related
Currently on my SAPUI5 project, I am creating a HMAC encoded string with this line of code:
var secretKey = CryptoJS.enc.Hex.parse('SECRETKEY'); //USING THE CRYPTOJS LIBRARY!
var hash = CryptoJS.HmacSHA256('abc', secretKey);
hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
which gives eZdbNMwgWKOANEiozokNG2FGfzI7Yy/B8IQKXr3+krY=
I am using the CryptoJS library to execute this code in UI5.
However the problem is that I am receiving the wrong HMAC encoded string when I want to do the same in ABAP. After testing a few times, it seems like the encoding (in abap) is wrong before the HMAC is calculated.
Is there a function module that does 'CryptoJS.enc.Hex.parse()' - after googling what it does it interprets the parameter as encoded and converts it into a word array:
DATA:
lv_sign_key_x TYPE xstring,
lv_hmac_result TYPE string.
DATA(lv_binary_secret) = cl_abap_hmac=>string_to_xstring('SECRETKEY').
cl_abap_hmac=>calculate_hmac_for_char(
EXPORTING
if_algorithm = 'SHA256' "Hash Algorithm
if_key = lv_binary_secret "HMAC Key
if_data = 'abc' "Data
IMPORTING
ef_hmacb64string = lv_hmac_result "HMAC value as base64-encoded string
).
which gives 9dyEZn5G+uiRwsNqgY5S6k9/gmCheFNF4vFa5qBKK1w=
Shows exact way to encode to hex and calculate to HMAC256. Link
data lv_binary_secret type xstring.
data lv_string type string value '48656c6c6f2c20576f726c6421'.
translate lv_string to upper case.
lv_binary_secret = lv_string.
cl_abap_hmac=>calculate_hmac_for_char(
EXPORTING
if_algorithm = 'SHA256' "Hash Algorithm
if_key = lv_binary_secret "HMAC Key
if_data = 'abc' "Data
IMPORTING
ef_hmacb64string = data(lv_hmac_result) "HMAC value as base64-encoded string
).`enter code here`
I'm trying to decrypt a C# encrypt string using javascript,
This is an example of the encryption on my server side
public class AesCrypt
{
public static string IV = #"!QAZ2WSX#EDC4RFV";
public static string Key = #"5TGB&YHN7UJM(IK<5TGB&YHN7UJM(IK<";
public static string Encrypt(string dectypted)
{
byte[] textbytes = ASCIIEncoding.ASCII.GetBytes(dectypted);
AesCryptoServiceProvider encdec = new AesCryptoServiceProvider();
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
encdec.Padding = PaddingMode.PKCS7;
encdec.Mode = CipherMode.CBC;
ICryptoTransform icrypt = encdec.CreateEncryptor(encdec.Key, encdec.IV);
byte[] enc = icrypt.TransformFinalBlock(textbytes, 0, textbytes.Length);
icrypt.Dispose();
return Convert.ToBase64String(enc);
}
}
The encryption of "Hello World" is "1i4zI5rB3Df2CYFalsiTwg=="
Now I'm trying to decrypt it using js on my client and get Hello World and this is where I fail,
I'm using <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script> in order to decrypt and I watch some of examples over the web (including stackoverflow).
Accoring to some examples over the web this is what I came out of, but it's not returning "Hello World" back.
data = "1i4zI5rB3Df2CYFalsiTwg==";
key = "5TGB&YHN7UJM(IK<5TGB&YHN7UJM(IK<";
iv = "!QAZ2WSX#EDC4RFV";
CryptoJS.AES.decrypt(atob(data), key, {
iv: atob(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
I'm using the same mode and padding, but I'm probably missing something.
I'm not that familiar with CryptoJS and I hope someone can help me understand where I went wrong.
Thanks in advance
From the old CryptoJS pages, the Cipher input part:
For the key, when you pass a string, it's treated as a passphrase and used to derive an actual key and IV. Or you can pass a WordArray that represents the actual key. If you pass the actual key, you must also pass the actual IV.
Although you are passing an IV, you do currently put in a string rather than a binary key as word array. I suppose this is the problem as I don't see any other obvious programming mistakes.
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 });
Here is my solution to PHP, Ruby & Swift.
I faced issues when using CryptoJS on my test.
my code is like this
var data = "Hello World";
var key = "57119C07F45756AF6E81E662BE2CCE62";
var iv = "GsCJsm/uyxG7rBTgBMrSiA==";
var encryptedData = CryptoJS.AES.encrypt(data,
CryptoJS.enc.Hex.parse(key), {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Base64.parse(iv)
}
);
console.log("encryptedData: " + encryptedData);
// var crypttext = encryptedData.toString();
var crypttext = "k4wX2Q9GHU4eU8Tf9pDu+w==";
var decryptedData = CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Base64.parse(crypttext)
}, CryptoJS.enc.Hex.parse(key), {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Base64.parse(iv)
});
console.log("decryptedData: " + decryptedData);
console.log result
encryptedData: 97SwKfGtNARERiSYyZxdAQ==
decryptedData:
I've looked at your PHP code. You're using a 32 character key which is obviously Hex-encoded, but instead of decoding it to bytes, you're using the characters directly. Therefore the aes-256-cbc cipher is also wrong.
If you don't want to change your misleading PHP code, you can simply make the same mistake in CryptoJS: CryptoJS.enc.Utf8.parse(key) instead of CryptoJS.enc.Hex.parse(key).
Security considerations:
The IV must be unpredictable (read: random). Don't use a static IV, because that makes the cipher deterministic and therefore not semantically secure. An attacker who observes ciphertexts can determine when the same message prefix was sent before. The IV is not secret, so you can send it along with the ciphertext. Usually, it is simply prepended to the ciphertext and sliced off before decryption.
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 trying to emulate the following .NET code in javascript using the Crypto.js library.
var hashInput = "public=ID1000000001::routetype=POST::route=personsearch::key1=ID1000000001::key2=1043"
byte[] inputBytes = new byte[hashInput.Length * sizeof(char)];
System.Buffer.BlockCopy(hashInput.ToCharArray(), 0, inputBytes,0,inputBytes.Length);
byte[] keyBytes = HexadecimalStringToByteArray("A_HEX_STRING_GOES_HERE");
var hmac = HMACSHA256.Create();
hmac.Key = keyBytes;
var clientHash = hmac.ComputeHash(inputBytes);
This gives me a ByteArray which is used as part of POST to a WebAPI in the form of
[41,197,254,91,244,87.....] etc.
I want to make the same exact byte array in a javascript client but i am having diffculty getting this. I have tried the following:
var stringToHash = 'public=ID1000000001::routetype=POST::route=personsearch::key1=ID1000000001::key2=1043';
var privateKey = 'A_HEX_STRING_GOES_HERE';
var hash = CryptoJS.HmacSHA256(stringToHash, privateKey);
//this results in a WordArray, which can be converted to many types
//however i cannot get the byte array as in the .net example
//i.e. i just want to get [41,197,254,91,244,87....] etc.
I can see on the documentation for Crypto.js how to convert to base64, and other formats, but not to the ByteArray which i need.
Any ideas?
--UPDATE
Thanks for the advice on not using BlockCopy, and also for pointing me in the direction of the encoding issues which i had completely neglected.
This was part of the issue. The other part was that i had managed to misuse the HMACSHA256 class. I found (after several, several hours) that the .NET code was not producing the correct hash value.
It turns out this code DID produce the correct Hash:
var hashInput = "a::string::to::hash";
var privateKey = "C0B615950F9D577A4EAF64C9B4F2E50F3DA2C6BB6F790FA346E9732788B29A08AC5444F1B82984DB190A75D3861CC4802D598EBF0025FD1C327928F43EB1C80E";
byte[] inputBytes = Encoding.UTF8.GetBytes(hashInput);
byte[] keyBytes = Encoding.UTF8.GetBytes(privateKey);
HMACSHA256 hmac = new HMACSHA256(keyBytes);
hmac.Initialize();
var clientHash = hmac.ComputeHash(inputBytes);
var base64 = Convert.ToBase64String(clientHash);
Lucky for me my WebAPI is not live yet!
The problem is probably the character encoding. You should use for instance the method encode_utf8(stringValue) in JavaScript and new UTF8Encoding.getBytes(String value) in .NET, to make sure that the character encodings match. Note that modern cryptography is based on bytes, not on strings or characters. Using System.Buffer.BlockCopy() to encode characters is really bad practice.