AES 128 Cross-Platform Swift / C# - 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"))

Related

How to fix AES encrypt function adding \0\0\0 to the end of encrypted text?

When I decrypt an encrypted username using AES encryption it shows a bunch of "\0\0\0" at the end of the decrypted username. Is there a way to not add these when encrypting?
The example code of the encryption and decryption:
string ky = xx_Convert.Base64ToString("RllhSzNjM09PZFAwT2RYMkdxOFI2cG9HYjVmWVIybnQ=");
string iv = xx_Convert.Base64ToString("Y2pZQmJsdVlqdlBYM0RtcXVleDJkNGNTa0FIYjhYQ2Y=");
string username = "test#mail.com";
string encriptedUsername = xx_Crypto.EncryptAES(ky, iv, username);
string encriptedPassword = xx_Crypto.EncryptAES(ky, iv, "password");
string decryptedUsername = xx_Crypto.DecryptAES(ky, iv, encriptedUsername);
string decryptedPassword = xx_Crypto.DecryptAES(ky, iv, encriptedPassword);
This gives the following result for decryptedUsername when inspecting the variable: "test#mail.com\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
This is likely a result of the Padding property of the encryption function.
In the Encryption function the Padding is set to Padding = PaddingMode.Zeros but when I remove this line it still pads the result but with other invisible characters.
Encryption function:
public static string EncryptAES(string keyString, string ivString, string
text)
{
RijndaelManaged myRijndael = new RijndaelManaged()
{
Padding = PaddingMode.Zeros,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256
};
byte[] key = Encoding.UTF8.GetBytes(keyString);
byte[] IV = Encoding.UTF8.GetBytes(ivString);
ICryptoTransform encryptor = myRijndael.CreateEncryptor(key, IV);
byte[] encrypted = null;
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor,
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(text);
}
encrypted = msEncrypt.ToArray();
}
}
return (Convert.ToBase64String(encrypted));
}
Edit: As requested here is the Decryption function:
public static string DecryptAES(string keyString, string ivString, string text)
{
var myRijndael = new RijndaelManaged()
{
Padding = PaddingMode.Zeros,
Mode = CipherMode.CBC,
KeySize = 256,
BlockSize = 256
};
byte[] key = Encoding.UTF8.GetBytes(keyString);
byte[] IV = Encoding.UTF8.GetBytes(ivString);
ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV);
byte[] sEncrypted = Convert.FromBase64String(text);
byte[] fromEncrypt = new byte[sEncrypted.Length];
MemoryStream msDecrypt = new MemoryStream(sEncrypted);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.UTF8.GetString(fromEncrypt));
}
So is it possible to prevent this in the encryption function?
P.S: I did not use the same keys as in the original code
So is it possible to prevent this in the encryption function?
Yes, just use:
read = csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.UTF8.GetString(fromEncrypt, 0, read));
You are currently encoding the entire buffer. The padding bytes are removed but the bytes in the buffer are preset to zero during initialization when you created the buffer.
Note that zero padding isn't used much because if your plaintext ends with one or more zero bytes, those bytes may be removed. So some messages cannot be decrypted successfully this way (and cryptographers want to encrypt arbitrary messages, not just strings).

How to implement AES decryption from string encrypted with CryptoJS?

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?

Junk bytes before payload in C# UTF8 to AES to Base64 conversion

I have been trying to implement proper IV practice in methods to encrypt and decrypt a UTF-8 string with AES which is then returned as a Base64 string. Using this question as a reference, I have prepended the generated IV to the byte array before the Base64 conversion. I'm having an issue where the decrypt method returns the UTF-8 string with exactly fifty characters of random junk (encryption artifacts?). I don't believe the issue is with the encryption because the decrypt method does consistently return the encrypted string. I think the problem is with one of the other conversion steps but I'm having trouble seeing where this might be coming from. Any help would be wildly appreciated.
Encrypt method
public static string EncryptString(string input, string key)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = System.Convert.FromBase64String(key);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] rawData = Encoding.UTF8.GetBytes(input);
// IV is the 16 byte AES Initialization Vector
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
using (var ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length); // aes.IV.Length should be 16
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(rawData, 0, rawData.Length);
cs.FlushFinalBlock();
}
byte[] encryptedData = ms.ToArray();
// this will hold the IV prepended to the encrypted data
byte[] output = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, output, aes.IV.Length); // save the iv
Array.Copy(encryptedData, 0, output, aes.IV.Length, encryptedData.Length); // save the data
// now encode the whole thing as base 64
return System.Convert.ToBase64String(output);
}
}
}
}
Decrypt method
public static string DecryptString(string input, string key)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = Convert.FromBase64String(key);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] rawData = Convert.FromBase64String(input);
byte[] IV = new byte[16]; // aes.IV.Length should be 16
Array.Copy(rawData, IV, IV.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, IV), CryptoStreamMode.Write))
{
using (var binaryWriter = new BinaryWriter(cs))
{
binaryWriter.Write(rawData,IV.Length ,rawData.Length - IV.Length);
}
}
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
My test
static void Main(string[] args)
{
string payload = "My super secret string";
string key = "tR4mPn7mBQ8G6HWusyFnGk/gqdd/enWiUTr7YbhNrJg=";
Console.WriteLine(payload);
Console.WriteLine(key);
Console.WriteLine("");
string encrypted = EncryptString(payload, key);
Console.WriteLine(encrypted);
Console.WriteLine("");
string decrypted = DecryptString(encrypted, key);
Console.WriteLine(decrypted);
Console.WriteLine(decrypted.Length.ToString() + " " + encrypted.Length.ToString());
Console.ReadKey();
}
Edit to add - this is an example of the output:
�XQ��=F�]�D�?�My super secret string
You are writing the IV to the output twice in EncryptString. First you have:
ms.Write(aes.IV, 0, aes.IV.Length); // aes.IV.Length should be 16
which is the start of encryptedData. You then copy the IV and encryptedData (which already includes the IV) into a new byte array:
// this will hold the IV prepended to the encrypted data
byte[] output = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, output, aes.IV.Length); // save the iv
Array.Copy(encryptedData, 0, output, aes.IV.Length, encryptedData.Length); // save the data
This doubling of the IV is what is causing the extra bytes.
You don’t need to do the second copying. Just convert encryptedData to base 64 directly and return that:
return System.Convert.ToBase64String(encryptedData);

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 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