How to implement AES decryption from string encrypted with CryptoJS? - c#

I am trying to decrypt Aes that was created from the JavaScript AES Encryptor from the CryptoJS library. I am trying to read a string that was encrypted by that library and decrypt it within my C# project using the following:
public static string Decrypt(string message, string secret, string salt = "zAvR2NI87bBx746n") {
return Encoding.UTF8.GetString(AESDecryptBytes(
Encoding.UTF8.GetBytes(message),
Encoding.UTF8.GetBytes(secret),
Encoding.UTF8.GetBytes(salt)
));
}
private static byte[] AESDecryptBytes(byte[] cryptBytes, byte[] passBytes, byte[] saltBytes) {
byte[] clearBytes = null;
// create a key from the password and salt, use 32K iterations
// var key = new Rfc2898DeriveBytes(passBytes, saltBytes, 32768);
var key = new Rfc2898DeriveBytes(passBytes, saltBytes, 1000);
using (Aes aes = new AesManaged()) {
// set the key size to 256
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
using (MemoryStream ms = new MemoryStream()) {
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write)) {
cs.Write(cryptBytes, 0, cryptBytes.Length);
cs.Close();
}
clearBytes = ms.ToArray();
}
}
return clearBytes;
}
The data was encrypted in JavaScript as follows:
// data is a JSON string
// gs.cryptoSecret is a string of random characters
let saveData = CryptoJS.AES.encrypt(data || '', gs.cryptoSecret).toString()
When I attempt to use my method to decrypt the data I get the following error message:
CryptographicException: Invalid input block size.
Which is triggered by cs.Close();. It could be that secret hasn't been implemented, which I am not exactly sure where that would go in this...
How can I implement this decryptor to work alongside the JavaScript Library?

Related

256 AES Encryption C# .Net Core not working as expected

I have a couple of projects in different languages that encrypt/decrypt string, for example, a php project can encrypt a string and store in a database and a c# project should be able to pull it out of the database and decrypt it.
I have a c# .net core project which I've successfully made the decryption method to decrypt string that are encrypted from other projects, but I now need a method that also does the encryption.
I have implemented a method which isn't throwing any errors but the encryption string it returns is completely wrong.
Below is my implementation of the encryption
public string encrypt(string decryptedString)
{
string encrypted;
byte[] key = Encoding.UTF8.GetBytes(CipherKey);
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key;
aes.IV = Encoding.UTF8.GetBytes(CipherIv);
aes.Mode = CipherMode.CBC;
ICryptoTransform decipher = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Write))
{
using (StreamWriter sr = new StreamWriter(cs))
{
sr.Write(decryptedString);
var encryptedString = ms.ToArray();
var result = new byte[aes.IV.Length + encryptedString.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(encryptedString, 0, result, aes.IV.Length, encryptedString.Length);
return Convert.ToBase64String(result);
}
}
}
}
And the equivalent method to decrypt (the decryption works fine) is below
public string decrypt(string encryptedPassword, bool throwException = false)
{
try
{
string decrypted;
byte[] key = Encoding.UTF8.GetBytes(CipherKey);
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key;
aes.IV = Encoding.UTF8.GetBytes(CipherIv);
aes.Mode = CipherMode.CBC;
byte[] base64decodeBytes = System.Convert.FromBase64String(encryptedPassword);
string base64Decode = Encoding.UTF8.GetString(base64decodeBytes);
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(base64decodeBytes))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
return decrypted;
}
catch (Exception ex)
{
return encryptedPassword;
}
}
UPDATE
From the advice in the comment by 500 - Internal Server Error I have made a change which seems to have resolved the issue somewhat but I am now getting junk at the start of the string in the decryption.
For example I am doing the following to test:
string encryptionString = encryption.encrypt("Hello World");
Console.WriteLine("Encrypted String: {0}", encryptionString);
string decodedString = encryption.decrypt(encryptionString);
Console.WriteLine("Decrypted String: {0}", decodedString);
Encrypted String: Z3BmenhibnNmZ1tqa2xhZgs/GYMu49dNPNGoRROTWz4=
Decrypted String: �u�D'���);�
SKHHello World

IV of first 16 bytes gets remove from decrypted string? C#/Python3

I was wondering why the first 16 bytes of all my strings being encrypted, then when being decrypted are missing and how to fix this if it is possible. I am encrypting like so in c#
public static string EncryptString(string b_key, string plainText)
{
byte[] iv = new byte[16];
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(b_key);
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
{
streamWriter.Write(plainText);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
and decrypting in python3 like so
enc = base64.b64decode(self.text)
iv = enc[:16]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
plain_text = cipher.decrypt(enc[16:])
plain_text = self.dePKCS7_padding(plain_text)
return plain_text
Is readding the first 16 bytes possible? or must be used for encryption. I also want it to crypto safe but the first 16 bytes are kind of important is this possible? anyway to get around this in either c# or python3?
Based on the discussion in comments and inputs from #MichaelFehr and #user9014097, I came up with the following code.
In this code the IV of AES will have random value created when AES.Create() is called. And the same will be used in the outcome of the encrypted value.
The decryptString method will capture the iv value from the incoming encrypted string and assign it to AES while decrypting the string.
public static string EncryptString(string b_key, string plainText)
{
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(b_key);
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
// Adding aes.IV to the stream's start.
memoryStream.Write(aes.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write(plainText);
}
}
array = memoryStream.ToArray();
}
}
// The final encrypted outcome will be aes.IV+encryptedtext.
return Convert.ToBase64String(array);
}
public static string DecryptString(string key, string cipherText)
{
//input is iv+encrypted text, convert them to byte array.
byte[] buffer = Convert.FromBase64String(cipherText);
// byte array for iv
byte[] iv = new byte[16];
// byte array for rest of the cipher text.
byte[] cipherBuffer = new byte[buffer.Length - 16];
// copy first 16 bytes from the cipher text to iv.
Buffer.BlockCopy(buffer, 0, iv, 0, 16);
// copy rest of the cipher text to the cipher buffer to be decrypted.
Buffer.BlockCopy(buffer, 16, cipherBuffer, 0, buffer.Length - 16);
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherBuffer))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader(cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I have following assumption in writing above code.
Length of IV is 16.
Python code (shared above) does not need split the input text based on some specific character. It takes first 16 bytes as IV value and rest of the bytes as cipher text.
I was able to encrypt and decrypt values successfully in C# using above methods.
I was not able to decrypt the value in python code as I have little to no idea on how to work with python.
You can test the outcome of above encryption in python to decrypt it. Let me know if it doesn't work as expected.
I hope this will help you solve your issue.

AES256 JAVA encryption doesn't match C# encryption

I have been requested to encrypt some data while talking to my partner's JAVA API, and he sent me the following details about encryption algorithm:
Algorithm : AES256
Key Size : 256 bits
Encryption Mode: CBC (16 bits blocks, PKCS5Padding with 0)
Output Type : Base-64
Password: 0xA8703827AE586460105696504327B7BB0806FEAE96BD664F89E36868FBB48E3D
IV: is a byte[16] with 0 values
I used the below code, but I didn't get a matched result with him:
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[16] { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.KeySize = 256;
aes.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public string EncryptText(string input, string password)
{
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
So, when we tried to encrypt Hello, How are you?, I got a different result and I should get the same result he had as he will decrypt my sent data on his side and will process it. The given example should have this result: TJTojNoVgoqnhCj4uTv1jLBiZU7r+s/0Bm234bHU+S0=
I did some testing and now able to match your expected result.
2 changes to be done.
IV
IV is the easiest, as you said IV = 0, so set IV as follows:
aes.IV = new byte[16];
In AES, IV is 16 bytes. The above would create a byte array of 16 bytes with each value initialized to zero.
Key
The password you have given starts with "0x" - this essentially means that this is hexadecimal representation of the password string. I converted this password to byte array using this
string password = "A8703827AE586460105696504327B7BB0806FEAE96BD664F89E36868FBB48E3D";
Please note I removed the starting "0x" from the above
byte[] passwordBytes = StringToByteArray(password);
The above converts the hexadecimal password representation to a byte array.
In your AES_Encrypt method, directly assign this byte[] as the Key
aes.Key = passwordBytes;
Now, my result is TJTojNoVgoqnhCj4uTv1jLBiZU7r+s/0Bm234bHU+S0= which exactly matches with your expected output.

AES 128 Cross-Platform Swift / C#

I've been trying to encrypt and decrypt on both iOS and .NET but I haven't been very successful. I've used this question but I get the error:
Specified initialisation vector (IV) does not match the block size for this algorithm.
Here's my encryption code for Swift using CryptoSwift:
let encrypt = try! "oauth_token".AES_encrypt("my key here (is 32 characters long)", iv: "1234567890123456")
func AES_encrypt(key: String, iv: String) throws -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)
let enc = try AES(key: key, iv: iv, blockMode:.CBC).encrypt(data!.arrayOfBytes(), padding: PKCS7())
let encData = NSData(bytes: enc, length: Int(enc.count))
let base64String: String = encData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0));
let result = String(base64String)
return result
}
And my decryption code for .NET:
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] key, byte[] iv)
{
byte[] decryptedBytes = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = key;
AES.IV = iv;
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
byte[] encrypted_text = Convert.FromBase64String("secret token");
byte[] key = Convert.FromBase64String("32 character key");
byte[] iv = Convert.FromBase64String("0123456789012345");
string plaintext = Convert.ToBase64String(AES_Decrypt(encrypted_text, key, iv));
The block size is 16 bytes (AES.blockSize). Either you're using old version or your AES_encrypt() have some problem (AES_encrypt is not part of CryptoSwift).
Simple example from README:
let input: NSData // data to encrypt
let encrypted = try? input.encrypt(AES(key: "secret0key000000", iv:"0123456789012345"))
or this
// Encrypt string and get Base64 representation of result
let base64: String = try? "my secret string".encrypt(AES(key: "secret0key000000", iv: "0123456789012345"))

AES encryption with Crypto-JS and C# Decryption - avoiding "Padding is invalid and cannot be removed."

I am busy creating a Javascript application which integrates with our client's existing C# services.
One of the requirements is to send AES encrypted data, which then is decrypted and used on the server.
However, I cannot send "valid" data, the server always responds with "Padding is invalid and cannot be removed."
Here are their C# Encrypt and Decrypt implementations (this cannot be changed, as they have various subsystems dependent on this:
public static string Encrypt(string input, string password)
{
byte[] utfData = Encoding.UTF8.GetBytes(input);
byte[] saltBytes = Encoding.UTF8.GetBytes(password);
string encryptedString = string.Empty;
using (var aes = new AesManaged())
{
var rfc = new Rfc2898DeriveBytes(password, saltBytes);
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
aes.Key = rfc.GetBytes(aes.KeySize/8);
aes.IV = rfc.GetBytes(aes.BlockSize/8);
using (ICryptoTransform encryptTransform = aes.CreateEncryptor())
{
using (var encryptedStream = new MemoryStream())
{
using (var encryptor =
new CryptoStream(encryptedStream, encryptTransform, CryptoStreamMode.Write))
{
encryptor.Write(utfData, 0, utfData.Length);
encryptor.Flush();
encryptor.Close();
byte[] encryptBytes = encryptedStream.ToArray();
encryptedString = Convert.ToBase64String(encryptBytes);
}
}
}
}
return encryptedString;
}
public static string Decrypt(string input, string password)
{
byte[] encryptedBytes = Convert.FromBase64String(input);
byte[] saltBytes = Encoding.UTF8.GetBytes(password);
string decryptedString = string.Empty;
using (var aes = new AesManaged())
{
var rfc = new Rfc2898DeriveBytes(password, saltBytes);
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
aes.Key = rfc.GetBytes(aes.KeySize/8);
aes.IV = rfc.GetBytes(aes.BlockSize/8);
using (ICryptoTransform decryptTransform = aes.CreateDecryptor())
{
using (var decryptedStream = new MemoryStream())
{
var decryptor =
new CryptoStream(decryptedStream, decryptTransform, CryptoStreamMode.Write);
decryptor.Write(encryptedBytes, 0, encryptedBytes.Length);
decryptor.Flush();
decryptor.Close();
byte[] decryptBytes = decryptedStream.ToArray();
decryptedString =
Encoding.UTF8.GetString(decryptBytes, 0, decryptBytes.Length);
}
}
}
return decryptedString;
}
I am using CryptoJS 3.1.2. eg
var encrypted = CryptoJS.AES.encrypt(input, password).toString();
how do I essentially write an equivalent to their "Encrypt()" using CryptoJS
CryptoJS documentation is severely lacking in depth, so it is hard to know what to expect without trying. It is pretty clear though that using the password as salt is not a secure nor standard way to handle salt. So you will have to call the PBKDF2 function yourself, create a key and IV yourself. You also need to create the PBKDF2 in CryptoJS with SHA-1 instead of SHA-256. SHA-256 seems to be the - again undocumented - default in CryptoJS.
The only way to do this is to step through the code, and compare each (binary) value for both the PBKDF2 and AES functions. Please convert to hexadecimals to make a good comparison.

Categories

Resources