[30 Minutes Hacks] Creating Fernet Key Generator with RabbitMQ Message Broker

Salman Chen
4 min readSep 7, 2021

--

Hi! Welcome to my first 30 minutes hacks! I started this program because I am not sure how to start big, so why not.

Recently I am having interest in cryptography, since Hackthebox is really engrossing. However, I promise this one would not be hard.

Image by Markus Spiske on Unsplash

Fernet Symmetric Encryption

Fernet use 128-bit symmetric encryption in Cipher Block Chaining (CBC) mode, with PKCS7 Padding.

Image from https://sergio.mendonca.pro/post/fernet-symmetric-encryption/

In Python, we can use the cryptography library. To install it, simply use

pip install cryptography

RabbitMQ RPC

RPC is used to run a function (the naming comes when it is run in remote computer) and waiting for the result. RabbitMQ is a good library for this purpose, or maybe you can use Kafka or Redis (but this one need more time to master).

To run RabbitMQ server, I will simply run it in Docker. TL;DR this is how to install Docker.

Then, simply run the server.

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.9-management

The complete guide to install RabbitMQ could be found here.

After that, we need to install pika which bridges RabbitMQ server to Python. you can install it by using pip as well.

pip install pika

And you are all set.

Creating Client

The first part of the code is the client, which simply send the message and wait for the reply. This class is used to operate the RPC client. First, a connection is create to localhost, then the call function send message to the server and waiting for response.

class FernetRpc(object):    def __init__(self):         self.connection = pika.BlockingConnection(             pika.ConnectionParameters(host='localhost'))         self.channel = self.connection.channel()         result = self.channel.queue_declare(queue='',   \
exclusive=True)
self.callback_queue = result.method.queue self.channel.basic_consume( \ queue=self.callback_queue,\ on_message_callback=self.on_response, auto_ack=True) \ def on_response(self, ch, method, props, body): if self.corr_id == props.correlation_id: self.response = body def call(self, n): self.response = None self.corr_id = str(uuid.uuid4()) self.channel.basic_publish(exchange='', \ routing_key='rpc_queue', properties = pika.BasicProperties( reply_to = \ self.callback_queue, \
correlation_id=self.corr_id,), \
body=json.dumps(n))
while self.response is None: self.connection.process_data_events() return (self.response)

Then, we define the message. This is an example.

message = {"text":"I was partying at the construction site yesterday"}

And finally send the message and waiting.

fernet_result = FernetRpc()print(" [x] Requesting user")start = time.time()response = fernet_result.call(message)end = time.time() - startprint(" [v] Got %r" % response)print(" [.] Time elapsed %r s" %end)

Creating Server

This one is the code to receive the message, process it algorithmically, and return the result. We will create the encrypting function here. If no key was given, we will generate a new key.

import pikaimport jsonfrom cryptography.fernet import Fernetconnection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))channel = connection.channel()channel.queue_declare(queue='rpc_queue')def encrypt(text):    key = Fernet.generate_key()    f = Fernet(key)    token = f.encrypt(b"{text}")    return token, text, key

And this part to consume incoming message and return the response based on the encrypting function.

def on_request(ch, method, props, body):    print("load json")    body = json.loads(body)    text = body["text"]    encrypted_text, plain_text, fernet_key = encrypt(text)    response = {"encrypted token": str(encrypted_text.decode( \           "utf-8")), "generated key": str(fernet_key.decode("utf-8"))}    ch.basic_publish(exchange='',  \        routing_key=props.reply_to, \        properties=pika.BasicProperties(correlation_id = \        props.correlation_id), \        body=json.dumps(response)) \        ch.basic_ack(delivery_tag=method.delivery_tag)    channel.basic_qos(prefetch_count=1)    channel.basic_consume(queue='rpc_queue',  \
on_message_callback=on_request)
print(" [x] Awaiting RPC requests") channel.start_consuming()

Fire it up

To run the RPC system, you need to run the client and server. It is not a matter what to run first, since the message goes to queue. We need two terminal for this (actually 3 if you run RabbitMQ docker server).

>>> python encryption_client.py  
[x] Requesting user
[v] Got b'{"encrypted token": "gAAAAABhNlLOKx5q4G95JZdbYgSIBCbyQh3BtxPRIasl4M_Yed4B3sSj9SECzmsRd4FtEE1cgDua8n1Vj74qtfGBGcorlgCMHg==", "generated key": "7KuVFz0qyyMOIFXd-OzRotwpARn77s5ywOXGxdzwRjI="}'
[.] Time elapsed 0.8873090744018555 s

The full code can be found at my Github repository.

References

Check those references for more detailed techniques.

About the Author

Salman is the Chief Data Officer of Allure AI, an emerging AI startup which recommends the personalized skincare products and routines.

He graduated from the Department of Astronomy, Institut Teknologi Bandung with a thesis on the utilization of deep learning to determine stellar parameters from spectra. Previously, he worked as a research assistant for the department and undergone the AI residency at Konvergen AI.

He was involved in the SETI@home project, college humanoid robotics team, collegiate capture the flag and information security competition, Princeton University Physics of Life Summer School 2020, and Machine Learning Summer School Indonesia 2020.

Sign up to discover human stories that deepen your understanding of the world.

--

--

Salman Chen
Salman Chen

Written by Salman Chen

Astro grad student at NTHU — interested in astrophysics and neuroscience, love chocolate and cookies

No responses yet

Write a response