0

我们有一个套接字服务器,它需要将加密的二进制文件传输到连接的客户端。当请求到达时,文件需要加密,最好不要先在磁盘上制作文件的加密副本。

由于我们使用异步套接字 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() 之后尝试将 memory.Position 作为缓冲区大小,但没有成功。

请注意,当 CryptoStream 使用 FileStream 作为其输出时,即既不使用 MemoryStream 也不使用 byte[] 缓冲区时,生成的文件被正常加密,然后成功解密。不过,我们的目标是能够在没有输出流的情况下直接写出加密的 byte[] 缓冲区。

如果带有 byte[] 缓冲区的 MemoryStream 不可行,是否还有其他替代方法可以增量加密缓冲区并将它们转发到例如套接字上?

4

2 回答 2

1

我已经在这两种方法中使用memory.Position作为Count参数对其进行了测试clientSocket.BeginSend(...),并且能够成功地往返(即加密然后解密加密的数据)。如果这对您不起作用,那么值得为一个完整的、独立的、可编译的示例提供代码来演示该问题。

于 2013-08-19T19:37:27.647 回答
0

在上面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();
...
于 2013-08-20T02:48:28.940 回答