Description: A simple project showing how encryption protects our everyday communication and digital life.
Project Status: Completed
Tags: #Privacy, #Cryptography, #Coding, #Networking
We are fortunate to live at a time when ‘secure’ communication is part of everyday life. However, as technical individuals, we often take it for granted that everyone knows how these technologies work. WhatsApp, Telegram, and Signal boast ‘end-to-end encryption’ that promises to keep your private message secure, receivable, and viewable by the intended person. Yet, I wondered if there is a simple way to demonstrate the difference between unencrypted and encrypted communication.
It isn't easy to visualize the difference between unencrypted and encrypted communication between two or more people or devices. One solution I came up with was to build a simple application to allow two individuals to chat with each other—a dumbed-down version of WhatsApp. But life is rarely simple. I will need to make two versions of the same app—one version will provide communication without encryption (aka plaintext), and the second version will implement encryption communication.
To address the white elephant, yes, an easy way to demonstrate this would be to compare communication using Telnet and SSH, but where would the fun be in that? 🤓
This article will cover enough technical bits to describe the differences without getting too technical or complicated. But, there are a few sections where I will need to get a bit technical, and I will try to provide relatable analogies to (hopefully) explain what is going on.
If you are not interested in the technical details and want a quick summary, here it is!
Let’s start by stating that it is possible to intercept almost all digital communication. What prevents cybercriminals and ‘other’ interested parties from reading our communication is encryption - either encryption of messages, encryption of network (internet) traffic, or ideally both.
But how can someone intercept my network (internet) data? So long as they can sit on the same network as you (Home, Coffee Shop, or Work WiFi, or if they are connected to your ISP), all digital communication originating from your devices can be intercepted. You can visualize this by imagining two people sitting on opposite sides of a coffee shop and shouting across the room to communicate. Everyone else in the shop can hear and understand what the two are talking about. Similarly, in the example below, because I am on the same network as the client and server, I intercepted their network traffic (messages) and read their unencrypted communication.
If data encryption is implemented within an application, cybercriminals cannot read messages even if they intercept network traffic 😎. In the example below, I cannot read any of the exchanged encrypted messages regardless of whether I grab the first, middle, or last bit of network traffic.
Thus, if you ever communicate over a network, use apps and websites that implement encryption. If you are like me and want to understand how and why things work, read on!
Messaging (Chat) apps are the most straightforward way to demonstrate encrypted communication. Internet Relay Chat (IRC) was among the first text-based chat systems for instant messaging - no emojis, pictures, audio, video, documents, or links. This project will use this as a base and then add encryption to secure messaging.
My messaging app will have two sides: ‘client’ and ‘server’. The server side will launch first and wait ‘listening’ for incoming connections. The app’s client-side will launch second and attempt to connect to the server. Once the connection is established, each side must wait for the other to send a message and reply. I will start with an unencrypted (plain-text) version (easier to build) and then create a second version with encrypted messaging. At the end of the project, I will have four sets of code (although the code between the client and server is pretty similar) with the following features:
Breakdown of server and client-side code functionality for unencrypted and encrypted communication.
This is not how real-world messaging applications work—they provide a lot more features. Each side (client) can initiate a conversation, re-send a message once the other side is online, send multiple messages, and store messages for you to read later. Therefore, the above code will not include any of these features, nor can it be used to send or receive emojis, images, audio, video, or files.
For this project, I thought using Wireshark would be an excellent way to demonstrate how encryption protects network communication.
If you are new to Wireshark or want to brush up, I highly recommend Chris Sander’s Practical Packet Analysis book; it starts with a refresher on networking protocols, encapsulation, hardware, and the Open Systems Interconnections (OSI) reference model before diving into packet analysis.
I used C++ for this particular project, but the same can be done with any other programming language that allows communication over a network. Also, note that the code in this article has been developed on Linux and may need amending for Windows x86/x64 environments.
A connection must be established to enable communication between the client and server. C++ provides ‘sockets’ for setting up this connection and allowing bidirectional data transfer. If you want a refresher on C++ sockets, I recommend the Geeks for Geeks Sockets Programming in C++ article.
If you are new to cryptography, check out my Cryptography article on TechSpot. If you want a refresher, skip to the section on the Types of Cryptography.
For the technical people reading this article, I used the OpenSSL library to implement communication encryption. The library provides functions to generate private keys, manage certificates, and equip clients with encryption and decryption utilities. For this project and demonstration, it will create and manage the Transport Layer Security/Secure Sockets Layer (TLS/SSL) connection between client and server.
To better understand how computers communicate on a network, we will use the example of a building with many windows, similar to the one below.
We can liken a single computer to a building on a network; in the same way, a physical building has a unique physical address, computers will be given a unique number on a network called an IP (Internet Protocol) address.
You have many floors within the building with one or more departments responsible for various functions on each floor - HR, Accounting, Legal, etc. Similarly, your computer (smartphone or tablet) can perform many tasks over the internet (often simultaneously) - work on a Google Document, visit a website, listen to music, stream a video, or message a colleague or friend.
A computer uses ports to handle and differentiate all this simultaneous incoming and outgoing data. Ports are like windows and doors on a building, granting access to specific floors and their departments. There are 65,535 ports in total, but not all are commonly used. For example, when you access a website, port 443 is used for secure web browsing, aka HTTPS.
Visualizing a computer IP address and ports using a building with a physical address and many windows.
If a messenger is sent to deliver a package to a particular person, they will need the building’s physical address, floor number, department, and name. Similarly, when you access a website, the server replying to you will need to know your physical address (IP address), window or door granting access to your floor number (port number), and department (application, e.g., a web browser or messaging app).
In this case, a server is just another computer on the Internet with its IP address, specific open ports (Port 443 for secure web browsing or Port 993 for secure email via IMAP—Internet Message Access Protocol), and a particular service provided, e.g., a website or email service.
For this project, the client will connect to the server by providing its physical address (IP address) and window/door to the specific floor (port). The server will confirm the details are correct and allow the two corresponding departments (messaging apps) to communicate.
A simple way to visualize this is by imagining an accountant in Building A shouting out the window to another accountant in Building B.
IP Address and Port communication is the same as two people across the street from each other shouting out their office windows to communicate.
Before we get into the actual code, explaining what happens when this messaging app is executed would be prudent.
The server app must be launched first. It will prompt the user (‘Admin’) to input a port to create a listening socket (open door/window) to accept chat messages. You can think
The client app must be launched next. It will prompt the user (‘Client’) to input the server’s IP address and port and attempt to create a socket to connect to the server.
The server will accept the connection, return the Admin’s name, and wait for the client to return their name. Once complete, the two can begin chatting with each other.
The functionality described above is enough to allow two people to communicate. Still, like the two accountants (or coffee shop patrons) across the road, everyone else can hear and understand what they are talking about. Hence, we need to hide our communication.
The code used for plaintext and encrypted communication is pretty much the same. The only difference is how socket-streamed data is handled before transmission, and I will focus on that. The article from Geeks for Geeks (mentioned earlier) covers C++ sockets pretty well, and thus, I will not review this bit of code.
In real-world applications, Public Key Cryptography (PKC) ensures privacy and security over insecure channels of communication by using two keys—a public and a private key (Asymmetric Encryption). When the client first connects to the server, the latter will share its certificate with the client, who can, in turn, use Certificate Authorities (CAs) to verify the server’s identity (my code assumes the server is trustworthy—not ideal). The client and server then exchange public keys to establish a Symmetric Encryption channel and critical; the key is unique to each ‘chat’ session and is used to encrypt and decrypt messages. This is, again, a high-level overview to simplify the process, but it is a lot more complicated.
To make all this possible in my code, I used the OpenSSL library (mentioned before) to implement the TLS protocol; OpenSSL must be installed on my system before compiling the code (make sure to do this first).
On both the server and client side, I need to set up the SSLContext, which is used to set up various details such as certificates, algorithms, verification, etc. This is done via the C++ function:
Server-Side SSL Context
Client-Side SSL Context
On the server side, if create_context() executes without error, an SSL context is returned and can be used to load the server’s certificate (which also includes its public key) and private key to configure the session’s SSL context utilizing the configure_context() function. This function requires two external files - server.crt and server.key; these must be generated using the OpenSSL library before executing the compiled code for the encrypted server app (covered below).
Configure Server SSL context with certificate and private key
Server-side SSL context initiation and configuration
On the server side, these two functions will set up everything needed to enable encrypted communication, but they must be called before all other processes are executed.
Like the plaintext server-side code, I set up a socket on the server and bound it to the server’s IP address and listening port provided by the server-side admin/client. The server will patiently listen to a client if all the checks pass.
Client connects to server and server confirms
Similarly, both client code bases (unencrypted and encrypted) will request their user for the server's IP address, attempt to connect to the server, create the SSL context, and create a socket.
Client-side socket set-up code
Create encrypted client-server communication socket
The server will attempt to accept the incoming client connection; if successful, it will create a client socket, the ‘SSL structure’ for the connection between itself and the client using the SSL_new() method. The ‘structure’ is based on the settings of the SSL context, and sets the file descriptor for the input/output facility for the TLS/SSL encryption connection for SSL via the SSL_set_fd() method. After accepting the incoming connection, the input/output facility will be the client socket created by the server.
Before any communication can proceed, the server and client must establish the rules for encryption and communication, which is handled by the SSL_connect() and SSL_accept() methods. The client-side SSL_connect() method initiates the TLS handshake with the server, performs certificate verification, and completes the steps needed to establish a secure channel. The server-side SSL_accept() the method waits for the client's TLS handshake and provides the necessary information before exchanging the keys to establish the symmetric encryption required for secure communication. For this project, the server proves its identity using its private key to sign (encrypt) specific data' for the client;' only the server with the correct private key can successfully establish the secure TLS session.
Everything before these two methods is not encrypted; no data is transmitted across the network.
From here on out, all data exchanged across the network is encrypted, including their respective user names, ‘chat’ data, and communication terminate command. The ‘Chat’ messages between the client and server are encrypted and decrypted automatically by the OpenSSL methods SSL_write() and SSL_read() respectively.
Before compiling the code, I need to install the OpenSSL library, which I can do by executing this command:
openssl req -new -x509 -days 365 -nodes -out server.crt -keyout server.key
This command generates two files: the server certificate (and public key) - server.crt and server private key - server.key.
Note:
You must run your encrypted server-compiled code in the same directory where these two files are created.
If you change the names of these two files when running the above command, update the configure_context() method of the encrypted server code.
From the Linux terminal, navigate to the directory that contains the plainclient.cpp and plainserver.cpp files and execute the following commands:
g++ server.cpp -o server
g++ client.cpp -o client
From the Linux terminal, navigate to the directory that contains the encclient.cpp and encserver.cpp files and execute the following commands:
g++ server.cpp -o server -lssl -lcrypto
g++ client.cpp -o client -lssl -lcrypto
The two flags -lssl and -lcrypto for the encrypted code compiling link the OpenSSL libraries for SSL and cryptographic functions to the code.
To execute the client and server side, run the following commands, follow the prompts, and begin chatting.
./server
./plaintext
./encserver
./encclient
Encrypted communication between client and server.
You can download all the code from my GitHub repo here: Encrypted Chat
During development, I had trouble connecting the client and server-side applications. After some troubleshooting, I realized the local firewall was blocking the two apps; switching off the firewall resolved the issue, and two apps could connect.
In future code iterations, I will work on adding functionality to identify if the port is opened for listening and test whether a firewall is present on both the client and server side. After this, it would be interesting to see if I can intercept the initial negotiation to compromise the ‘secure’ channel and retrieve any messages shared between the two.
Analyzing the packets sent across using the encrypted version of the application was quite interesting; all data is encrypted and transmitted using the TLS protocol, with each side confirming receipt via the TCP ACK packet.
The termination of the plaintext communication versus the encrypted communication was also different. Unlike the plaintext communication that ended with a TCP packet with the FIN/ACK flags set, the encrypted communication ends the connection via a TCP packet with the RST flag set.
Plaintext versus Encrypted socket termination TCP flags
All in all, this was an exciting project from a coding, security, and network perspective.