简体   繁体   中英

Always receive from ssl socket message in two parts (c# client, python3 server)

I'm writing ssl client in c# (windows) and ssl server in python 3 (linux). My problem is, that I send this (c#):

byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
byte[] empty = Encoding.UTF8.GetBytes("Baa");
sslStream.Write(empty);
sslStream.Write(messsage);
sslStream.Flush();

And I get this in python server:

b'B'
b'aa'
b'H'
b'ello from the client.<EOF>'

My messages are split to two parts, where first always have 1 byte and second have the rest.

Problem is only when I try send from my client to my server. When I tested it with openssl ( openssl s_client -connect 172.22.22.1:10443 and openssl s_server -accept 10443 -cert server.cert -key server.key ) it print correct message.

My ssl server:

#!/usr/bin/python

import socket, ssl

HOST, PORT, CERT = '0.0.0.0', 10443, 'server.pem'

def handle(conn):
  while True:
      data = conn.recv()
      if not data:
          break
      else:
          print(data)

def main():
  sock = socket.socket()
  sock.bind((HOST, PORT))
  sock.listen(5)
  context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
  context.load_cert_chain(certfile=CERT)
  while True:
    conn = None
    ssock, addr = sock.accept()
    try:
      conn = context.wrap_socket(ssock, server_side=True)
      handle(conn)
    except ssl.SSLError as e:
      print(e)
    finally:
      if conn:
        conn.close()
if __name__ == '__main__':
  main()

And my ssl client:

class Program
    {
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,      
              SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
            return false;
        }
        public static void RunClient(string machineName, string serverName)
        {
            TcpClient client = new TcpClient(machineName, 10443);
            Console.WriteLine("Client connected.");
            SslStream sslStream = new SslStream(
                client.GetStream(),
                false,
                new RemoteCertificateValidationCallback(ValidateServerCertificate),
                null
                );
            try
            {
                sslStream.AuthenticateAsClient(serverName, new X509CertificateCollection(), SslProtocols.Tls, true);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine("Authentication failed - closing the connection.");
                client.Close();
                return;
            }

            byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
            byte[] empty = Encoding.UTF8.GetBytes("Baa");
            sslStream.Write(empty);
            sslStream.Write(messsage);
            sslStream.Flush();
            client.Close();
            Console.WriteLine("Client closed.");
        }        

        public static int Main(string[] args)
        {
            string serverCertificateName = "test";
            string machineName = "172.22.22.1";

            RunClient(machineName, serverCertificateName);
            return 0;
        }
    }

My messages are split to two parts...

There is no concept of a message when using TLS on top of TCP. It is only an unstructured data stream and any structures (like message boundaries) have to be added by the application protocol on top of this stream, same as with plain TCP.

While the behavior of the .NET client is strange (and it seems to be caused by the client and not the server) it could also be that recv would return both "messages" at once, return a short message full but a larger message in pieces or similar. So your code and application protocol has to account for this in that it does not assume that recv implicitly handles your message boundaries.

The practice of sending the first byte of the outgoing data separately is called 1/n-1 split and recent client-side implementations do it in order to mitigate the SSL/TLS vulnerability CVE-2011-3389 , also known as "BEAST".

The .Net/OS library used on the sender side performs this mitigation and the results of the underlying trick apparently leak to the application level in the Python code.

It might be possible to avoid this by using different protocol or cipher suite. But as already stated by Steffen, SSL code must expect the data to come split in any way and still handle it correctly anyway, so the solution is to implement some framing of messages (special "end-of-message" character, send length before data, ...) to know where they end without relying on implementation details and luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM