Envelope Encryption Brief

Envelope Encryption is a high-performance encryption and decryption solution for massive data. Various domestic public clouds have key management systems, referred to as KMS (Key Management Service), which include envelope encryption solutions.

A picture is worth a thousand words (picture from Tencent Cloud Documents):

enveope encrypt decrypt

Glossary:

  • DEK = data encryption keys
  • KMS = Key Management Service
  • AES = Advanced Encryption Standard

Encryption core process:

    1. When the user requests KMS, the system will return to you a pair of keys (plaintext key + ciphertext key). The two have different functions. The plaintext key is used to encrypt local files, and the ciphertext key is used to encrypt local files. Obtain the plaintext key from the KMS system. As for how the KMS system maintains the corresponding relationship between the two, the reader does not need to worry. Anyway, the relationship between the two keys is extraordinary, and each performs its own duties. If you really can't understand it, you can think about the marriage registration office. In traditional marriage, one person does housework at home, and the other person makes money outside.
    1. Use plaintext key to encrypt data, for example, you want to encrypt a test.txt file or something. Those who are impatient may ask, how to encrypt the plaintext key? Don’t panic, I will show you a simple Python example:
    1from Crypto.Cipher import AES
    2key = b'Sixteen byte key' #Here is the plainkey
    3cipher = AES.new(key, AES.MODE_EAX)
    4nonce = cipher.nonce
    5ciphertext, tag = cipher.encrypt_and_digest(data) # Encrypt data and return ciphertext (ignoring nonce and tag first)
    
    1. Assume that the above text.txt is encrypted and named secret.bin. After the local encryption is completed, it must be stored in a place. Corrupt officials in mainland news store their money in walls, safes, vacant houses in the community, etc. Nowadays In the digital age, files are generally stored in cloud storage. When saving, you not only have to store the encrypted file itself, but you also have to store the ciphertext key. Otherwise, it cannot be decrypted, which is a big joke and your work is in vain. Generally, the plaintext key is deleted after encryption, leaving no trace. Store the encrypted file and the ciphertext key together, like wrapping it in an envelope? (So it’s called envelope encryption? That’s my guess...)

Decrypt core process:

    1. After a few years, you will need to use data. First take out the envelope from the storage system. The envelope contains encrypted data + ciphertext key. You use the ciphertext key to request the KMS system to obtain the plaintext key. Then you think of the encryption program above that has been sealed for a long time, and you think about it. Very good. I encrypted it with my own hands at the beginning, and now I decrypt it with my own hands. Everything is like going back to the past. You can decode the secret documents that have been sealed for a long time. I feel like a military spy, mysterious and taking the most credit. This is a familiar feeling!
    1. Back to reality, use the plaintext key returned by KMS to decrypt the data and give a simple example:
    1  from Crypto.Cipher import AES
    2  key = b'Sixteen byte key' # Still the familiar plainkey (the same plainkey)
    3  cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
    4  plaintext = cipher.decrypt(ciphertext) #Decrypt to get plaintext
    5  try:
    6      cipher.verify(tag)
    7      print("The message is authentic:", plaintext)
    8  except ValueError:
    9      print("Key incorrect or message corrupted")
    
    1. After getting the clear text of the data, it is burned after reading it. You will be excited and restless during the day and unable to sleep at night. For decades, you are the only one on this planet who knows this secret. It is incredible 😅.

Okay, the virtual story is over. In reality, you are not Dai Li, and you do not have the support of the Chairman.

Hahaha, writing code is my daily routine...

As the saying goes, helping people to the end, I will give you a relatively complete pseudo-code example below:

 1
 2import requests
 3import base64
 4from Crypto.Cipher import AES #pip install pycryptodome
 5
 6
 7url = "your_kms_url"
 8
 9
10def gen_signature(method, params, secret_key):
11
12     '''
13     signature
14     '''
15     pass
16
17
18def generate_data_key():
19     '''
20     get planinkey and cipherkey
21     '''
22     params = {
23         'xxx1': 'yyy1',
24         'xxx2': 'yyy2',
25     }
26     params['signature'] = gen_signature()
27     r = requests.get(url, params=params)
28     rdata = r.json()["data"]
29     plainkey, cipherkey = rdata["plaintext"], rdata["ciphertext"]
30     return plainkey,cipherkey
31
32
33def get_plainkey(cipherkey):
34     '''
35     use cipherkey get plainkey"
36     '''
37     params = {
38         'zzz1': 'mmm1'
39         'foo': 'bar'
40     }
41     params["ciphertext"] = cipherkey
42     params['signature'] = gen_signature()
43
44     r = requests.get(url, params=params)
45     rdata = r.json()["data"]
46     plainkey = rdata["plaintext"]
47     return plainkeypt
48
49
50def envelope_encrypt(plainkey):
51     '''encrypt'''
52     key = base64.b64decode(plainkey)
53     with open(to_be_encrypt_file, "rb") as f:
54         data = f.read()
55     cipher = AES.new(key, AES.MODE_SIV) # chose your aes mode
56     ciphertext, tag = cipher.encrypt_and_digest(data)
57
58     with open(encrypted_file,"wb") as file_out:
59         [ file_out.write(x) for x in (tag, ciphertext) ]
60
61def envelope_decrypt():
62     '''decrypt'''
63     with open(cipherkey_file,'r') as f:
64         cipherkey = f.read()
65     key = getDatakeyPlaintext(cipherkey)
66     key = base64.b64decode(key)
67     if key:
68         with open(encrypted_file,"rb") as file_in:
69             tag, ciphertext = [ file_in.read(x) for x in (16, -1) ]
70         cipher = AES.new(key, AES.MODE_SIV)
71         data = cipher.decrypt_and_verify(ciphertext,tag)
72         with open(decrypt_file,"wb") as file_origin:
73             file_origin.write(data)
74
75def upload_to_cloud(upload_file):
76     '''
77     upload cipherkey and secret file to cloud like aws s3
78     '''
79     pass
80
81
82if __name__ == "__main__":
83
84     to_be_encrypt_file = 'text.txt' # File to be encrypted
85     encrypted_file = "secret.bin" # Encrypted file
86     decrypt_file = "origin" # Decrypted file
87     cipherkey_file = "cipherkey"
88
89     plainkey, cipherkey = generate_data_key()
90
91     #encryt
92     envelope_encrypt(plainkey)
93
94     #upload
95     upload_to_cloud(upload_file)
96
97     #decrpyt at someday
98     #envelope_decrypt()

The above pseudocode only reflects the core process and needs to be modified by readers according to their own business conditions.

Lastmod: Friday, December 22, 2023

See Also:

Translations: