Send your request Join Sii

Cybersecurity has become a fairly popular issue last days. It is influenced by public incidents related to data leaks, hacker attacks, scandals related to Pegasus spyware, surveillance and information interception. A reckless approach to the issue of data security in the network and, above all, the lack of knowledge about good practices in the context of security, additionally increases this problem. It is therefore a topic worth taking a closer look at.

Curious and at the same time inspired by these topics, I decided to try my hand at designing and implementing an internet instant message communicator, the main assumption of which is to ensure the highest level of security and user privacy. The application that was created is only a concept and have mainly educational values. The time spent on implementing it, above all, is a great fun and learning for anyone who would like to deal with such a topic. At the same time, it is a great opportunity to introduce You to issues related to cryptography.

The following article is intended for software developers who would like to use their theoretical knowledge of cryptography in a practical project. Feel free to read and play with the source code.


Algorithms are an inherent part related to cybersecurity and cryptography. The knowledge of cryptographic techniques and the ability to apply them in practice will be useful to any software developer. So, improving your skills in this area is a good investment.

At the very beginning, however, it should be mentioned that no algorithm or technique provides 100% security. When choosing a protection method, we must take into account the ratio of the value of the information we protect to the costs that a potential attacker will have to incur to extract this information. In practice, it comes down to balancing the outlays that must be incurred to break the security in relation to the value of the protected information.

For example, information about what we ate for breakfast does not have to be encrypted with some terribly complicated algorithm, i.e. incurring the costs associated with its protection, because it is generally worthless, and no attacker will spend large resources on it to gain access to such information. Facebook password will be enough 🙂

On the other hand, the hypothetical file with passwords to our bank accounts (despite the fact that such a file should not even exist) is already quite valuable and should be subject to greater protection, because the attacker is also able to spend more resources on reaching the information contained therein.

In the context of our instant messenger, we will limit ourselves to the basics of symmetric and asymmetric cryptography. Correctly applied techniques have an impact on the final security of data exchange in the application. Proven, certified and tested algorithms have an advantage over self-made solutions, they guarantee a certain level of security and protection against certain types of attacks.

Symmetric cryptography

The most popular group of cryptographic methods are those based on symmetric cryptography. They are based on the assumption that the encryption and decryption operations are symmetrical, that is, they use the same common key. However, the encryption and decryption algorithm itself need not be the same in both operations.

The further classification of symmetric algorithms leads to the separation of block and stream algorithms. Block algorithms are only able to operate on a fixed-length data block, while stream algorithms accept any input data stream. There are also adaptive techniques that make it possible to use the block algorithm also to work with the data stream.

The main advantages of symmetric related to asymmetric cryptography algorithms are:

  • relatively high speed of operation,
  • simpler and less resource-consuming implementation.

Basic high-level AES encryption and decryption operations are shown in Figures 1 and 2. This particular implementation corresponds to the AES-CBC mode. In other operational modes, the use of the initialization vector may be slightly different. AES uses different algorithms for encryption and decryption operation, but for these both uses the same key, that’s why it is symmetric algorithm.

AES encryption
Fig. 1 AES encryption
AES decryption
Fig. 2 AES decryption

In contrast, a simple algorithm based on the XOR function only, uses the same function for both encryption and decryption operations. It is shown in Figure 3.

Simple XOR encryption/decryption
Fig. 3 Simple XOR encryption/decryption

Asymmetric cryptography

Asymmetric cryptography algorithms are based on the assumption that we use two different keys for encryption and decryption. One of the keys is the public key and the other is the private key. Sharing the public key doesn’t affect the security of data used for private key operations in any way.

At this stage, I have not specifically marked which key we use for encryption and which for decryption, because asymmetric cryptography offers two other operations that we can perform with private and public keys. These are signing and signature verification operations.

Thus, we can distinguish two groups of operations:

  • encryption and decryption (shown in Figure 4),
  • signing and signature verification (shown in Figure 5).

For data encryption and decryption:

  • public key is used to encrypt data (anyone who has access to the public key can encrypt the data),
  • private key is used to decrypt the data (only the owner of the private key can decrypt the data).
Asymmetric encryption/decryption
Fig. 4 Asymmetric encryption/decryption

For signing and signature verification:

  • private key is used to sign data (only the owner of the private key can sign the data with his unique private key)
  • public key is used to verify the signature (anyone who has access to the public key can verify that the data has been signed using the correct private key)
Signing and verification scheme
Fig. 5 Signing and verification scheme


Fortunately, educational projects have their own rules, so we can let our imagination run wild and assume that the information exchanged between the communicator’s users will be super top secret, and that decryption of messages will be practically impossible. Of course, you have to look at it with a grain of salt and have in mind that the concept is more important than the actual functionality.

Even the most sophisticated cryptographic algorithms, which can only be broken by brute-force and will take a million years on supercomputers, are useless if we do not properly secure access to the key. Thus, any cryptographic algorithm provides protection only to the extent that the key is secure.


Two cloud services are required for the communicator to work. They are the “central” part of the system that allows you to establish connections between any communicator users. This assumption cannot be easily avoided and is simply due to the inability to connect directly between hosts in internal networks behind NAT, without public IP addresses. Of course, there are technical possibilities to deal with this problem as well, but it is not crucial from the point of view of the communicator design.

The high-level simplified system architecture is presented in Figure 6.

High-level simplified architecture
Fig. 6 High-level simplified architecture

Message broker

The first service is a message broker that acts as an intermediary in the exchange of messages between the users of the messenger. As long as the messages are end-to-end encrypted, there is no problem with messages going through an intermediary as they are fully encrypted. Assuming that encrypted messages can always be intercepted, this is not a problem. Anyone can intercept an encrypted message, but only the intended recipient will be able to decrypt it.

The only public information that will be openly available on the broker side is the name of the message recipient. This is an acceptable simplification, resulting from the operation of the MQTT broker (subscription to a specific user-related topic), the use of which has greatly simplified the architecture and operation of the messenger.

Theoretically, this information can also be anonymized (which would increase the complexity) or even broadcast messages to all users (because only the right addressee will read it anyway) by completely removing this information, but it would have a critical impact on system performance.

As the messages are encrypted, we can even use the public MQTT broker, it does not significantly affect the security of the system. We do not have to use an encrypted TLS connection or even broker certificates, because the messenger application layer takes care of the security and authentication of messages. A simplified method of data exchange is shown in Figure 7.

MQTT broker communication
Fig. 7 MQTT broker communication

Public keys provider

The second service necessary for the functioning of the communicator is a service that provides users’ public keys. The use of asymmetric cryptography with public keys determines the necessity to share the public keys of individual communicator users. Therefore, we must ensure that every messenger user has access to the public key sharing service. It has to be a centralized system with a database and API.

Of course, it is only needed when we do not have access to the recipients’ public keys offline and when we do not have access to the public key of the author of the message (for its verification). If we assume that each user can exchange information with everyone else at any time, the online service of sharing such public keys becomes essential.

To maintain security and ensure the credibility of the origin of the public keys, each message containing the server’s response with the keys is also signed with the private key located on the server. The server’s public key is available in the messenger client (hardcoded in the source code), so after retrieving the public keys, they can be verified. This provides protection against spoofing, of course, only as long as the password to the server on which the public key sharing service is running is not “1234” 🙂

It is worth noting here that there are ready-made key sharing systems available on the network (both commercial and free), and the application itself that was created during the work on the communicator is a topic for a completely different article. However, it is quite universal, and it can certainly find more use-cases than just a part of our instant messenger system.

A short description of the API of the key sharing system used in the communicator is presented in Table 1.

Table 1: Public keys provider API

Request URLDescriptionResponse codeContent body
GET /users/<username>Get user public keys200{ “response”: { “public_key_message”: “<PUBLIC_KEY>”, “public_key_sign”: “<PUBLIC_KEY>”, } “signature”: “<RESPONSE_SIGNATURE>” }
404User not found
GET /users/<username>/<token>Security token to activate and deactivate user200User activated/deactivated
404User/token not found
POST /usersRegister new user201{ “username”: “<USERNAME>”, “email”: “<EMAIL>”, “public_key_message”: “<PUBLIC_KEY>”, “public_key_sign”: “<PUBLIC_KEY>” }
400Bad request

Registration in the key sharing system requires only the e-mail address to which one-time activation and deactivation links will be sent. This is a necessary process to protect the system from bots that will flood the database with non-existent users. Inactivated user accounts are automatically removed from the database. The messenger itself doesn’t require username registration to make it work.

Description of the public keys provider software with source codes can be found here.

Encryption and sending

In order an encrypted message to be verified and decrypted only by the addressee, its preparation requires several steps on the sender’s side. This is shown in Figure 8.

  1. Generate a message-unique symmetric key.
  2. Encrypt the message using a symmetric key.
  3. Encryption of a symmetric key with an asymmetric private key.
  4. Signing the message using the private key.
  5. Combination of the encrypted key, encrypted message and signature, these 3 elements constitute the entire encrypted message that can be safely sent to the addressee even through a public channel.
Message encryption scheme
Fig. 8 Message encryption scheme

The private and public key pair used to encrypt and decrypt symmetric keys is different from the key pair used to sign and verify the signature. Each messenger client therefore has a total of 4 keys. Two of them are public keys, placed on the key-sharing service, and two are private keys, available only to the user.

The message is sent as shown in Figure 9.

Message sending process
Fig. 9 Message sending process

In order to minimize the network traffic, in particular the polling of the website that provides public keys, a local cache for the public keys was used. Once used, the public key is stored in local memory and when it is used again, it is retrieved directly from the memory instead of from the key server. The key is kept in memory only until the end of the messenger application.

Receiving and decryption

The message is received from the MQTT message broker by subscribing to a topic associated with a specific messaging client. After receiving the message, the client performs several steps as shown in Figure 10.

Message receiving process
Fig. 10 Message receiving process

The process of decrypting the message itself at the recipient side also requires several steps. This is shown in Figure 11.

  1. Decryption of a symmetric key with a private key.
  2. Decryption of the message with a decrypted symmetric key.
  3. Verifying the signature of the decrypted message using the sender public key.
Message decryption scheme
Fig. 11 Message decryption scheme


In the context of communication and establishing an encrypted connection, it is often referred to as a session of a secure encrypted connection. Setting up and maintaining sessions could be a relative complex procedure, especially if we want to conduct several independent conversations with different users. From a functional point of view, sessions (at the messaging application layer level) can be a useful solution. Thanks to it, it is possible to optimize the exchange of keys and additionally send some metadata specific to the active session.

In our case, however, I decided not to implement such a solution. This significantly simplified the operation of the system and, paradoxically, increased its security, as each sent message is encrypted with a separate generated unique symmetric key. In this case, decoding the message string (without access to the private key used to encrypt the symmetric key itself) will require each message to be decoded independently. Breaking the key of one message does not affect the security of the others.

Message format

Messages are sent between messenger clients using the JSON format. It is a text format, so the binary data that we receive after encryption must be additionally encoded to be represented in text form. The Base-64 encoding was used for this.

The encrypted message exchanged between the communicator’s clients consists of 3 fields:

  1. cipherkey – encrypted symmetric key
  2. ciphertext – encrypted message
  3. signature – message signature
"cipherkey": "<encrypted_key>",
"ciphertext": "<encrypted_message>",
"signature": "<message_signature>"

The unencrypted message containing sensitive data from the security point of view consists of 4 fields:

  1. from – sender username
  2. to – recipient username
  3. timestamp – message sending timestamp
  4. message – message
"from": "<sender_username>",
"to": "<sender_username>",
"timestamp": <unix_timestamp>,
"message": "<text_message>"


The Figure 12 shows a detailed diagram showing an example of communication between two users of the messenger, and with a third user trying to listen in on the exchange of messages.

Example process flow
Fig. 12 Example process flow


I used the Go language to implement both the messenger client and the key-sharing service applications. Go language (sometimes called Go-Lang) is a general-purpose language, but with quite wide support for building server applications. Additionally, in the set of Go tools, we get multiplatform support and build to native code. In the context of security, the advantage of compiled programs over programs written in scripting languages (e.g., Python) is certainly their more difficult analysis and reverse-engineering.

The choice, however, was dictated mainly by personal preferences and the desire to consolidate the skills related to this fairly fresh and constantly developing technology from Google. The implementation of the entire software, including testing and tool investment, took about 2 weeks. This is quite a good result for an extensive PoC.


To create and deploy a fully functioning communicator system, we need a server on which we will run a public key sharing service. The server must be accessible to the public for every instant messenger user to have access to the key sharing service. In addition, the service that provides the keys uses an e-mail account to send activation links. This software is deployed to the server and run in the docker container.

The default messaging broker of our messenger is the test broker available at Nothing prevents you from using other message broker, it is not a key element of the system from the security point of view, but it ensures the exchange of information between system users. This particular choice was dictated only by good knowledge of tools to support the mosquitto broker.

Client application

The client software was created as a simple tool with a CLI interface for sending and receiving messages. Over time, an interactive mode was added that allows you to have conversations in traditional chat mode.

Application Quick Start Guide:

usage: nes <Command> [-h|--help] [-b|--broker "<value>"] [-p|--provider
       "<value>"] [-P|--provider_key "<value>"] [-k|--private_message
       "<value>"] [-K|--public_message "<value>"] [-j|--private_sign
       "<value>"] [-J|--public_sign "<value>"] [-u|--user "<value>"]
       [-c|--config "<value>"]
       NES messenger


register  Register username at PubKey Service
listen    Listen to messages
send      Send message to recipient
config    Configuration management
generate  Generate private and public keys pair
chat      Interactive chat with recipient
version   Application version


-h  --help             Print help information
-b  --broker           MQTT broker server address. Default:
-p  --provider         Public keys provider address. Default:
-P  --provider_key     Provider public key. Default:
-k  --private_message  Private key file for message. Default:
-K  --public_message   Public key file for message. Default:
-j  --private_sign     Private key file for signing. Default:
-J  --public_sign      Public key file for signature verification. Default:
-u  --user             Local username. Default: <os_user>
-c  --config           Optional config file. Supported fields:
                 PUBLIC_KEY_SIGN_FILE, NES_USERNAME. Default:

You can find the application with a description, user manual and source codes here.


A video showing the simple data exchange is attached below. In order to enable visualization, the two communication sides are on the same host. Of course, nothing prevents you from having a conversation from two different computers, this is the purpose of the internet instant messenger application 🙂


The created communicator carries out its task. It enables the secure messages exchange between users. The multitude of technologies used and issues necessary to run the entire system make the project interesting and educational. The main challenge during the implementation was to combine the various parts of the system into a working whole. This required running smaller parts of the system, testing them, and integrating them into larger and larger modules. This reflects a common approach to developing virtually any software.

The application is certainly not perfect, it only implements the most important functionality, which is encrypted, secure messages exchange between users. A functional messenger should have much more functionalities, i.e. user statuses, contact lists, conversation histories, files exchange. The information exchange protocol is not complicated, so it can also be implemented on mobile devices as well as in embedded systems or broadly understood IoT. The development potential is therefore considerable. As the project was originally created as open source, there may be someone who will improve this application. I encourage you to try and experiment in this field!

5/5 ( votes: 11)
5/5 ( votes: 11)
Marcin Borowicz

Software Engineer at the Embedded Competence Center at Sii. He has over 10 years of commercial experience in the automotive and industrial electronics industry. He effectively uses his electronic education and acquired engineering experience in projects requiring knowledge of low-level issues. He also actively participates in typical software projects in the IT industry. A programming polyglot who uses many programming languages ​​on a daily basis. Privately, a busy father and a practicing fan of classic motoring.

Leave a comment

Your email address will not be published. Required fields are marked *

You might also like

More articles

Don't miss out

Subscribe to our blog and receive information about the latest posts.

Get an offer

If you have any questions or would like to learn more about our offer, feel free to contact us.

Send your request Send your request

Natalia Competency Center Director

Get an offer

Join Sii

Find the job that's right for you. Check out open positions and apply.

Apply Apply

Paweł Process Owner

Join Sii


Ta treść jest dostępna tylko w jednej wersji językowej.
Nastąpi przekierowanie do strony głównej.

Czy chcesz opuścić tę stronę?