c# AESManaged encrypts fine, ALMOST decrypts fine, and always throws exception - c#

TLDR: Client encrypts image, sends it to server, server tries to decrypt and throws exception but still saves 98% of the image normally with some junk appearing on the bottom-right part of the image.
Important Notes:
Key and IV are static at the moment for testing purposes but even when I use my randomized ones the rest of the messages work 100% of the time
I encrypt the whole byte array, split it up and send it, join the pieces in the server and then try to decrypt the image.
I've tried using Write mode on Decryption, there all messages fail
So this is a bit of a baffling problem I've tried to solve the past few days. I have a client and a server communicating via UDP and I encrypt/decrypt the data for security purposes. It all works fine for all the messages I send/receive except for Images (binary data). At first I had some encoding being done but I since removed that because I found out that it can alter the binary data and produce junk. I converted both my Encrypt and Decrypt functions to accept and return only byte[] so the image never changes format. So I encrypt the Image, send it over to the server, the server tries to decrypt the image and immediately throws an exception. Funnily, if I catch that exception and the function returns, the image is written normally with the difference of a weird line at the bottom-right corner that usually has different colors (its always at the bottom right so its probably junk data from the encryption). So it seems that the decryption works until the very end and then it fails leaving the extra bytes at the end or something along those lines ? (I tried encrypting on client and not decrypting on the server to see if the encryption was maybe failing but that produced complete junk when written to disk, so encryption should be working)
I don't think I forgot any other information but please do let me know if you'd like to know something more, anyway here is the code:
CLIENT SIDE
Encryption
public static byte[] Encrypt(byte[] plainText, byte[] iv)
{
byte[] encrypted;
// Create a new AesManaged.
using (AesManaged aes = new AesManaged())
{
// Create encryptor
aes.Padding = PaddingMode.PKCS7;
aes.Key = new byte[16];
aes.IV = new byte[16];
aes.Mode = CipherMode.CBC;
//aes.Key = StringToBytes(pinNumber);
//aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// Create MemoryStream
using (MemoryStream ms = new MemoryStream())
{
// Create crypto stream using the CryptoStream class. This class is the key to encryption
// and encrypts and decrypts data from any given stream. In this case, we will pass a memory stream
// to encrypt
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(plainText, 0, plainText.Length);
}
encrypted = ms.ToArray();
}
}
// Return encrypted data
return encrypted;
}
Client Side Image
int counter = 0;
byte[] iv = SecureHelper.GenerateIV();
Debug.Log("Phone Unencrypted: " + pngBytes.Length);
byte[] encrypted = SecureHelper.Encrypt(pngBytes, iv);
Debug.Log("Phone Encrypted: " + encrypted.Length);
MessageSender.SendImageLength(encrypted.Length, WebcamUI.Singleton.scanToggle.isOn); // If the toggle is turned on, the method will return true and
// a scan will take place
byte[][] imageByteArrays = new byte[(encrypted.Length / Message.MaxMessageSize) + 1][];
for (int i = 0; i < imageByteArrays.Length; i++)
{
if (counter < encrypted.Length)
{
imageByteArrays[i] = encrypted.Skip(counter).Take(1224).ToArray();
MessageSender.SendImage(imageByteArrays[i], i, iv);
counter += 1224;
}
prefabSlider.value = (counter * 100f) / encrypted.Length;
yield return new WaitForEndOfFrame();
}
Server Side Decryption:
public static byte[] Decrypt(ushort fromClientId, byte[] cipherText, byte[] iv)
{
byte[] plaintext = new byte[cipherText.Length];
int readBytes = 0;
try
{
using (AesManaged aes = new AesManaged())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = new byte[16];
aes.IV = new byte[16];
//aes.Key = StringToBytes(GetPin(fromClientId));
//aes.IV = iv;
// Create a decryptor
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
using (MemoryStream ms = new MemoryStream(cipherText))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
// Read crypto stream
readBytes = cs.Read(plaintext, 0, plaintext.Length);
}
plaintext = plaintext.Take(readBytes).ToArray();
Debug.Log("Did take");
}
}
}
catch (Exception e)
{
Debug.LogError("Errot at SecureHelper(Decrypt) " + e.Message + "\n" + e.StackTrace);
}
// Create AesManaged
return plaintext;
}
Server Side Image:
public void ReconstructAndWritePicture(string path, string currentCompany, ushort fromClientId)
{
string fullPath = path + "/" + currentCompany + "/";
Debug.Log("Size: " + imgLength);
int counter = 0;
byte[] wholeImage = new byte[imgLength];
for (int i = 0; i < imgParts.Length; i++)
{
for (int j = 0; j < imgParts[i].Length; j++)
{
wholeImage[j + counter] = imgParts[i][j];
}
counter += imgParts[i].Length;
}
Debug.Log("Phone Encrypted: " + wholeImage.Length);
byte[] img = SecureHelper.Decrypt(fromClientId, wholeImage, iv);
if (img == null)
{
return;
}
Debug.Log("Phone Unencrypted: " + img.Length);
string imgName = currentCompany + Companies.Singleton.samplesPerCompany[currentCompany].ToString() + ".jpg";
FileInfo file = new FileInfo(fullPath);
if (!file.Directory.Exists)
{
file.Directory.Create();
}
File.WriteAllBytes(fullPath + imgName, img);
Thank you in advance for taking the time to check this out!
UPDATE:
I copy pasted the Decrypt method from the server to the client and it worked flawlessly. So now I think I should look into whether all the data is sent from the Server to the Client and if I am piecing it back together correctly (but for unencrypted images it worked so that's weird).

Related

AES decrypt in chunks adds random bytes at the end

I have a very big csv file which is encrypted using AES. The code that does the encryption
using var aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = initializationVector;
using var memoryStream = new MemoryStream();
var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.Flush();
This is later saved into a file. On the decryption end, I'm trying to decrypt it in chunks, e.g.
using var sourceStream = File.OpenRead(path_to_encrypted_file);
using var aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = iv;
using (var fs = File.Create(path_to_decrypted_file))
using (var cryptoStream = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Write)
{
var dataBuffer = new byte[81290];
int read;
while ((read = await sourceStream.ReadAsync(dataBuffer)) != 0)
{
ReadOnlyMemory<byte> buffer = dataBuffer.AsMemory().Slice(0, read);
await cryptoStream.WriteAsync(buffer);
await cryptoStream.FlushAsync();
}
}
File is decrypted, however, I see some random bytes and empty lines at the end of the file
Is there anything wrong with how I decrypt ?
There's a couple potential issues I'd investigate first, at least in the existing provided code. There may be more depending on how you're generating the initial data byte array, how you're generating your key, how you're writing the encrypted stream to disk, etc.
You're using ECB and you almost certainly shouldn't. It isn't doing anything with your IV, either. Consider CBC or GCM depending on the application. https://stackoverflow.com/a/22958889/13374279
You're not using a padding mode. Unless your data is exactly contained within the block size, there's a chance you're losing some data, which might be contributing to the gibberish at the end.
You don't show the original encrypting stream disposal, you just show the Flush(). Depending on its disposal, it is likely not calling the CryptoStream's FlushFinalBlock() method, which is important. Given the lack of the padding mode, if you add this in, you'll likely suddenly see yourself with an exception here to alert you that The input data is not a complete block. due to #2 until you swap that out.
Thanks to the answer by #Adam G I reimplemented encrypt/decrypt following suggestions in the answer + comments.
A little background – I needed a solution where encryption happens on the client machine (disconnected from the internet) & decryption later on takes place in the cloud once the encrypted file uploaded to a blob storage.
I wanted to have a hybrid encryption, where key is RSA encrypted, data - AES.
So the file contents on the client:
RSA encrypted key
RSA encrypted IV (RSA encryption of the IV is not necessary AFAIK)
AES encrypted data
This is the final implementation:
// Local
var localRsa = RSA.Create();
localRsa.ImportRSAPublicKey(
Convert.FromBase64String(public_key),
out var _);
var localAes = Aes.Create();
localAes.GenerateKey();
localAes.GenerateIV();
localAes.Mode = CipherMode.CBC;
localAes.Padding = PaddingMode.PKCS7;
using (var dataStream = File.OpenRead(file_to_encrypt))
using (var secretFileStream = File.Create(encrypted_file))
{
await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.Key, RSAEncryptionPadding.OaepSHA256));
await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.IV, RSAEncryptionPadding.OaepSHA256));
using (var cryptoStream = new CryptoStream(secretFileStream, localAes.CreateEncryptor(localAes.Key, localAes.IV), CryptoStreamMode.Write))
{
await dataStream.CopyToAsync(cryptoStream);
}
}
And the decryption piece:
// Cloud
var cloudRsa = RSA.Create();
cloudRsa.ImportRSAPrivateKey(
Convert.FromBase64String(private_key),
out var _);
var cloudAes = Aes.Create();
cloudAes.Mode = CipherMode.CBC;
cloudAes.Padding = PaddingMode.PKCS7;
using (var secretFileStream = File.OpenRead(encrypted_file))
{
var keyBuffer = new byte[256];
await secretFileStream.ReadAsync(keyBuffer, 0, keyBuffer.Length);
cloudAes.Key = cloudRsa.Decrypt(keyBuffer, RSAEncryptionPadding.OaepSHA256);
var ivBuffer = new byte[256];
await secretFileStream.ReadAsync(ivBuffer, 0, keyBuffer.Length);
cloudAes.IV = cloudRsa.Decrypt(ivBuffer, RSAEncryptionPadding.OaepSHA256);
secretFileStream.Position = 512;
using (var plainTextStream = File.Create(decrypted_file))
{
using (var cryptoStream = new CryptoStream(secretFileStream, cloudAes.CreateDecryptor(cloudAes.Key, cloudAes.IV), CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(plainTextStream);
}
}
}

How to convert CryptoJS decryption code into C#?

I have this code in CryptoJS, inside browser:
var decrypt = function (cipherText) {
var key = "a_long_key_goes_here";
var iv = "initial_vector_goes_here";
key = CryptoJS.enc.Hex.parse(key);
iv = CryptoJS.enc.Hex.parse(iv);
var decrypted = CryptoJS.TripleDES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(cipherText)
}, key, {
iv: iv,
mode: CryptoJS.mode.CBC
});
var clearText = decrypted.toString(CryptoJS.enc.Utf8);
return clearText;
};
This code is not written by me. Also the cipherText come from another server that I have no access to. However, I have access to key and to iv.
I can decrypt that cipherText inside a browser's console. But I want to use these keys to decrypt that cipherText inside C# code. Here's the code I've written:
public void Desrypt()
{
ICryptoTransform decryptor;
UTF8Encoding encoder;
string key = "a_long_key_goes_here";
string iv = "initial_vector_goes_here";
var cipherText = "cipher_text_goes_here";
string clearText = "";
byte[] cipherBytes = FromHexString(cipherText);
using (Aes aes = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(key, new byte[] { });
aes.Key = pdb.GetBytes(32);
aes.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
clearText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return clearText;
}
public static byte[] FromHexString(string hexString)
{
var bytes = new byte[hexString.Length / 2];
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return bytes;
}
I have some problems though. I don't understand if I'm correctly decoding the given cipherText from hexadecimal or not. Also I can't instantiate Rfc2898DeriveBytes, because I don't know what the second parameter (salt) should be.
Also I don't know where should I use that iv I've gotten from the CryptoJS code.
Could you please help?
So that both codes are compatible, the following changes of the C# code are necessary:
The return type of the Decrypt method must be changed from void to string.
Key and IV have to be decoded hexadecimal like the ciphertext with FromHexString.
Instead of AES, TripleDES must be used.
Rfc2898DeriveBytes implements PBKDF2 and must not be applied (since the JavaScript code does not use PBKDF2 either).
The decrypted data must not be decoded with Encoding.Unicode (which corresponds to UTF16LE in .NET), but with Encoding.UTF8.
The C# code can handle 24 bytes keys (to support 3TDEA) and 16 bytes keys (to support the less secure 2TDEA). The posted CryptoJS code also handles these key sizes plus additionally 8 bytes keys (to support the least secure, DES compatible variant 1TDEA).
The following C# code decrypts a ciphertext generated with CryptoJS and 3TDEA:
public string Decrypt()
{
byte[] key = FromHexString("000102030405060708090a0b0c0d0e0f1011121314151617"); // 24 bytes (3TDEA)
byte[] iv = FromHexString("0001020304050607"); // 8 bytes
byte[] ciphertext = FromHexString("2116057c372e0e95dbe91fbfd148371b8e9974187b71e7c018de89c757280ad342d4191d29472040ee70d19015b025e1");
string plaintext = "";
using (TripleDES tdes = TripleDES.Create())
{
tdes.Key = key;
tdes.IV = iv;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, tdes.CreateDecryptor(tdes.Key, tdes.IV), CryptoStreamMode.Write))
{
cs.Write(ciphertext, 0, ciphertext.Length);
}
plaintext = Encoding.UTF8.GetString(ms.ToArray());
}
}
return plaintext;
}
The decryption is also possible with the posted JavaScript code, which shows the functional equivalence of both codes.
Note: Since AES is more performant than TripleDES, AES should be used if possible.

Decrypting CryptoStream into MemoryStream

I have written a process where a file is encrypted and uploaded to Azure, then the download process has to be decrypted which is what fails with a "Padding is invalid and cannot be removed" error, or a "Length of the data to decrypt is invalid." error.
I've tried numerous solutions online, including C# Decrypting mp3 file using RijndaelManaged and CryptoStream, but none of them seem to work and I end up just bouncing back and forth between these two errors. The encryption process uses the same key/IV pair that decryption uses, and since it will decrypt a portion of the stream I feel like that's working fine - it just ends up dying with the above errors.
Here is my code, any ideas? Please note that the three variants (cryptoStream.CopyTo(decryptedStream), do {} and while) aren't run together - they are here to show the options I've already tried, all of which fail.
byte[] encryptedBytes = null;
using (var encryptedStream = new MemoryStream())
{
//download from Azure
cloudBlockBlob.DownloadToStream(encryptedStream);
//reset positioning for reading it back out
encryptedStream.Position = 0;
encryptedBytes = encryptedStream.ConvertToByteArray();
}
//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
//stream where decrypted contents will be stored
using (var decryptedStream = new MemoryStream())
{
using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
{
using (var decryptor = aes.CreateDecryptor())
{
//decrypt stream and write it to parent stream
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
//fails here with "Length of the data to decrypt is invalid." error
cryptoStream.CopyTo(decryptedStream);
int data;
//fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
//implying it is in fact decrypting part of it, just not everything
do
{
data = cryptoStream.ReadByte();
decryptedStream.WriteByte((byte)cryptoStream.ReadByte());
} while (!cryptoStream.HasFlushedFinalBlock);
//fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
//implying it is in fact decrypting part of it, just not everything
while ((data = cryptoStream.ReadByte()) != -1)
{
decryptedStream.WriteByte((byte)data);
}
}
}
}
//reset position in prep for reading
decryptedStream.Position = 0;
return decryptedStream.ConvertToByteArray();
}
}
One of the comments mentioned wanting to know what ConvertToByteArray is, and it's just a simple extension method:
/// <summary>
/// Converts a Stream into a byte array.
/// </summary>
/// <param name="stream">The stream to convert.</param>
/// <returns>A byte[] array representing the current stream.</returns>
public static byte[] ConvertToByteArray(this Stream stream)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
The code never reaches this though - it dies before I can ever get it to this point.
After a lot of back and forth from various blogs, I found I actually had a couple of errors in the above code that were nailing me. First, the encryption process was incorrectly writing the array - it was wrapped with a CryptoStream instance, but wasn't actually utilizing that so I was writing the unencrypted data to Azure. Here is the proper route to go with this (fileKey is part of a custom class I created to generate Key/IV pairs, so wherever that is referenced can be changed to the built-in process from RijndaelManaged or anything else you'd utilize for coming up with a key/IV pair):
using (var aes = new RijndaelManaged { KeySize = 256, Key = fileKey.Key, IV = fileKey.IV })
{
using (var encryptedStream = new MemoryStream())
{
using (ICryptoTransform encryptor = aes.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write))
{
using (var originalByteStream = new MemoryStream(file.File.Data))
{
int data;
while ((data = originalByteStream.ReadByte()) != -1)
cryptoStream.WriteByte((byte)data);
}
}
}
var encryptedBytes = encryptedStream.ToArray();
return encryptedBytes;
}
}
Second, since my encryption process involves multiple steps (three total keys per file - container, filename and file itself), when I tried to decrypt, I was using the wrong key (which is seen above when I referenced blobKey to decrypt, which was actually the key used for encrypting the filename and not the file itself. The proper decryption method was:
//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
//stream where decrypted contents will be stored
using (var decryptedStream = new MemoryStream())
{
using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
{
using (var decryptor = aes.CreateDecryptor())
{
//decrypt stream and write it to parent stream
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
int data;
while ((data = cryptoStream.ReadByte()) != -1)
decryptedStream.WriteByte((byte)data);
}
}
}
//reset position in prep for reading
decryptedStream.Position = 0;
return decryptedStream.ConvertToByteArray();
}
}
I had looked into the Azure Encryption Extensions (http://www.stefangordon.com/introducing-azure-encryption-extensions/), but it was a little more local file-centric than I was interested - everything on my end is streams/in-memory only, and retrofitting that utility was going to be more work than it was worth.
Hopefully this helps anyone looking to encrypt Azure blobs with zero reliance on the underlying file system!
Bit late to the party, but in case this is useful to someone who finds this thread:
The following works well for me.
internal static byte[] AesEncryptor(byte[] key, byte[] iv, byte[] payload)
{
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(key, iv);
var encrypted = encryptor.TransformFinalBlock(payload, 0, payload.Length);
return iv.Concat(encrypted).ToArray();
}
}
and to decrypt:
internal static byte[] AesDecryptor(byte[] key, byte[] iv, byte[] payload)
{
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
return decryptor.TransformFinalBlock(payload, 0, payload.Length);
}
}
this works for encrypting/decrypting both fixed length hex strings when decoded from hex to byte[] as well as utf8 variable length strings when decoded using Encoding.UTF8.GetBytes().

Decrypted data with usage of TripleDESCryptoServiceProvider has additonal characters

I am facing with problem when decrypting data with usage of TripleDESCryptoServiceProvider. The problem is that decrypted value contains beside of original value some additional, strange characters at the end
Per instance if I provide "rastko" to be encrypted, I will get later with decryption something like this "rastko⥊㮶". For other values it could be different number of 'dummy' characters or in some cases I will get exact value.
Then, I saw that for all encrypted data byte array size is divisible by 8. It looks like any provided data is rounded on value that is divisible by 8. Only in case when original encoded value is divisible by 8, decryption will retrieve appropriate value.
Here are methods that I am using :
public static byte[] EncryptPassword(string password, out byte[] cryptoKey, out byte[] cryptoIV)
{
try
{
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
byte[] unicodePassword = unicodeEncoding.GetBytes(password);
byte[] encryptedPassword;
using (TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider())
{
tripleDes.Key = GetCryptoKey();
tripleDes.Mode = CipherMode.CBC;
tripleDes.Padding = PaddingMode.PKCS7;
cryptoKey = tripleDes.Key;
cryptoIV = tripleDes.IV;
using (MemoryStream memoryStream = new MemoryStream())
{
ICryptoTransform cryptoTransform = tripleDes.CreateEncryptor();
using (
CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(unicodePassword, 0, unicodePassword.Length);
////cryptoStream.FlushFinalBlock();
}
encryptedPassword = memoryStream.ToArray();
}
}
return encryptedPassword;
}
catch (Exception ex)
{
throw new Exception("Password encryption failed !", ex);
}
}
public static string DecryptPassword(byte[] encryptedPassword, byte[] cryptoKey, byte[] cryptoIV)
{
try
{
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
string readablePassword;
using (TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider())
{
tripleDes.Key = cryptoKey;
tripleDes.IV = cryptoIV;
tripleDes.Mode = CipherMode.CBC;
tripleDes.Padding = PaddingMode.PKCS7;
// Create a new MemoryStream using the passed
// array of encrypted data.
using (MemoryStream memoryStream = new MemoryStream(encryptedPassword))
{
// Create crypto transform that defines the basic operations of cryptographic transformations.
ICryptoTransform cryptoTransform = tripleDes.CreateDecryptor();
// Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
decryptoStream.Write(encryptedPassword, 0, encryptedPassword.Length);
///decryptoStream.FlushFinalBlock();
}
byte[] decryptedPassword = memoryStream.ToArray();
//Convert the buffer into a string and return it.
readablePassword = unicodeEncoding.GetString(decryptedPassword, 0, decryptedPassword.Length);
}
}
return readablePassword;
}
catch (Exception ex)
{
throw new Exception("Password decryption failed !", ex);
}
}
private static byte[] GetCryptoKey()
{
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
string plainKey = "rastkoisajev2310982josipasenera153";
byte[] encodedKey = unicodeEncoding.GetBytes(plainKey);
// Prepares 192 bit key
byte[] preparedKey = new byte[24];
Array.Copy(encodedKey, preparedKey, 24);
return preparedKey;
}
Here is sample test invocation :
private static void CryptoTest()
{
string password = "rastko";
byte[] cryptoKey;
byte[] cryptoIV;
byte[] encryptedPassword = Crypto.EncryptPassword(password, out cryptoKey, out cryptoIV);
string decryptedPAssword = Crypto.DecryptPassword(encryptedPassword, cryptoKey, cryptoIV);
}
I have not good experience with security. What I see is that IV vector is 8byte size and as I found it is related to BlockSize, that is 8times greater then IV size. TripleDESCryptoServiceProvider for IV vector is using 8byte value. I can not change this.
Could you please tell me what I have to do or did I wrote something wrongly ?
DES is a 64 bit block cypher. Any text that does not divide cleanly into 64 bit (=8 byte) blocks needs to be padded to make up a whole number of blocks. You need to set padding for encryption and decryption. If you have control of both ends then use PKCS#5 padding to encrypt and decrypt. If you only have control over the decryption end, then ask the encrypting end what padding they are using and expect that.
Note that encrypting a password is normally not the way to go. Use PBKDF2 instead. Don't confuse passwords and keys!
Try to make sure that your CryptoStreams get closed or flushed:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.flushfinalblock.aspx
If you don't then the padding/unpadding will likely not be performed, and you get trash instead.
After detail investigation I have found the solution for my problem.
I have changed a little bit decryption logic.
Instead of this part in DecryptPassword method :
// Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
decryptoStream.Write(encryptedPassword, 0, encryptedPassword.Length);
///decryptoStream.FlushFinalBlock();
}
byte[] decryptedPassword = memoryStream.ToArray();
//Convert the buffer into a string and return it.
readablePassword = unicodeEncoding.GetString(decryptedPassword, 0, decryptedPassword.Length);
}
I am now using the Read logic from CryptoStream and then I am just removing nullable characters. It is like this now :
// Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
{
// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[encryptedPassword.Length];
decryptoStream.Read(fromEncrypt, 0, fromEncrypt.Length);
//Convert the buffer into a string and return it.
readablePassword = unicodeEncoding.GetString(fromEncrypt);
readablePassword = readablePassword.Replace("\0", string.Empty);
}
This works perfectly for me ! Thank you all for your time.

C# AES Encryption in CFB Where Plaintext Length Equals Encrypted Length

I have an existing data format that has portions of it encrypted in what appears to be AES in CFB mode. The plaintext data length and the encrypted data length are the same.
In C#, pretty much every angle I've taken seems to expect the encrypted length to be a multiple of the block size... so I get an exception trying to decrypt the data.
In researching solutions, I've used Crypto++ and wrote a quick C++ app that successfully decrypts the data, so I'm pretty sure I'm using the right algorithm, key and IV. This works fine, but I'd like to keep everything inside C# if at all possible. Any suggestions?
Working C++ code below:
//define key
unsigned char key[16];
//populate key
//...
//define iv
unsigned char iv[16];
//populate iv
//...
std::ifstream inFile;
//open file
inFile.open("file.aes",ios::binary );
//get file size
inFile.seekg(0,ios::end);
int fileSize = (int) inFile.tellg();
inFile.seekg(offset, ios::beg);
//read/close file
char* inBytes = new char[fileSize];
inFile.read(inBytes,fileSize);
inFile.close();
//configure decryption
CFB_Mode<AES>::Decryption cfbDecryption(key, 16, iv);
//populate output bytes
char* outBytes = new char[fileSize];
cfbDecryption.ProcessData((byte*) outBytes,(byte*) inBytes,fileSize);
//open/write/close output file
std::ofstream outFile;
outFile.open("out.dec");
outFile.write(outBytes,fileSize);
outFile.close();
delete[] inBytes;
Here is an example showing how to use the RijndaelManaged class to achieve 8-bit feedback CFB encryption. AesManaged does not support CFB because, I believe, the official NIST AES does not support it. By noting that AES is just Rijndael restricted to the 128 bit blocksize and the 128, 192, and 256 bit keysizes you can use the RijndaelManaged classes to get your CFB functionality. NOTE: I'm not a C# or .NET expert so improvements are welcome.
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace AesCFB8Mode
{
class AESCFB8Example
{
static void Example()
{
//
// Encrypt a small sample of data
//
String Plain = "The quick brown fox";
byte[] plainBytes = Encoding.UTF8.GetBytes(Plain);
Console.WriteLine("plaintext length is " + plainBytes.Length);
Console.WriteLine("Plaintext is " + BitConverter.ToString(plainBytes));
byte [] savedKey = new byte[16];
byte [] savedIV = new byte[16];
byte[] cipherBytes;
using (RijndaelManaged Aes128 = new RijndaelManaged())
{
//
// Specify a blocksize of 128, and a key size of 128, which make this
// instance of RijndaelManaged an instance of AES 128.
//
Aes128.BlockSize = 128;
Aes128.KeySize = 128;
//
// Specify CFB8 mode
//
Aes128.Mode = CipherMode.CFB;
Aes128.FeedbackSize = 8;
Aes128.Padding = PaddingMode.None;
//
// Generate and save random key and IV.
//
Aes128.GenerateKey();
Aes128.GenerateIV();
Aes128.Key.CopyTo(savedKey, 0);
Aes128.IV.CopyTo(savedIV, 0);
using (var encryptor = Aes128.CreateEncryptor())
using (var msEncrypt = new MemoryStream())
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var bw = new BinaryWriter(csEncrypt, Encoding.UTF8))
{
bw.Write(plainBytes);
bw.Close();
cipherBytes = msEncrypt.ToArray();
Console.WriteLine("Cipher length is " + cipherBytes.Length);
Console.WriteLine("Cipher text is " + BitConverter.ToString(cipherBytes));
}
}
//
// Now decrypt the cipher back to plaintext
//
using (RijndaelManaged Aes128 = new RijndaelManaged())
{
Aes128.BlockSize = 128;
Aes128.KeySize = 128;
Aes128.Mode = CipherMode.CFB;
Aes128.FeedbackSize = 8;
Aes128.Padding = PaddingMode.None;
Aes128.Key = savedKey;
Aes128.IV = savedIV;
using (var decryptor = Aes128.CreateDecryptor())
using (var msEncrypt = new MemoryStream(cipherBytes))
using (var csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
using (var br = new BinaryReader(csEncrypt, Encoding.UTF8))
{
//csEncrypt.FlushFinalBlock();
plainBytes = br.ReadBytes(cipherBytes.Length);
Console.WriteLine("Decrypted plain length is " + plainBytes.Length);
Console.WriteLine("Decrypted plain text bytes is " + BitConverter.ToString(plainBytes));
Console.WriteLine("Decrypted plain text is " + Encoding.UTF8.GetString(plainBytes));
}
}
}
static void Main(string[] args)
{
Example();
}
}
}
I revisited trying to use cryptlib and it solved my problem... code is below:
using cryptlib;
byte[] key = new byte[16] {...key bytes here...};
byte[] iv = new byte[16] {...iv bytes here...};
byte[] enc; //ciphertext bytes (i populated them from a filestream)
crypt.Init();
int cryptContext = crypt.CreateContext(crypt.UNUSED, crypt.ALGO_AES);
crypt.SetAttribute(cryptContext, crypt.CTXINFO_MODE, crypt.MODE_CFB);
crypt.SetAttributeString(cryptContext, crypt.CTXINFO_KEY, key, 0, 16);
crypt.SetAttributeString(cryptContext, crypt.CTXINFO_IV, iv, 0, 16);
crypt.Decrypt(cryptContext, enc); //ciphertext bytes replaced with plaintext bytes
crypt.DestroyContext(cryptContext);

Categories