I need to take working JavaScript that decrypts a message and convert it into C#. I have the decryption information (the “decrypt” variable below) which looks like: AES-128:<salt>:<iv>:<key>
. Here’s the JavaScript:
function decodeString(message, decrypt) { var parts = decrypt.split(':', 4); var salt = CryptoJS.enc.Hex.parse(parts[1]); var iv = CryptoJS.enc.Hex.parse(parts[2]); var key = CryptoJS.PBKDF2(parts[3], salt, { keySize: 128/32, iterations: 100 }); try { message = message.replace(/s+/g, ''); var d = CryptoJS.AES.decrypt(message, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); message = d.toString(CryptoJS.enc.Utf8); } catch (e) { console.error("Encountered a problem decrypting and encrypted page!"); console.log(e); } return(message); }
Here’s what I have in C#, but I get an exception on the CreateDecryptor call.
using System.Security.Cryptography; private string DecodeString(string message, string decrypt) { string[] parts = decrypt.ToString().Split(':'); byte[] salt = Encoding.UTF8.GetBytes(parts[1]); byte[] iv = Encoding.UTF8.GetBytes(parts[2]); var pbkdf2 = new Rfc2898DeriveBytes(parts[3], salt, 100); int numKeyBytes = 128; // Not sure this is correct byte[] key = pbkdf2.GetBytes(numKeyBytes); string plainText = null; using (AesManaged aes = new AesManaged()) { aes.KeySize = numKeyBytes; // Not sure if this is correct aes.BlockSize = 128; // Defaults to 128, but not sure this is correct aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; try { // The below line has the following exception: // The specified key is not a valid size for this algorithm. // Parameter name: key using (var decrypter = aes.CreateDecryptor(key, iv)) using (var plainTextStream = new MemoryStream()) { using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write)) using (var binaryWriter = new BinaryWriter(decrypterStream)) { string encryptedText = Regex.Replace(message, @"s+", ""); binaryWriter.Write(encryptedText); } byte[] plainTextBytes = plainTextStream.ToArray(); plainText = Encoding.UTF8.GetString(plainTextBytes); } } catch (Exception ex) { log.Error("Unable to decrypt message.", ex); } return plainText; } }
Any suggestions would be appreciated!
Answer
In the C# code there are the following issues:
- Salt and IV must be hex decoded (and not UTF8 encoded).
numKeyBytes
specifies the key size in bytes and is therefore 16 (and not 128) for AES-128.aes.KeySize
specifies the key size in bits and is thereforenumKeyBytes * 8
(and notnumKeyBytes
), but can alternatively be omitted.- For
aes.BlockSize
,aes.Mode
andaes.Padding
the default values are used (128, CBC, PKCS7), so they do not need to be specified explicitly. encryptedText
must be Base64 decoded.
A possible implementation is:
private string Decrypt(string message, string decrypt) { string[] parts = decrypt.ToString().Split(':'); byte[] salt = StringToByteArray(parts[1]); // Hex decode salt byte[] iv = StringToByteArray(parts[2]); // Hex dedoce IV var pbkdf2 = new Rfc2898DeriveBytes(parts[3], salt, 100); int numKeyBytes = 16; // AES-128 key size in bytes: 16 byte[] key = pbkdf2.GetBytes(numKeyBytes); string plainText = null; using (AesManaged aes = new AesManaged()) { aes.KeySize = 192; try { string encryptedText = Regex.Replace(message, @"s+", ""); using (var decrypter = aes.CreateDecryptor(key, iv)) using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(encryptedText))) // Base64 decode ciphertext { using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decrypter, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) { plainText = srDecrypt.ReadToEnd(); } } } } catch (Exception ex) { Console.WriteLine("Unable to decrypt message.", ex); } return plainText; } }
where StringToByteArray()
is from here.
I changed the Stream part to be analogous to this example, but the original implementation works as well, so this change is optional.
Test: Both codes return for the data
message = "YhyXEjjNAnRUUONwVzlha59tRoWkeEwTkOtSKOicRd/iBKkGgIp+DeWmvEXxAU53"; decrypt = "AES-128:30313233343536373839303132333435:35343332313039383736353433323130:my passphrase";
the plaintext:
The quick brown fox jumps over the lazy dog