我正在尝试读取服务器上的文件(以5KB为单位),使用AES加密块并将其发送到客户端。在客户端上,我解密收到的块,并附加到文件以获取原始文件。
但是,我在客户端收到的解密块大小与在服务器上加密的明文块不同。
e.g。 我有一个15.5 KB的exe文件,所以我有15.5 * 1024/5 * 1024 = 4个块(圆形图)来加密并发送到客户端(前3个块是5120个字节,最后一个块是512个字节长)。但是,在客户端上,解密的块大小为5057,4970,5016和512字节,等于15.1 KB的文件大小(小于服务器实际发送的大小)。
这是我的代码段:
服务器(将文件发送到客户端):
FileStream fs = new FileStream("lcd.exe", FileMode.Open, FileAccess.Read);
//block size = 5KB
int blockSize = 5 * 1024;
//calculate number of blocks in data
long numberOfBlocks = fs.Length / blockSize;
if (fs.Length % blockSize != 0) numberOfBlocks++;
byte[] numberOfBlocksBytes = BitConverter.GetBytes(numberOfBlocks);
//send number of blocks to client
SendMessage(sw, numberOfBlocksBytes);
int count = 0, offset = 0, numberOfBytesToRead=0;
Aes objAes = new Aes();
while (count < numberOfBlocks)
{
byte[] buffer;
numberOfBytesToRead = blockSize;
if (fs.Length < offset + blockSize)
{
numberOfBytesToRead = (int)(fs.Length - offset);
}
buffer = new byte[numberOfBytesToRead];
fs.Read(buffer, 0, numberOfBytesToRead);
//encrypt before sending
byte[] encryptedBuffer = objAes.Encrypt(buffer, Encoding.Default.GetBytes(sessionKey), initVector);
SendMessage(sw, encryptedBuffer);
offset += numberOfBytesToRead;
count++;
}
fs.Close();
接收文件的客户端代码:
byte[] numberOfBlocksBytes = ReadMessage(sr);
long numberOfBlocks = BitConverter.ToInt64(numberOfBlocksBytes, 0);
FileStream fs = new FileStream("lcd.exe", FileMode.Append, FileAccess.Write);
//block size = 5KB
int blockSize = 5 * 1024;
Aes objAes = new Aes();
int count = 0, offset = 0;
while (count < numberOfBlocks)
{
byte[] encryptedBuffer = ReadMessage(sr);
byte[] buffer = objAes.Decrypt(encryptedBuffer, sessionKey, initVector);
fs.Write(buffer, 0, buffer.Length);
offset += buffer.Length;
count++;
}
fs.Close();
我的加密AES代码:
private const int StandardKeyLength = 16;
public byte[] Encrypt(byte[] plainText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bPlainBytes = plainText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateEncryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream();
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Write);
objCs.Write(bPlainBytes, 0, bPlainBytes.Length);
objCs.FlushFinalBlock();
var bEncrypted = objMs.ToArray();
return bEncrypted;
}
我的解密AES代码:
public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bCipherBytes = cipherText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream(bCipherBytes);
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);
var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();
return (Encoding.Default.GetBytes(strDecrypted));
}
这些是调试在服务器上发送文件块的while循环时得到的结果:
发送的实际文件大小:15.5 KB = 15872字节
Buffer size(plaintext) Encrypted Buffer Size(Sent) Offset Count 5120 5136 5120 0 5120 5136 10240 1 5120 5136 15360 2 512 528 15872 3
这些是我在调试接收客户端上的文件块的while循环时得到的结果:
收到的实际文件大小:15.1 KB = 15555字节
Received Buffersize Decrypted Buffer Size Offset Count 5136 5057 5057 0 5136 4970 10027 1 5136 5016 15043 2 528 512 15555 3
很明显,发送和接收代码工作正常(因为发送的加密缓冲区大小=接收缓冲区大小)。但是,解密的缓冲区大小与缓冲区大小(明文)完全不匹配,除了最后一个512字节的块。
解密可能有什么问题,因为我没有在客户端完全接收文件?
答案 0 :(得分:2)
你被绊倒了,因为在你的Decrypt声明中,你正在将你的密文看作是一个字符串。具体来说,这些行:
var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();
return (Encoding.Default.GetBytes(strDecrypted));
相反,您希望在CryptoStream上调用Read来将原始字节数组读入缓冲区。然后,您可以返回该缓冲区而不尝试将其强制转换为字符串(这是使用流读取器发生的情况)。
你应该使用更像的东西:
public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bCipherBytes = cipherText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream(bCipherBytes);
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);
var buffer = new byte[cipherText.Length];
int readBytes = objCs.Read(buffer, 0, cipherText.Length);
var trimmedData = new byte[readBytes];
Array.Copy(buffer, trimmedData, readBytes);
return trimmedData;
}
我也建议你ake a look at the encryption utilities I maintain on Snipt。特别是Symmetric Encrypt和Decrypt方法。你现在的代码有很多使用的块丢失和许多潜在的资源泄漏。
答案 1 :(得分:1)
var streamobj = new StreamReader(objCs);
这不太可能奏效。 StreamReader将假设解密数据是utf-8编码文本。从加密数据的代码实际上就是这种情况没有任何暗示,它需要一个byte []。
请使用FileStream,以便根本不进行任何转换。还可以帮助您避免使用Encoding.Default.GetBytes()数据随机数发生器。
答案 2 :(得分:-1)
快速观察,这可能只是我的无知:Encrypt()方法使用默认编码来获取会话密钥字节。在接收端,Decrypt()方法使用sessionKey本身作为第二个参数,即没有获取字节?