AES-ECB block-decryption in C# - c#

I wrote code that decrypts the input string completely. But since this is an ECB mode, I wanted to somehow decrypt not the entire input text, but only a separate block of it.
As far as I understand, ECB AES encrypts in blocks of 8 bytes. How can I add the AES_Decrypt function to it so that it decrypts only the last 8 bytes of the input string, for example.
byte[] bytesToBeDecrypted = new byte[32];
byte[] 8_bytesToBeDecrypted = new byte[8]; // Only 8 bytes of bytesToBeDecrypted
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
string salt = "12345678";
Encoding unicode = Encoding.Unicode;
byte[] saltBytes = unicode.GetBytes(salt);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.Zeros;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 65535);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.ECB;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}

You can likely just Seek the MemoryStream with a multiple of 16 bytes from the beginning (or apparently even 16 bytes from the last part of the stream) and then connect the CryptoStream to decrypt whatever is left. With CBC that would be a bit more tricky as you would have to set the IV to the previous ciphertext block, but with ECB this should be a breeze.
Notes:
Of course, you don't need to set the IV for the ECB mode, that should not be required.
Rijndael with a block size of 128 is actually AES. C#/.NET only supports a subset of Rijndael by the way: only 64 increments of the block size and key size are supported, if I remember correctly.
I'd still use aes = Aes.Create() rather than aes = new RijndaelManaged() as that leaves the choice of the AES implementation to the system, and it will probably choose the hardware accelerated one over the managed one.

Related

C# ASE Encryption crashes DLL with 128 bit Blocksize

I am trying to work out why a C# DLL I have is crashing since I added in AES encryption.
I initially put managed rijndael encryption in with a 256 bit key and 256 bit blocksize and the DLL worked, but I found out that the dotnet base receiving end only supports 128 bit blocksize.
However, when I changed the blocksize to 128 bits the DLL simply crashes when I encrypt. I also read the rijndael managed should not be used for new projects and so I tied other examples of AES for C#. All of THESE OTHER EXAMPLES once again crash the DLL as soon as the encrypt function is called.
I am generating both a 64 bit version of the DLL and a 32 bit version of the DLL, both crash as soon as I try to encrypt a string of around 1000 characters.
Is there some sort of initialisation I am missing, or do I need to increase memory / Stack size to use AES in the DLL? Would really value some suggestions as to why this doesn't work.
This worked for 256 bit blocksize, but crashes for 128 bit blocksize
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
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.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;
}
Various other ways I have tried so far are below:
byte[] ivBytes = ComputeSHA256(iVphrase);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] keyBytes = ComputeSHA256(password);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Key = keyBytes;
aes.IV = ivBytes;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
ICryptoTransform crypto1 = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] encrypteddata = crypto1.TransformFinalBlock(plainTextBytes, 0, plainTextBytes.Length);
crypto1.Dispose();
return Convert.ToBase64String(encrypteddata);
byte[] encryptedData;
byte[] ivBytes = ComputeSHA256(iVphrase);
byte[] keyBytes = ComputeSHA256(password);
using (Aes aesAlgorithm = new AesManaged())
{
ICryptoTransform encryptor = aesAlgorithm.CreateEncryptor(keyBytes, ivBytes);
//Encryption will be done in a memory stream through a CryptoStream object
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(plainText);
}
encryptedData = ms.ToArray();
}
}
return Convert.ToBase64String(encryptedData);
}
byte[] ivBytes = ComputeSHA256(iVphrase);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] keyBytes = ComputeSHA256(password);
using (var symmetricKey = Aes.Create("AesManaged"))
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.Key = keyBytes;
symmetricKey.IV = ivBytes;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cs = new CryptoStream(memoryStream, symmetricKey.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(plainTextBytes, 0, plainTextBytes.Length);
cs.Close();
}
var cipherBytes = memoryStream.ToArray();
memoryStream.Close();
return Convert.ToBase64String(cipherBytes);
}
}
}
I finally managed to get an exception output which identified the issue as being a 32 byte IV being passed in to a 128 bit Block size which generated an exception. On windows server machines the exception didn't return any result code, it simply crashed the machine. Testing on another windows 10 device correctly logged the incorrect IV length as an exception.

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 256 Encryption is not matching with result

I have a document that is saying to Encrypted string using AES256. According to my document with two value 10002:1486703720424 AND HashKey: hpIw4SgN)TxJdoQj=GKo)p83$uHePgoF it will generate the result 1ltQFLRGNif73uCNzi0YEvBqLKiRgx6fWsk5e/GcTQc= but when i try to generate the result it is generating 6SKbqJAxbBrg4eU7r/B8gJoJEPg+KjMvGL5L7bfykUU= from my code. Can you please tell what i am doing mistakes. This is the first time when i doing encryption so i am little bit confuse to find the my mistakes.
string getHashKey1 = EncryptText("10002:1486703720424", "hpIw4SgN)TxJdoQj=GKo)p83$uHePgoF");
public string EncryptText(string input, string password)
{
string result = "";
try
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
result = Convert.ToBase64String(bytesEncrypted);
}
catch (Exception ex)
{
ErrorLog errLog = new ErrorLog();
errLog.LogsWrite(ex, Path.GetDirectoryName(Application.ExecutablePath));
}
return result;
}
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
try
{
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
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.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
}
catch (Exception ex)
{
ErrorLog errLog = new ErrorLog();
errLog.LogsWrite(ex, Path.GetDirectoryName(Application.ExecutablePath));
}
return encryptedBytes;
}
Found... They are using ECB as the cypher mode, so no IV. I won't comment on the "security" of this. The padding seems to be PKCS7 (the default of AES). The password is used "as is", simply encoded in UTF8 (or perhaps even ASCII) (so it must be 32 bytes long).
public static string EncryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
using (MemoryStream ms = new MemoryStream())
{
using (Aes aes = Aes.Create())
{
aes.Key = passwordBytes;
aes.Mode = CipherMode.ECB;
// "zero" IV
aes.IV = new byte[16];
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
}
byte[] encryptedBytes = ms.ToArray();
return encryptedBytes;
}
}
#LukePark correctly made a tirade about this answer that is useful for the OP but useless in the greater world. I'll bold the correctly word. For this reason I'll explain what is "wrong" on the specification given to A. Goutam and what a "correct" specification must always contain.
A specification for encryption should always contain: the algorithm used (AES for example), the key size (if you say AES256 then clearly it is 256 bits), the block mode (CBC, ECB etc). Many block modes (CBC for example) require an IV vector. ECB has a lower security than other block modes (see for example https://crypto.stackexchange.com/questions/225/should-i-use-ecb-or-cbc-encryption-mode-for-my-block-cipher). If the IV is necessary, then the specification must contain it (or explain how it should be generated). The specification must contain the padding that should be used. The Padding.None should be used only if the data to be encrypted can be exactly subdivided in encryption blocks (so for example, with AES, PaddingMode.None is good only if the data is 16, 32, 64, 96, ... bytes). PaddingMode.Zeros is good only for text (and I wouldn't use it, because it will add '\0' at the end of the text). The other padding modes are good.
Often the key isn't used "as is", because for example it is a string. The specification should contain how the encryption key must be derived from the string key. SHA256 on the key is a weak solution. Normally a good solution is using a strong key derivation function, like Rfc2898DeriveBytes. If this function is used, the specification must contain the number of iteration and other informations about using Rfc2898DeriveBytes or similar functions. Clearly what encoding should be used for the key (and for the data to be encrypted, if it is a text) must be included (UTF8 is always a good idea).
I'll add that a good specification should contain some test cases. At least one test case should be of length smaller than the encryption block and at least one test case must be of length greater than the encryption block but smaller than two encryption blocks (or > 2 and < 3... some complete blocks and one non-complete block). In this way you are testing both the PaddingMode and the CipherMode (note the should/must: by testing something bigger than an encryption block plus an incomplete block you are already testing everything)

Encryption AES 256 Input Plaintext Length Issue

From my security team, they want me to use AES256 key strength and CBC mode. My code only works when I enter a input plaintext of 32 letters in length now after changing to 256 CBC and block size to 128.
If I enter "This is a test" (not 32 characters long), I receive:
System.Security.Cryptography.CryptographicException: The input data is
not a complete block.
If I enter: " ABCDEFGHIJKLMNOPQRSTUVWXYZ000000", works!
What code do I need to make this work with "This is a test" as input.
Code Below:
public byte[] EncryptStringToByte(string plainText, byte[] key, byte[] vector)
{
byte[] encrypted;
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = vector;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// Create the streams used for encryption.
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(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
AES is a block cipher, so it only works on plaintexts that have exactly the size of one block. A mode of operation like CBC enables you to encrypt plaintexts that are a multiple of the block size. To encrypt plaintexts of arbitrary length a padding mode must be used.
A common mode used for block ciphers is PKCS#5/PKCS#7:
aes.Padding = PaddingMode.PKCS7;

Categories

Resources