使用C#/.Net通过MemoryStream(byte[])对文件中的数据进行加密并在套接字上发送
本文关键字:加密 数据 套接字 文件 通过 MemoryStream Net byte 使用 | 更新日期: 2023-09-27 18:28:23
我们有一个套接字服务器,需要将加密的二进制文件传输到连接的客户端。当对文件的请求到达时,需要对文件进行加密,最好是而不要先在磁盘上制作文件的加密副本。
由于我们使用异步套接字API,因此不能使用sockets.NetworkStream。相反,我们需要使用带有byte[]缓冲区的Socket.BeginSend()作为输入。
一种看似直接的可能性是使用CryptoStream和MemoryStream作为加密内容的目的地。MemoryStream本身将使用byte[]缓冲区作为自己的数据存储库。
例如:
int blockSizeBytes = 1024 * 64;
byte [] plainData = new byte [blockSizeBytes];
byte [] encData = new byte [blockSizeBytes];
MemoryStream memory = new MemoryStream( encData );
ICryptoTransform encryptor = ... any ***CryptoServiceProvider.CreateEncryptor();
CryptoStream csEnc = new CryptoStream( memory, encryptor, CryptoStreamMode.Write );
一个文件可以一次读取并加密一个缓冲区,然后通过套接字发送到客户端。例如:
Socket clientSocket = ...; // connected peer.
int bytesRead = 0;
FileStream streamIn = new FileStream( strInputFile, FileMode.Open );
do
{
bytesRead = streamIn.Read( plainData , 0, blockSizeBytes );
if (bytesRead > 0)
{
csEnc.Write( plainData , 0, bytesRead ); // Write to crypto stream
// At this point the underlying byte array encData will hold the most recently
// encrypted buffer of data.
// Ideally we would send the encData buffer over the socket to the client via
// the following pseudo-code:
// 1) How can we determine the precise number of encoded bytes to send?
clientSocket.BeginSend( encData, 0, bytesRead, ... /* other params */ );
...
memory.Seek( 0, SeekOrigin.Begin ); // Reset memory stream back to start
}
}
while (bytesRead > 0);
streamIn.Close(); // close intput file stream
outEnc.FlushFinalBlock(); // Deal with padding, etc.
// Send the final buffer with all the necessary padding to the client.
// 2) Again, how can we determine the exact number of encoded bytes to send?
clientSocket.BeginSend( encData, 0, ??how-many-bytes??, ... );
outEnc.Close();
出于测试目的,我们将编码的缓冲区写入文件,而不是套接字。尝试解密生成的文件时,会引发以下异常:CryptographicException:要解密的数据长度无效
从上面的第1)项和第2)项可以看出,我们不知道要传输到客户端(或保存到文件)的编码字节的确切数量。我们试着回忆。定位为FlushFinalBlock()之后的缓冲区大小,尽管没有成功。
请注意,当CryptoStream使用FileStream进行输出时,即既不使用MemoryStream也不使用byte[]缓冲区时,生成的文件会被正常加密,然后成功解密。不过,我们的目标是能够在没有输出流的情况下直接写出加密的byte[]缓冲区。
如果带有byte[]缓冲区的MemoryStream不可行,是否有其他替代方案可以增量加密缓冲区并在套接字上转发它们?
我已经在两种clientSocket.BeginSend(...)
方法中使用memory.Position
作为Count
参数进行了测试,并且能够成功地往返(即加密然后解密加密的数据)。如果这对您不起作用,那么值得提供一个完整的、自包含的、可编译的示例代码来演示这个问题。
经过Iridium的澄清,以下是一个功能齐全的代码块,它使用带有byte[]缓冲区的MemoryStream来加密输入文件中的二进制数据,并将生成的加密数据写入输出文件:
...
RijndaelManaged rjndl = new RijndaelManaged()
{ KeySize = 128, BlockSize = 128, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 };
int blockSizeBytes = 1024 * 64; // blockSizeBytes can be any arbitrary size.
byte [] data = new byte [blockSizeBytes];
byte [] encData = new byte [blockSizeBytes];
int bytesRead = 0;
ICryptoTransform encryptor = rjndl.CreateEncryptor( keyIVBytes, keyIVBytes );
FileStream streamOut = new FileStream( strOutputFile, FileMode.Create );
FileStream streamIn = new FileStream( strInputFile, FileMode.Open );
MemoryStream memory = new MemoryStream( encData );
CryptoStream outEnc = new CryptoStream( memory, encryptor, CryptoStreamMode.Write );
do
{
bytesRead = streamIn.Read( data, 0, blockSizeBytes );
if (bytesRead > 0)
{
outEnc.Write( data, 0, bytesRead );
streamOut.Write( encData, 0, (int)memory.Position );
memory.Seek( 0, SeekOrigin.Begin );
}
} while (bytesRead > 0);
streamIn.Close();
outEnc.FlushFinalBlock();
streamOut.Write( encData, 0, (int)memory.Position );
outEnc.Close();
streamOut.Close();
...