So far, we have been dealing more with the SSL or TLS client. Now, let us have a look at the server side briefly. As you are already familiar with the TCP/UDP socket server creation process, let's skip that part and just concentrate on the SSL wrapping part. The following code snippet shows an example of a simple SSL server:
import socket import ssl SSL_SERVER_PORT = 8000 if __name__ == '__main__': server_socket = socket.socket() server_socket.bind(('', SSL_SERVER_PORT)) server_socket.listen(5) print("Waiting for ssl client on port %s" %SSL_SERVER_PORT) newsocket, fromaddr = server_socket.accept() # Generate your server's public certificate and private key pairs. ssl_conn = ssl.wrap_socket(newsocket, server_side=True, certfile="server.crt", keyfile="server.key", ssl_version=ssl.PROTOCOL_TLSv1) print(ssl_conn.read()) ssl_conn.write('200 OK '.encode()) print("Served ssl client. Exiting...") ssl_conn.close() server_socket.close()
As you can see, the server socket is wrapped with the wrap_socket()
method, which uses some intuitive parameters such as certfile
, keyfile
, and SSL
version number. You can easily generate the certificate by following any step-by-step guide found on the Internet. For example, http://www.akadia.com/services/ssh_test_certificate.html suggests to generate the SSL certificate in a few steps.
Now, let's make a simplified version of a SSL client to talk with the above SSL server. The following code snippet shows an example of a simple SSL client:
from socket import socket import ssl from pprint import pprint TARGET_HOST ='localhost' TARGET_PORT = 8000 CA_CERT_PATH = 'server.crt' if __name__ == '__main__': sock = socket() ssl_conn = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1, ca_certs=CA_CERT_PATH) target_host = TARGET_HOST target_port = TARGET_PORT ssl_conn.connect((target_host, int(target_port))) # get remote cert cert = ssl_conn.getpeercert() print("Checking server certificate") pprint(cert) if not cert or ssl.match_hostname(cert, target_host): raise Exception("Invalid SSL cert for host %s. Check if this is a man-in-the-middle attack!" %target_host ) print("Server certificate OK. Sending some custom request... GET ") ssl_conn.write('GET / '.encode('utf-8')) print("Response received from server:") print(ssl_conn.read()) ssl_conn.close()
Running the client/server will show output similar to the following screenshot. Can you see any difference in comparison to our last example client/server communication?
Let us inspect the SSL client/server interaction once again in order to observe the differences. The first screenshot shows the entire communication sequence. In the following screenshot we can see that the server's Hello
and certificate are combined in the same message.
The client's Client Hello packet looks pretty similar to our previous SSL connection, as shown in the following screenshot:
The server's
Server Hello packet is a bit different. Can you identify the differences? The cipher specification is different that is TLS_RSA_WITH_AES_256_CBC_SHA
, as shown in the following screenshot:
The Client key exchange packet also looks very familiar, as shown in the following screenshot:
The following screenshot shows the New Session Ticket packet offered in this connection:
Now let's have a look at the application data. Is that encrypted? For the captured packet, it looks like garbage. The following screenshot shows the encrypted message that hides the real data. This is what we want to achieve using SSL/TLS.