I have server-client. Server encrypt message and send to client.
Client want to decrypt, but program is crash and error say: Length of the data to decrypt is invalid.
Here is code, where I listener server.
while (true)
{
if (tok.CanRead)
{
tok = client.GetStream();
int buffSize = 0;
byte[] inStream = new byte[10025];
tok.Read(inStream, 0, inStream.Length);
RijndaelManaged AesEncryption = new RijndaelManaged();
AesEncryption.KeySize = 256; // 192, 256
AesEncryption.BlockSize = 128;
AesEncryption.Mode = CipherMode.CBC;
AesEncryption.Padding = PaddingMode.PKCS7;
string keyStr = "cGFzc3dvcmQAAAAAAAAAAA==";
string ivStr = "cGFzc3dvcmQAAAAAAAAAAA==";
byte[] ivArr = Convert.FromBase64String(keyStr);
byte[] keyArr = Convert.FromBase64String(ivStr);
AesEncryption.IV = ivArr;
AesEncryption.Key = keyArr;
ICryptoTransform decrypto = AesEncryption.CreateDecryptor();
inStream = decrypto.TransformFinalBlock(inStream, 0, inStream.Length); //here is error
string returndata = System.Text.Encoding.ASCII.GetString(inStream);
readdata = "" + returndata;
msg();
}
}
How can I fix that length?.
The .NET API is build around streams. Try and use the CryptoStream API instead, initialized with an AesCryptoServiceProvider. TransformFinalBlock should only be used for low level access, and only for the last bytes of the ciphertext.
Quote:
TransformFinalBlock is a special function for transforming the last block or a partial block in the stream. It returns a new array that contains the remaining transformed bytes. A new array is returned, because the amount of information returned at the end might be larger than a single block when padding is added.
Related
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).
With the below code, I get the error message at stream2.Close():
System.Security.Cryptography.CryptographicException: "The input data is not a complete block.
I searched on the internet, but no solution seems to match my program.
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] buffer = null;
byte[] buffer1 = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
byte[] salt = buffer1;
using (MemoryStream stream = new MemoryStream())
{
using (RijndaelManaged managed = new RijndaelManaged())
{
managed.KeySize = 0x100;
managed.BlockSize = 0x80;
Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(passwordBytes, salt, 0x3e8);
managed.Key = bytes.GetBytes(managed.KeySize / 8);
managed.IV = bytes.GetBytes(managed.BlockSize / 8);
managed.Mode = CipherMode.CBC;
using (CryptoStream stream2 = new CryptoStream((Stream)stream, managed.CreateDecryptor(), CryptoStreamMode.Write))
{
stream2.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
stream2.Close();
}
buffer = stream.ToArray();
}
}
return buffer;
}
For CBC mode the input size of the ciphertext, in this case bytesToBeDecrypted should be a multiple of the block size (16 bytes for AES, irrespective of the key size). Somehow your input size is not a multiple of the block size.
However, we cannot tell why bytesToBeDecrypted is not a multiple of the block size, that's outside the code fragment. Usually it is either that the data is not read up to the end of the ciphertext, or that here has been an encoding / decoding mistake.
I get error "the input data is not a complete block", I dont know if my code is wrong or something is missing. i try to encrypt/decrypt bytes with same lenght.
=byte[] plain => MyEnc(plain) => byte[] encrypted => MyDec(encrypt) => byte[] plain
Plain and encrypted have the same length.
This is my encryption code:
public static byte[] MyEnc(byte[] Input)
{
byte[] inputencdec = Input;
byte[] encrypted;
using (MemoryStream mstream = new MemoryStream())
{
using (AesCryptoServiceProvider encdec = new AesCryptoServiceProvider())
{
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
ICryptoTransform icrypt = encdec.CreateEncryptor(encdec.Key, encdec.IV);
using (CryptoStream cryptoStream = new CryptoStream(mstream,
icrypt, CryptoStreamMode.Write))
{
cryptoStream.Write(inputencdec, 0, inputencdec.Length);
}
}
encrypted = mstream.ToArray();
}
return encrypted;
}
this is my decryption code:
public static byte[] MyDec(byte[] Input)
{
byte[] inputencdec = Input;
byte[] buffer = new byte[Input.Length];
int totalRead = 0;
byte[] plain;
MemoryStream plainStream = new MemoryStream();
using (MemoryStream mStream = new MemoryStream(inputencdec))
{
using (AesCryptoServiceProvider encdec = new AesCryptoServiceProvider())
{
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
ICryptoTransform icrypt = encdec.CreateDecryptor(encdec.Key, encdec.IV);
using (CryptoStream cryptoStream = new CryptoStream(mStream, icrypt, CryptoStreamMode.Read))
{
while (true)
{
int read = cryptoStream.Read(buffer, 0, inputencdec.Length);
if (read == 0)
break;
else
plainStream.Write(buffer, totalRead, read);
totalRead += read;
}
}
}
plain = plainStream.ToArray();
}
return plain;
}
Plain and encrypted are not the same length for CBC mode encryption, which you are using. The plaintext needs to be padded before it can be encrypted, so the ciphertext size is always larger than the plaintext message for CBC (which is the default mode that the decryptor is using).
Streaming modes do not need to expand the ciphertext as they do not require padding. Unfortunately Microsoft opted not to include counter mode in .NET. You could use CFB mode.
You could also decide to implement counter mode using ECB if you require parallel encryption (in the comments below the question). Generally AES is so fast nowadays that parallelism is not required. Implementing a statically sized counter and CTR buffer should pale in comparison with creating the multithreaded code.
This is incorrect and may cause an issue:
int read = cryptoStream.Read(buffer, 0, inputencdec.Length);
you should of course put in buffer.length, not inputencdec.length. The use of a buffer is to store data in a buffer, by reading up to buffer.length bytes per loop iteration.
This is incorrect as well:
plainStream.Write(buffer, totalRead, read);
the problem is that totalRead should be the offset in the buffer, not the offset within the stream. You're reading into offset 0 of the buffer, so you should start writing from offset 0 as well.
You could also create a MemoryStream for the plaintext, wrap it with a CryptoStream using a decryptor, and then write the ciphertext all in one go. There is no need for the encryption / decryption to use a different scheme, as far as I can see. You seem to keep all the plaintext / ciphertext in memory anyway.
Notes:
for CBC mode the IV should be random; it is often prefixed to the ciphertext and removed and used by the decryptor;
keys and IV's should consist of random bytes; they should not be strings.
I'm trying to Decrypt an byte array and I got this error:
Length of the data to decrypt is invalid.
The error occurs at this point:
int decryptedByteCount = cryptoStream
.Read(plainTextBytes, 0, plainTextBytes.Length);
here's the complete code
public static byte[] Decrypt(byte[] encryptedBytes, string key) {
string initVector = "#1B2c3D4e5F6g7H8";
string saltValue = "s#1tValue";
string passPhrase = key;//"s#1tValue";
string hashAlgorithm = "SHA1";
int passwordIterations = 2;
int keySize = 128;
// Convert strings defining encryption key characteristics into byte
// arrays. Let us assume that strings only contain ASCII codes.
// If strings include Unicode characters, use Unicode, UTF7, or UTF8
// encoding.
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
// Convert our ciphertext into a byte array.
byte[] cipherTextBytes = encryptedBytes;
// First, we must create a password, from which the key will be
// derived. This password will be generated from the specified
// passphrase and salt value. The password will be created using
// the specified hash algorithm. Password creation can be done in
// several iterations.
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations);
// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
byte[] keyBytes = password.GetBytes(keySize / 8);
// Create uninitialized Rijndael encryption object.
TripleDESCryptoServiceProvider symmetricKey = new TripleDESCryptoServiceProvider();
// It is reasonable to set encryption mode to Cipher Block Chaining
// (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.CBC;
// Generate decryptor from the existing key bytes and initialization
// vector. Key size will be defined based on the number of the key
// bytes.
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
// Define memory stream which will be used to hold encrypted data.
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
// Define cryptographic stream (always use Read mode for encryption).
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
// Since at this point we don't know what the size of decrypted data
// will be, allocate the buffer long enough to hold ciphertext;
// plaintext is never longer than ciphertext.
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
// Start decrypting.
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
// Close both streams.
memoryStream.Close();
cryptoStream.Close();
// Convert decrypted data into a string.
// Let us assume that the original plaintext string was UTF8-encoded.
string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
// Return decrypted string.
//return plainText;
return plainTextBytes;
}
the error is here,
MemoryStream fileStreamIn = new MemoryStream(buffer);
ZipInputStream zipInStream = new ZipInputStream(fileStreamIn);
ZipEntry entry = zipInStream.GetNextEntry();
MemoryStream fileStreamOut = new MemoryStream();
int size;
byte[] bufferOut = new byte[buffer.Length];
do {
size = zipInStream.Read(bufferOut, 0, bufferOut.Length);
fileStreamOut.Write(bufferOut, 0, size);
} while (size > 0);
zipInStream.Close();
fileStreamOut.Close();
fileStreamIn.Close();
return bufferOut;
this is the unzip method and the size of the bufferout is bigger than should be
I am using this code to encryp/decrypt strings between c# and php:
class encryption
{
public string SimpleTripleDes(string Data)
{
byte[] key = Encoding.ASCII.GetBytes("passwordDR0wSS#P6660juht");
byte[] iv = Encoding.ASCII.GetBytes("password");
byte[] data = Encoding.ASCII.GetBytes(Data);
byte[] enc = new byte[0];
TripleDES tdes = TripleDES.Create();
tdes.IV = iv;
tdes.Key = key;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform ict = tdes.CreateEncryptor();
enc = ict.TransformFinalBlock(data, 0, data.Length);
return ByteArrayToString(enc);
}
public string SimpleTripleDesDecrypt(string Data)
{
byte[] key = Encoding.ASCII.GetBytes("passwordDR0wSS#P6660juht");
byte[] iv = Encoding.ASCII.GetBytes("password");
byte[] data = StringToByteArray(Data);
byte[] enc = new byte[0];
TripleDES tdes = TripleDES.Create();
tdes.IV = iv;
tdes.Key = key;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform ict = tdes.CreateDecryptor();
enc = ict.TransformFinalBlock(data, 0, data.Length);
return Encoding.ASCII.GetString(enc);
}
public static string ByteArrayToString(byte[] ba)
{
string hex = BitConverter.ToString(ba);
return hex.Replace("-", "");
}
public static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
}
Now what I'd like to do is to know when the decryption failed, when it fails it show me a messagebox with this text:
Could not find any recognizable digits
I could just compare that to the decrypted string bu, will this "error" text be the same on all computers even if they .net lib is from another language?
'Decryption failed' could mean many things.
You decrypt engine TransformFinalBlock() throws exception because you supplied invalid key or IV
You supplied valid but incorrect IV - this can be taken care of because you know their correct values and how they are protected.
you supplied correct key, IV but wrong cyphertext (or tampered).
1 is algorithimic failure and can be handled.
For 2 and 3 unfortunately without comparing decrypted text with orignal plaintext it's difficult to know whether 'decryption failed,' unless you introduce some additional measures for tamper-checks - hashing is the one answer for that. In both cases result could be inconsistent.
Tamper detection is unlikely in both stream ciphers and block ciphers, because these are not designed for this purpose. You have to use a combination of ctyptographic techniques to create a reselient infrastructure.
If you have a .NET library, designed to give a specific message, it doesnot matter what language (I am assuming you're talking about a CLS compliant language, C#, VB.NET etc.) it was written in and what computer it runs on, the behaviour ought to be consistent.
EDIT:
Block ciphers always add padding to your plaintext irrespective of chaining technique used to get the next full block size before encryption. Decryption should remove padding, but you might expect a string terminated with one or more nulls. Be wary of this and consider maintaining length of your data.