使用加密从文件数据,并且经由发送的MemoryStream的插座上(字节[])C#/ .NET
我们有需要加密二进制文件传送到连接的客户端套接字服务器。 当文件到达请求时需要加密文件,最好是而不是先制作磁盘上文件的加密副本。使用加密从文件数据,并且经由发送的MemoryStream的插座上(字节[])C#/ .NET
由于我们使用异步套接字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)可以看出,我们不知道要传输到客户端(或保存到文件)的编码字节的确切数量。我们试图将memory.Position作为FlushFinalBlock()之后的缓冲区大小,尽管没有成功。
请注意,当CryptoStream使用FileStream作为其输出时,即既不使用MemoryStream也不使用byte []缓冲区时,结果文件将被正常加密,然后成功解密。不过,我们的目标是能够直接写出加密的byte []缓冲区而不需要输出流。
如果带有byte []缓冲区的MemoryStream不可行,是否还有其他替代方法可以对缓冲区进行递增加密并转发它们,例如套接字?
我已经在clientSocket.BeginSend(...)
方法中使用memory.Position
作为Count
参数进行了测试,并且能够成功地往返(即加密然后解密加密的数据)。如果这不适合你,那么值得提供一个完整的,自包含的,可编译的例子来演示这个问题。
澄清通过上述Iridium后,以下是使用的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();
...
你绝对正确!在**中使用memory.Position ** 1)和2)为我们解决了它。在我们使用.Position之前,只能在FlushFinalBlock()之后。非常感谢! – alexg