I’m trying to securely transfer files between 2 devices, so I’m using an SslStream attached to a TcpClient. Documents and text files come across just fine, but image files don’t show up correctly. The following is the server code:
listener = new TcpListener(IPAddress.Any, 1337);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), new LocalCertificateSelectionCallback(CertificateSelectionCallback));
var certificate = Connection.GetClientCertificate(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString());
try
{
sslStream.AuthenticateAsServer(certificate, true, SslProtocols.Default, true);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
var messageData = ReadMessage(sslStream);
var mode = messageData[0];
var tokenBytes = messageData.Splice(1, 16);
var fileNameBytes = messageData.Splice(17, 128);
var fileBytes = messageData.Splice(146);
var fileName = Encoding.ASCII.GetString(fileNameBytes).TrimEnd('\0');
using (var tempFile = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))
{
tempFile.Write(fileBytes, 0, fileBytes.Length);
tempFile.Flush();
}
if (mode == 0)
tempFiles.Add(fileName);
Process.Start(fileName);
}
catch (AuthenticationException e)
{
MessageBox.Show("The other side failed to authenticate.");
}
finally
{
sslStream.Close();
client.Close();
}
}
And ReadMessage is defined as follows:
private static byte[] ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
MemoryStream stream = new MemoryStream();
int bytes = -1;
while (bytes != 0)
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
stream.Write(buffer, 0, bytes);
}
return stream.ToArray();
}
And then the client code is this:
TcpClient client = new TcpClient();
client.Connect(new IPEndPoint(IPAddress.Parse(ip), 1337));
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), new LocalCertificateSelectionCallback(CertificateSelectionCallback));
var certificate = Connection.GetClientCertificate(ip);
try
{
sslStream.AuthenticateAsClient(ip, new X509CertificateCollection() { certificate }, SslProtocols.Default, false);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
sslStream.Write(data);
}
catch (AuthenticationException e)
{
MessageBox.Show("The other side failed to authenticate.");
}
finally
{
sslStream.Close();
client.Close();
}
And the code that calls into it just does:
var fileBytes = File.ReadAllBytes(file);
var tokenBytes = Encoding.UTF8.GetBytes(token);
var fileNameBytes = Encoding.UTF8.GetBytes(Path.GetFileName(file));
var buffer = new byte[145 + fileBytes.Length];
buffer[0] = 1;
for (int i = 0; i < 16; i++)
{
buffer[i + 1] = tokenBytes[i];
}
for (int i = 0; i < fileNameBytes.Length; i++)
{
buffer[i + 17] = fileNameBytes[i];
}
for (int i = 0; i < fileBytes.Length; i++)
{
buffer[i + 145] = fileBytes[i];
}
SocketConnection.Send(ip, buffer);
Is there anything inherently wrong with what I’m doing, or do I need to do something different for images?
EDIT: I have changed it to reflect the current code, and also, after doing a dump of the raw bytes on both ends, it looks like for some reason the bytes are getting rearranged when they come over the wire. Is there any way to ensure that the bytes come across in the original order?
In
ReadMessageyou’re writingbytes.Lengthbytes to the stream, regardless of the number of bytes that were actually read. Try:Based on your follow-up, you’re also taking the file data from the wrong point in the buffer, and so you’re losing the first byte of the file.
Your code should be: