Frage

We have a sockets server that needs to transfer encrypted binary files to connected clients. The files need to be encrypted when requests for them arrive, preferably without making encrypted copies of files on disk first.

Due to the fact that we use asynchronous sockets API, Sockets.NetworkStream cannot be used. Instead, we need to utilize Socket.BeginSend() with a byte[] buffer as the input.

A seemingly straight forward possibility is to use CryptoStream with a MemoryStream as its destination for encrypted content. MemoryStream itself would use a byte [] buffer as its own data repository.

For example:

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 );

A file can be read and encrypted a buffer at a time, which would then be sent over the socket to the client. For example:

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();

For testing purposes we wrote out the encoded buffers to file instead of the socket. When trying to decrypt the resulting file, the following exception is thrown: CryptographicException: Length of the data to decrypt is invalid.

As can be seen in items 1) and 2) above, we don't know the precise number of encoded bytes to transfer to the client (or save to a file). We tried memory.Position as the buffer size after FlushFinalBlock(), though without success.

Please note, when CryptoStream is using a FileStream for its output, i.e., neither MemoryStream nor byte[] buffers are used, the resulting file is encrypted normally and then subsequently decrypted successfully. Our goal, though, is to be able to write out encrypted byte[] buffers directly without output streams.

If MemoryStream with byte[] buffer is not feasible, are there other alternatives to be able to incrementally encrypt buffers and forward them on, e.g., socket?

War es hilfreich?

Lösung

I've tested this using memory.Position as the Count parameter in both clientSocket.BeginSend(...) methods, and was able to round-trip (i.e. encrypt then decrypt the encrypted data) successfully. If this isn't working for you, it'd be worth providing code for a complete, self-contained, compilable example that demonstrates the issue.

Andere Tipps

After the clarification by Iridium above, the following is a fully functioning chunk of code that uses MemoryStream with byte[] buffers to encrypt binary data from an input file and write out the resulting encrypted data to an output file:

...
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();
...
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top