You must encrypt sensitive information in your IT assets that are deployed on Alibaba Cloud. Envelope encryption allows you to use data keys generated by Key Management Service (KMS) to encrypt large amounts of local data. You can call the corresponding cryptographic operations of Key Management Service (KMS) to generate a data key pair online and then use the data key pair to encrypt and decrypt local data. This encryption mechanism is known as envelope encryption.

Scenarios

You can use envelope encryption in many scenarios, including but not limited to the following:
  • Encrypt business data files.
  • Encrypt all data stored on local disks.
This topic describes how to use envelope encryption to encrypt and decrypt local files.

How envelope encryption works

Use KMS to create a customer master key (CMK), use the CMK to generate a data key pair, and then use the plaintext data key to encrypt local files. Envelope encryption is suitable for encrypting large amounts of data. The following figure shows the entire envelope encryption procedure.
  • Envelope encryption

    Procedure:
    1. Use the KMS console or call the CreateKey operation to create a CMK.
    2. Call the GenerateDataKey operation of KMS to generate a data key pair. KMS returns a data key pair: a plaintext data key and a ciphertext data key.
    3. Use the plaintext data key to encrypt the local files, and then clear the plaintext data key stored in Random Access Memory (RAM).
    4. Store the ciphertext data key and encrypted data files on a storage device or service.
  • Envelope decryption

    Procedure:
    1. Retrieve the ciphertext data key from the local device or service.
    2. Call the Decrypt operation of KMS to decrypt the ciphertext data key. A plaintext copy of the data key is returned.
    3. Use the plaintext data key to decrypt the local files, and then clear the plaintext data key stored in RAM.

Related API operations

You can call the following KMS API operations to encrypt and decrypt local data.
Operation Description
CreateKey Creates a CMK.
CreateAlias Assigns an alias to a CMK.
GenerateDataKey Generates a data key, uses the specified CMK to encrypt the data key, and then returns a plaintext data key and a ciphertext data key.
Decrypt Decrypts data that is encrypted by KMS, including the ciphertext data key generated by calling the GenerateDataKey operation. You do not need to specify a CMK.

Encrypt and decrypt a local file

  • Envelope encryption
    1. Create a CMK.
      $ aliyun kms CreateKey
      {
        "KeyMetadata": {
          "CreationDate": "2019-04-08T07:45:54Z",
          "Description": "",
          "KeyId": "1234abcd-12ab-34cd-56ef-12345678****",
          "KeyState": "Enabled",
          "KeyUsage": "ENCRYPT/DECRYPT",
          "DeleteDate": "",
          "Creator": "111122223333",
          "Arn": "acs:kms:cn-hangzhou:111122223333:key/1234abcd-12ab-34cd-56ef-12345678****",
          "Origin": "Aliyun_KMS",
          "MaterialExpireTime": ""
        },
        "RequestId": "2a37b168-9fa0-4d71-aba4-2077dd9e80df"
      }
    2. Assign an alias to the CMK.
      Aliases are optional to CMKs. If a CMK does not have an alias, you can use its ID.
      $ aliyun kms CreateAlias --AliasName alias/Apollo/WorkKey --KeyId 1234abcd-12ab-34cd-56ef-1234567890ab
      Note In this example, Apollo/WorkKey specifies the CMK in the Apollo project that is used to encrypt data keys. The alias of the CMK is WorkKey. This means that you can specify alias/Apollo/WorkKey to use the CMK WorkKey to encrypt a data key.
    3. Encrypt a local data file.
      Sample code:
      • CMK: The alias of the CMK is alias/Apollo/WorkKey.
      • Plaintext data file: ./data/sales.csv
      • Ciphertext data file: ./data/sales.csv.cipher
      #! /usr/bin/env python
      #coding=utf-8
      
      import json
      import base64
      
      from Crypto.Cipher import AES
      
      from aliyunsdkcore import client
      from aliyunsdkkms.request.v20160120 import GenerateDataKeyRequest
      
      def KmsGenerateDataKey(client, key_alias):
          request = GenerateDataKeyRequest.GenerateDataKeyRequest()
          request.set_accept_format('JSON')
          request.set_KeyId(key_alias)
          request.set_NumberOfBytes(32)
          response = json.loads(client.do_action(request))
      
          datakey_encrypted = response["CiphertextBlob"]
          datakey_plaintext = response["Plaintext"]
          return (datakey_plaintext, datakey_encrypted)
      
      def ReadTextFile(in_file):
        file = open(in_file, 'r')
        content = file.read()
        file.close()
        return content
      
      def WriteTextFile(out_file, lines):
        file = open(out_file, 'w')
        for ln in lines:
          file.write(ln)
          file.write('\n')
        file.close()
      
      # Out file format (text)
      # Line 1: b64 encoded data key
      # Line 2: b64 encoded IV
      # Line 3: b64 encoded ciphertext
      # Line 4: b64 encoded authentication tag
      def LocalEncrypt(datakey_plaintext, datakey_encrypted, in_file, out_file):
        data_key_binary = base64.b64decode(datakey_plaintext)
        cipher = AES.new(data_key_binary, AES.MODE_EAX)
      
        in_content = ReadTextFile(in_file)
        ciphertext, tag = cipher.encrypt_and_digest(in_content)
      
        lines = [datakey_encrypted, base64.b64encode(cipher.nonce), base64.b64encode(ciphertext), base64.b64encode(tag)];
        WriteTextFile(out_file, lines)
      
      clt = client.AcsClient('Access-Key-Id','Access-Key-Secret','Region-Id')
      
      key_alias = 'alias/Apollo/WorkKey'
      
      in_file = './data/sales.csv'
      out_file = './data/sales.csv.cipher'
      
      # Generate Data Key
      datakey = KmsGenerateDataKey(clt, key_alias)
      
      # Locally Encrypt the sales record
      LocalEncrypt(datakey[0], datakey[1], in_file, out_file)
  • Envelope decryption

    Decrypt a local file.

    Sample code:
    • Ciphertext data file: ./data/sales.csv.cipher
    • Plaintext data file: ./data/decrypted_sales.csv
    #! /usr/bin/env python
    #coding=utf-8
    
    import json
    import base64
    
    from Crypto.Cipher import AES
    
    from aliyunsdkcore import client
    from aliyunsdkkms.request.v20160120 import DecryptRequest
    
    def KmsDecrypt(client, ciphertext):
      request = DecryptRequest.DecryptRequest()
      request.set_accept_format('JSON')
      request.set_CiphertextBlob(ciphertext)
      response = json.loads(clt.do_action(request))
      return response.get("Plaintext")
    
    def ReadTextFile(in_file):
      file = open(in_file, 'r')
      lines = []
      for ln in file:
        lines.append(ln)
      file.close()
      return lines
    
    def WriteTextFile(out_file, content):
      file = open(out_file, 'w')
      file.write(content)
      file.close()
    
    def LocalDecrypt(datakey, iv, ciphertext, tag, out_file):
      cipher = AES.new(datakey, AES.MODE_EAX, iv)
      data = cipher.decrypt_and_verify(ciphertext, tag).decode('utf-8')
      WriteTextFile(out_file, data)
    
    clt = client.AcsClient('Access-Key-Id','Access-Key-Secret','Region-Id')
    
    in_file = './data/sales.csv.cipher'
    out_file = './data/decrypted_sales.csv'
    
    # Read encrypted file
    in_lines = ReadTextFile(in_file)
    
    # Decrypt data key
    datakey = KmsDecrypt(clt, in_lines[0])
    
    # Locally decrypt the sales record
    LocalDecrypt(
      base64.b64decode(datakey),
      base64.b64decode(in_lines[1]), # IV
      base64.b64decode(in_lines[2]), # Ciphertext
      base64.b64decode(in_lines[3]), # Authentication tag
      out_file
      )