In communication between two applications, I'd like to encrypt a piece of information in JavaScript and decrypt the message from an Objective-C client using a fixed key (just for basic security).
Encryption works well:
var command = "mjallo";
var crypto_key = CryptoJS.enc.Base64.parse('280f8bb8c43d532f389ef0e2a5321220');
var crypto_iv = CryptoJS.enc.Base64.parse("CC0A69779E15780A");
// Encrypt and encode
var encrypted = CryptoJS.AES.encrypt(command, crypto_key, {iv: crypto_iv}).toString();
var encrypted_and_encoded = btoa(encrypted);
// encrypted_and_encoded => 'dFBQVDZZS3dGSktoa0J3Y1NQOElpZz09'
// Confirms that decrypt works with CryptoJS:
// Decode and decrypt
var decrypted = CryptoJS.AES.decrypt(atob(encrypted_and_encoded), crypto_key, {iv: crypto_iv});
// decrypted => 'mjallo'
How would you go about decoding and decrypting the message in Objective-c after it was encrypted by CryptoJS?
I've attempted to decrypt using CocoaSecurity, but with no luck. Following is RubyMotion syntax:
begin
res = CocoaSecurity.aesDecryptWithBase64('dFBQVDZZS3dGSktoa0J3Y1NQOElpZz09', hexKey: '280f8bb8c43d532f389ef0e2a5321220', hexIv: 'CC0A69779E15780A')
rescue NSException => e
p e.reason # => "Length of iv is wrong. Length of iv should be 16(128bits)"
end
AES supports a block size of 128 bit and key sizes of 128, 192 and 256 bit. The IV for CBC mode (which is the default) should be 128 bit.
Your encoded key consists of 32 characters. In CryptoJS you're parsing it as Base64 which results in a 192 bit key, but in CocoaSecurity you're assuming that it is Hex encoded. Since it only contains digits and the letters a to f, it's likely Hex encoded and not Base64 encoded. If one would assume that it is Hex encoded, then one would get a valid AES key size of 128 bit:
var crypto_key = CryptoJS.enc.Hex.parse('280f8bb8c43d532f389ef0e2a5321220');
Your IV on the other hand doesn't have a valid size under the same assumption. An IV should be 16 bytes long for AES in CBC mode. Additionally, an IV should never be fixed at a static value. You would need to generate a random IV for every encryption. Since the IV doesn't have to be secret, you can send it along with the ciphertext.
var crypto_iv = CryptoJS.lib.WordArray.random(128/8);
console.log("IV: " + crypto_iv.toString()); // hex encoded
The result of CryptoJS.<Cipher>.encrypt()
is a special formattable object. If you call toString()
on that object, you will get a Base64 encoded ciphertext (optionally with a salt when password-based encryption was used). But then you're encoding it again with Base64 by calling btoa()
. You don't need to encode it twice.
var encrypted = CryptoJS.AES.encrypt(command, crypto_key, {iv: crypto_iv}).toString();
console.log("Ciphertext (Base64): " + encrypted.toString());
console.log("Ciphertext (Hex): " + encrypted.ciphertext.toString());
As far I can judge, your RubyMotion code looks fine.
If you can only change the CocoaSecurity code, then you will need to
You should always authenticate the ciphertexts. This can either be done with an authenticated mode like GCM or with an HMAC over the ciphertext.