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