使用加密从文件数据,并且经由发送的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参数进行了测试,并且能够成功地往返(即加密然后解密加密的数据)。如果这不适合你,那么值得提供一个完整的,自包含的,可编译的例子来演示这个问题。

+0

你绝对正确!在**中使用memory.Position ** 1)和2)为我们解决了它。在我们使用.Position之前,只能在FlushFinalBlock()之后。非常感谢! – alexg

澄清通过上述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(); 
...