Building a Secure Data Transfer Service Using RSA Asymmetric Encryption (In Go)

How to make data safe from Man In The Middle attack and comply with the regulation.

Purnaresa Yuliartanto
The Startup
7 min readMar 28, 2020

--

Protecting the valuable. Photo by Markus Spiske on Unsplash

Background

Security has been a primary concern in business and government for hundreds of years ago. Government regulation is explicitly mentioned how to handle Customer data. And it is essential to protect your business continuity from vulnerability exploit.

Objective

Send data securely to other parties via an HTTP request. Other parties mean a partner entity which doing a communication to us. Secure parameters are:

  • Data cannot be read by an unauthorized party
  • The data source can be verified from the right party

Components

The service is able to send and receive data. Users will use the service to send data to any party using the same service. Prerequisite information for this setup is each party should have Public Key for each partner.

The diagram below shows how Alice transfers data to Bob using a secure service. The service which Alice use needs Bob’s public key to send a message to Bob and Alice’s private key to sign the message. On the other side, Bob running a service that has his private key to read the message from Alice and Alice’s private key to verify her signature.

Top-view secure data transfer service

Once top-view is understood, we need to see components inside the service. Keep in note that a single service can communicate with many services as long as it has the partner’s public key.

Here is the minimum requirement to build the service.

  1. The private key reader
  2. The public key reader
  3. Message encryptor
  4. Signature writer
  5. Message decipher
  6. Signature verifier
  7. HTTP listener
  8. HTTP post

The diagram below shows the interaction between components.

What's Inside the Service

Code

The code below is the implementation of the requirement above in Go. The service will be able to communicate with any service even with different code language as long as follow the same requirement.

Private key reader

The function will read the private key file in naming format:[name]-private.pem

There are many tools to generate RSA pair keys. It’s recommended to use a 2048bit size.

Public key reader

The function will read the public key given by the partner in the naming format: [name]-public.pem

The input parameter is an array of string in the sense of the service will communicate with many parties.

The output is a map-of-object type with the key is partner name and value is the public key. The map-of-object will be utilized when encrypting data.

Message encryptor

The input of the function is the message. Original data before encryption called plaintext and data after encryption called ciphertext.

The function uses the minimum required encryption which is plaintext and public key.

The output is written in base64 string to make it readable to a human.

Signature writer

The function will use ciphertext from the previous function. Using the private key of Alice (the sender) the output will be a unique signature that can’t be replicated by other parties.

Signature verifier

When receiving a message from other parties, the signature verifier function shall be executed first.

The function will calculate the ciphertext and signature using the partner’s public key. The output is an error object if the signature is not verified.

Decipher

If the signature is verified, the next thing to do is decipher the ciphertext into plaintext. Only the right private key able to read the message.

This is the last step of the flow. We can write out the plaintext in the form of a text display or any kind presentation form.

HTTP post

Now, we need to put all the functions together.

The input is a gin object which comes from gin — a very popular HTTP routing.

We will use this function on HTTP routing when the user calls the service to transfer the data. The parameter required is

  • receiver: partner name, this required to get the right public key
  • host: this is the partner service host address that will be hit after encrypt and sign the message
  • message: the original information to be sent

The function will first encrypt the message, sign the ciphertext and transfer the ciphertext to the partner service. The response to the user is HTTP Status to tell the partner service is reachable or not and ciphertext for that sent.

This is the complete flow to send data securely to other parties.

HTTP receiver

This function is executed when the service receives data from other services. Put all the necessary functions to make the data available to the user. First, it matches the public key and the sender. Then verify the signature based on the public key. If the signature is not verified, it tells the user there is a message that not verified. If the signature is verified, the function will decrypt the message into plaintext and print the plaintext for the user.

Service Main

To make all function executable, we need to build the main function with HTTP routing.

During the service initiation, it will read the flag to allow one service to be deployed by every party. The service required 3 flag

  • name: the service user — service will use the private key that matches the user name
  • contacts: is the partner's name separated in comma ‘,’ — service will use the partner public key that matches the partner's name
  • port: is the port that the service will run

And that will be all, the service is able to do two full flow, sending data and receiving data.

Testing

To test the service, we need to build the Go file. Example to build the service with the name secure-comm is following:

go build -o secure-comm

Copy the file into two different folders for Alice and Bob. Put Alice’s Private key and Bob’s public key into folder Alice. Put Bob’s private key and Alice’s public key in folder Bob.

➜  test git:(master) ✗ tree
.
├── alice
│ ├── alice-private.pem
│ ├── bob-public.pem
│ └── secure-comm
└── bob
├── alice-public.pem
├── bob-private.pem
└── secure-comm

Now run this command on folder Alice

./secure-comm -name=alice -contacts=bob -port=8080

The command use Alice for name’s flag, Bob for contacts’s flag and 8080 for the port’s flag. Running in default gin HTTP routing configuration, the output will be like this.

Running the service at folder Alice

Run the following command in folder Bob.

./secure-comm -name=bob -contacts=alice -port=8081

It works similar way like previous command execution for folder Alice except for the different port — which is 8081. It means the service is running in 8081.

Now, we need to test the communication. Here is the HTTP call in the CURL command simulating scenario where Alice sends the message to Bob.

curl --location --request POST 'http://localhost:8080/send' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'receiver=bob' \
--data-urlencode 'host=http://localhost:8081/receive' \
--data-urlencode 'message=hello Bob, how are you'

The service in folder Alice will response with the following response. This means the data is sent to Bob encrypted.

{"ciphertext": "i5WGunu3+qMTUTaClS09whPhtxDn3mKMahcMQPo7IHmQEutIeDuGzJkoS7i/kDl85DLv0YgHJtdF6kuR99u9SBL7SMN3PdhqtyPpDyEZal6H8N+4wndB688Cw0SRtOIHJraDFbrBTt2bB13gRQX1l+VNv5MwsDswJ9Gv3UsaKL6Gpi2SatD/BcAsSd7bTeM8cS7ekZ55lV45Ll7je84cKVa7auOvWwuFiY8QdGEJBer0MQ96yB92cPxxvN9/kLfwQ2i0b2d1F1LrWstXDu26wXIiROmAgeQeIahx8+lTFGedLR8y2yY5xvdBAG7caYMgRl1yniSQXVp9we33LOMAuA=="}

When we check on folder Bob service, the service will print out the message from Alice.

Message from Alice to Bob

That’s mean the Bob will be able to read the message from Alice. End-to-end testing is complete.

Bob actually able to reply by doing an HTTP call to the service in folder Bob at port 8081. Here is the screenshot of a conversation between Alice and Bob using the secure data transfer service.

Conversation between Alice and Bob

Footnote

The service we build here is aligned with the two security principles — Confidentiality and Integrity. The implementation, of course, can be adjusted to real-life business cases. As long as the flow and component are the same, this service can be built in various other languages. The full code repository is available here

Prerequisites for using this service are having a pair of RSA Keys. Here is how to generate the RSA Keys.

Hopefully, this service will be useful to you. What do you think the next improvement would be?

--

--

Purnaresa Yuliartanto
The Startup

IT architect at best cloud provider in the planet. Experience in cybersecurity and tech-fire-fighting.