Encryption as a Service with Vault

Yiğit İrez
5 min readDec 28, 2021

Every developer has used encryption at some point and while easy to use, security is a major issue. You need those keys safe. You could of course stuff your keys in Vault key-value secrets engine but why manage keys at all. Let Vault handle it with the Transit Engine.

What transit engine does is, it provides encryption as a service so you don’t have to deal with or know encryption features and requirements.

What we need

  • Vault, somewhere (I’m using local)
  • Postman to send service requests if you want, bash is fine

Goal: Transfer encryption responsibilities to Vault and encrypt regular data without having any keys within our host.

The short version: Your app sends data it wants encrypted to Vault Transit Engine. Vault then encrypts and sends a cipher text of your data back to your app. The app can then store this encrypted data in any environment and send the cipher text back to Vault again to receive decrypted version.

How it works

Think of a case like this. You have sensitive data fields like accounts, private case details and such. These fields must be encrypted on the storage whether its db or anywhere else. You can manage access to these resources but generally storage and db (let’s call all of it storage from now on) are managed by another team and may be out of your control. Additionally, while there are other encryption tools available, you will just be adding more people and more tools to the equation, increasing the risk. When we add Vault to this equation, we just need a single tool to manage both our secrets and encryption capabilities. Vault does not hold anything we encrypt using the transit engine.

Let’s see the usage in example. Enable a transit engine like below;

let’s get the engine running
usual basic config

I’m intentionally changing engine root directory so we don’t mistake it in commands. For the next part, we need to create an encryption key;

our top secret key

After the key creation, check out the options we can now perform.

all is ready

From the UI, we can make basic tests. Write random things in the encrypt plain text field and click encrypt.

doing basic tests via UI

You will get a cipher text like below. This is the data that needs to be stored and will use this to convert it back to the original data.

vault:v1:KgAvW98a2EVjmnlNH2BsMxKR5F+mbrFjZRWR2seE3D0MTcVhYU4DPvf8IdZZ

Now going back to the transit engine and selecting decrypt tab, we can place the above cipher text and click decrypt.

base64 version, noice
a random site for base64 decoding because I’m lazy

Same operation can be done from the cli as well. We need to use base64 for the plaintext field which is the same as the previous example (test encrypt data)

vault write transittest/encrypt/my-super-secret-encyrption-key plaintext="dGVzdCBlbmNyeXB0IGRhdGE="
encryption via cli

Using the ciphertext to decyrpt we get the same base64 we sent.

vault write transittest/decrypt/my-super-secret-encyrption-key ciphertext="vault:v1:X0B3STGriYAH7f/xVJQ18hu/Uhp4DMVE8+DvxEP/xldzRvEXVuDA8XI7/0gJ"
decyrption via cli

One thing to mention is the key we created for encryption operations needs to be protected otherwise everything encrypted by the transit engine can be decrypted.

To this end, instead of flinging the main data key around, we can generate a key for apps to own and use to encrypt, decrypt their data (using Vault still). This applies specifically for large data that needs to be encrypted as well. Instead of sending and receiving large chunks of data, apps can manage their own encryption with keys provided from Vault. They could be rotated daily to minimize risk and source would still be Vault.

Let’s create a key from our current key as below;

a key from our original key
the key ciphertext which can be en/decrypted with the original key

We can now encrypt data using the key in the Plaintext field and delete that key after we are done. But we need to save the cipherText when we want to decrypt since it contains our key in encrypted format. Using decrypt again we can reach the generated key.

As best practice, we can store the ciphertext for our key in the kv store.

base64 version

So far we have been using a single key to encrypt/decrypt everything. Well, transit engine supports key rotation, versioned keys and minimum key version for decryption,

In all of our encrypt operations we received a cipher text like below so far

the ciphertext

The v1 here is the version of our key. We can rotate it to create a new key as below;

multiple versions of the encyrption key

Selecting edit, we can choose the minimum versions to decrypt and encrypt. Below I changed to min decrypt version to 2.

Also notice the Allow deletion checkbox. It’s false by default because if you delete a key, encrypted data cannot be decrypted anymore so it’s a matter to think about.

some nuclear options here

Get a ciphertext you have belonging to v1 and try to decrypt it now.

noice

In case of emergency, the min decrypt version can be moved to allow this data to be decrypted as well.

One nifty thing Vault does for situations regarding data encrypted with old key versions is, it can rewrap the encryption with the current version.

Of course, we need to allow v1 we disabled previously for this operation to work,

repackaging old christmas gifts
v3 rewrapped data ready for action

Thanks for reading and see you next time.

--

--

Yiğit İrez

Let’s talk devops, automation and architectures, everyday, all day long. https://www.linkedin.com/in/yigitirez/