CryptoJS not decrypting non-Latin characters faithfully - javascript

I am trying to use CryptoJS AES, like so:
var msg = "café";
var key = "something";
var c = CryptoJS.AES.encrypt(msg, key).toString();
CryptoJS.AES.decrypt(c, key).toString(CryptoJS.enc.Latin1);
Unfortunately this returns café, not café. Clearly Latin1 is not the right encoding to use, but I can't find a better one. Is there a solution?
Thanks.

You are just missing the format
The proper way is using CryptoJS.enc.Utf8
So, Please try:
CryptoJS.AES.decrypt(c, key).toString(CryptoJS.enc.Utf8);

https://code.google.com/p/crypto-js/#The_Hasher_Input
The hash algorithms accept either strings or instances of CryptoJS.lib.WordArray [...] an array of 32-bit words. When you pass a string, it's automatically converted to a WordArray encoded as UTF-8.
So, when you pass a string (and don't use CryptoJS.enc.* to generate a WordArray) it automatically converts the string (message) to a utf8 WordArray.
See here for sample roundtrip encrypt/decrypt:
https://code.google.com/p/crypto-js/#The_Cipher_Output
Here's a jsfiddle to play with CryptoJS
https://jsfiddle.net/8qbf4746/4/
var message = "café";
var key = "something";
var encrypted = CryptoJS.AES.encrypt(message, key);
//equivalent to CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(message), key);
var decrypted = CryptoJS.AES.decrypt(encrypted, key);
$('#1').text("Encrypted: "+encrypted);
$('#2').text("Decrypted: "+decrypted.toString(CryptoJS.enc.Utf8));
To emphasize my point here is the same thing using Latin1 encoding:
https://jsfiddle.net/3a8tf48f/2/
var message = "café";
var key = "something";
var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Latin1.parse(message), key);
var decrypted = CryptoJS.AES.decrypt(encrypted, key);
$('#1').text("Encrypted: " + encrypted);
$('#2').text("Decrypted: " + decrypted.toString(CryptoJS.enc.Latin1));
On a side note, the API would probably be better if it only accepted WordArray and didn't overload the toString method (which is just a convenience interface to CryptoJS.enc.*.stringify). The string conversion magic is a little misleading.

You are trying to decrypt your data as a Latin1 string, even though your input string is not in Latin1. The encoding used by CryptoJS internally is not the same as the encoding you use to write the input file.
You need to specify the same encoding both when encrypting (for the string -> byte array conversion) and when decrypting (for the byte array -> string conversion).

Related

Why won't window.btoa work on – ” characters in Javascript?

So I'm converting a string to BASE64 as shown in the code below...
var str = "Hello World";
var enc = window.btoa(str);
This yields SGVsbG8gV29ybGQ=. However if I add these characters – ” such as the code shown below, the conversion doesn't happen. What is the reason behind this? Thank you so much.
var str = "Hello – World”";
var enc = window.btoa(str);
btoa is an exotic function in that it requires a "Binary String", which is an 8-bit clean string format. It doesn't work with unicode values above charcode 255, such as used by your em dash and "fancy" quote symbol.
You'll either have to turn your string into a new string that conforms to single byte packing (and then manually reconstitute the result of the associated atob), or you can uri encode the data first, making it safe:
> var str = `Hello – World`;
> window.btoa(encodeURIComponent(str));
"SGVsbG8lMjAlRTIlODAlOTMlMjBXb3JsZA=="
And then remember to decode it again when unpacking:
> var base64= "SGVsbG8lMjAlRTIlODAlOTMlMjBXb3JsZA==";
> decodeURIComponent(window.atob(base64));
"Hello – World"
The Problem is the character ” lies outside of Latin1 range.
For this you can use unescape (now deprecated)
var str = "Hello – World”";
var enc = btoa(unescape(encodeURIComponent(str)));
alert(enc);
And to decode:
var encStr = "SGVsbG8g4oCTIFdvcmxk4oCd";
var dec = decodeURIComponent(escape(window.atob(encStr)))
alert(dec);
This ultimately owes to a deficiency in the JavaScript type system.
JavaScript strings are strings of 16-bit code units, which are customarily interpreted as UTF-16. The Base64 encoding is a method of transforming an 8-bit byte stream into a string of digits, by taking each three bytes and mapping them into four digits, each covering 6 bits: 3 × 8 = 4 × 6. As we see, this is crucially dependent on the bit width of each symbol.
At the time the btoa function was defined, JavaScript had no type for 8-bit byte streams, so the API was defined to take the ordinary 16-bit string type as input, with the restriction that each code unit was supposed to be confined to the range [U+0000, U+00FF]; when encoded into ISO-8859-1, such a string would reproduce the intended byte stream exactly.
The character – is U+2013, while ” is U+201D; neither of those characters fits into the above-mentioned range, so the function rejects it.
If you want to convert Unicode text into Base64, you need to pick a character encoding and convert it into a byte string first, and encode that. Asking for a Base64 encoding of a Unicode string itself is meaningless.
The most bullet proof way is to work on binary data directly.
For this, you can encode your string to an ArrayBuffer object representing the UTF-8 version of your string.
Then a FileReader instance will be able to give you the base64 quite easily.
var str = "Hello – World”";
var buf = new TextEncoder().encode( str );
var reader = new FileReader();
reader.onload = evt => { console.log( reader.result.split(',')[1] ); };
reader.readAsDataURL( new Blob([buf]) );
And since the Blob() constructor does automagically encode DOMString instances to UTF-8, we could even get rid of the TextEncoder object:
var str = "Hello – World”";
var reader = new FileReader();
reader.onload = evt => { console.log( reader.result.split(',')[1] ); };
reader.readAsDataURL( new Blob([str]) );

Java and javascript generates different output for Base64 Hmac SHA256

When I tried to create similar function in Java and javascript which outputs a Base64 string of a Hmac SHA 256 encryption, the output given is not the same.
Javascript
var dataToSign = "message";
var secret = "secret";
function generateAuthHeader(dataToSign){
var hash = CryptoJS.HmacSHA256(dataToSign,secret);
return hash.toString(CryptoJS.enc.Base64);
which outputs
+eZuF5tnR65UEI+C+K3os8Jddv0wr95sOVgixTAZYWk=
Java
String key="secret";
String dataToSign = "message";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
System.out.println(Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(dataToSign.getBytes("UTF-8"))));
which outputs
i19IcCmVwVmMVz2x4hhmqbgl1KeU0WnXBgoDYFeWNgs=
is there errors in my code?
What's the value of hash in the JavaScript version and what's the value of sha256_HMAC.doFinal(dataToSign.getBytes("UTF-8")) in the Java version?
Also, can you please try getting the Base64 repr in JavaScript:
CryptoJS.enc.Base64.stringify(hash);
instead of hash.toString ?
Also UTF-8 shouldn't make a difference in the Java version, but I'd try to remove those and simply do getBytes() everywhere.

Base64 encoding of String in Node.js

I try to encode a string in both browser and server but I have different base64 encoding for a specific string
Here is my string: "£aº©S=³hPó c¨¸" (Hexa: 00a3006100ba00a900940053003d00b30068005000f300900020006300a800b8 )
Client-side: I encode this String using btoa() and I have : o2G6qZRTPbNoUPOQIGOouA== and this is the result I expect.
Server-side: I code this String using Buffer according to this answer in Node.js i have :
var ciphertext = ... // myString
console.log(ciphertext.hexEncode()); // 00a3006100ba00a900940053003d00b30068005000f300900020006300a800b8
console.log(Buffer.from(ciphertext, 'utf8').toString('base64')) // wqNhwrrCqcKUUz3Cs2hQw7PCkCBjwqjCuA==
console.log(Buffer.from(ciphertext, 'ucs2').toString('base64')) // owBhALoAqQCUAFMAPQCzAGgAUADzAJAAIABjAKgAuAA=
I managed to obtain the base64 encoding I expect using the node-package base-64
I still don't know why, so if someone has a clue
var base64 = require('base-64');
var ciphertext = ...; //myString
var encoded = base64.encode(bytes);
console.log(encoded); // o2G6qZRTPbNoUPOQIGOouA==

MD5.ComputeHash(Encoding.Unicode.GetBytes(value)) into javascript

I need to translate the line below from vb.net to javascript
MD5.ComputeHash(Encoding.Unicode.GetBytes(value))
Im trying to use CryptoJS but I get diffrent results as I need to pass a string into that but a byte array into the MD5 function in VB.net
Can anyone help?
Thank you
Encoding.Unicode is a (misleading) name used by Windows for the UTF-16LE encoding.
However the CryptoJS functions, when given a string, encode it to bytes using the (more common) UTF-8, not UTF-16LE:
The hash algorithms accept either strings or instances of CryptoJS.lib.WordArray. A WordArray object represents an array of 32-bit words. When you pass a string, it's automatically converted to a WordArray encoded as UTF-8.
So you will need to create a WordArray from the string yourself before passing it in to MD5. With a new enough CryptoJS there's a function to do that for you:
CryptoJS.MD5(CryptoJS.enc.Utf16LE.parse(str))
IN C#:
var data = md5.ComputeHash(Encoding.Default.GetBytes(password));
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++) {
stringBuilder.Append(data[i].ToString("x2"));
}
return stringBuilder.ToString();
In Node.js
const crypto = require('crypto');
crypto.createHash('md5').update(value).digest('hex');

What is DigestUtils sha256 encoding equivalent from JavaScript?

I call following and create the password hash.
ByteString password = ByteString.copyFrom(DigestUtils.sha256("mypassword"));
But now I need to send the sha256 converted password message from client (JavaScript). I tired to use CryptoJS as following
var pass = CryptoJS.SHA256(document.getElementById('password').value);
var passhash = pass.toString(CryptoJS.enc.Latin1)
login(passhash);
I tried all Base64, Latin1, and Hex types to get the string. But it will not produce the same password as the one in Java
Problem was with character encoding. Following fixed the problem.
in JS:
var password = pass.toString(CryptoJS.enc.Utf16);
In Java:
byte[] passhash = jsCryptoString.getBytes("UTF-16BE");

Categories

Resources