Envelope encryption is an encryption mechanism similar to the digital envelope technology. The mechanism encrypts data by using data keys that are encapsulated in an envelope. This ensures security during the storage, transfer, and use of data keys. Customer master keys (CMKs) are not used to directly encrypt or decrypt data. This topic describes how to use envelope encryption to encrypt and decrypt local data.

Background information

You may encounter the following issues when you use data keys:

  • Security risks: Security risks such as eavesdropping and phishing may occur during the process of transferring your sensitive data to Alibaba Cloud services over a network.
  • Absence of mutual trust and reliable credentials: You may not trust Alibaba Cloud services and may not want to upload your sensitive data to Alibaba Cloud services. In addition, Alibaba Cloud cannot prove that Alibaba Cloud will never misuse or disclose the received sensitive data.
  • Poor performance and high costs: A large amount of data needs to be transferred to Alibaba Cloud services over a secure channel. After the transfer, the data needs to be encrypted, and the encrypted data needs to be returned. This severely affects the service performance of Alibaba Cloud services. In addition, the transfer of a large amount of data costs high.

Envelope encryption uses a CMK to generate a data key and then uses an offline data key to encrypt a large amount of local data. Envelope encryption does not use a CMK to directly encrypt or decrypt data. Envelope encryption provides the following benefits:

  • Protection for data keys: Data keys are encrypted and protected. Encrypted data and encrypted data keys can be stored together.
  • Provision of trust and reliable credentials: Key Management Service (KMS) implements access control on all operations that are performed on data keys and generates logs for tracking. KMS also records the usage of all data keys to meet your auditing and compliance requirements.
  • High performance and cost-effectiveness: KMS calls cryptographic API operations to generate data keys and uses offline data keys to encrypt a large amount of local data.

Scenarios

You can use envelope encryption in, but not limited to, the following scenarios:
  • Encrypt business data files.
  • Encrypt all data stored on disks.

How data encryption and decryption work

Use KMS to create a CMK, use the CMK to generate a data key, and then use the data key to encrypt and decrypt local files. The following figure shows the procedure of envelope encryption.
  • Envelope encryptionencryption1Procedure:
    1. Create a CMK in the KMS console or by calling the CreateKey operation.
    2. Call the GenerateDataKey operation to generate a data key. KMS returns the plaintext and ciphertext of the data key.
    3. Use the plaintext data key to encrypt local files and then delete the plaintext data key from the memory.
    4. Store the ciphertext data key and the encrypted files on a persistent storage device or service.
  • Envelope decryptionencryProcedure:
    1. Retrieve the ciphertext data key and the encrypted files from the persistent storage device or service.
    2. Call the Decrypt operation to decrypt the ciphertext data key. The plaintext data key is returned.
    3. Use the plaintext data key to decrypt the encrypted files and then delete the plaintext data key from the memory.

Related operations for encryption and decryption

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

Use Alibaba Cloud CLI to encrypt and decrypt local files

  1. Call the CreateKey operation to create a CMK.
    aliyun kms CreateKey

    Expected output:

    {
      "KeyMetadata": {
        "CreationDate": "2019-04-08T07:45:54Z",
        "Description": "",
        "KeyId": "1234abcd-12ab-34cd-56ef-12345678****",
        "KeyState": "Enabled",
        "KeyUsage": "ENCRYPT/DECRYPT",
        "DeleteDate": "",
        "Creator": "151266687691****",
        "Arn": "acs:kms:cn-hangzhou:151266687691****:key/1234abcd-12ab-34cd-56ef-12345678****",
        "Origin": "Aliyun_KMS",
        "MaterialExpireTime": ""
      },
      "RequestId": "2a37b168-9fa0-4d71-aba4-2077dd9e80df"
    }
  2. (Optional) Create an alias for the CMK.
    Aliases are optionally used to identify CMKs. If a CMK does not have an alias, you can use the ID of the CMK to identify the CMK.
    aliyun kms CreateAlias --AliasName alias/Apollo/WorkKey --KeyId 1234abcd-12ab-34cd-56ef-12345678****
    Note In this example, Apollo/WorkKey is the alias of the CMK to use in the Apollo project. You can use the alias alias/Apollo/WorkKey in subsequent sample code to call the Encrypt operation.
  3. Encrypt a local file.
    • alias/Apollo/WorkKey is the alias of the CMK.
    • ./data/sales.csv is the plaintext file.
    • ./data/sales.csv.cipher is the returned ciphertext file.
    #!/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.encode('utf-8'))
    
        lines = [datakey_encrypted, base64.b64encode(cipher.nonce).decode('utf-8'), base64.b64encode(ciphertext).decode('utf-8'), base64.b64encode(tag).decode('utf-8')]
        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)
  4. Decrypt a local file.
    • ./data/sales.csv.cipher is the ciphertext file.
    • ./data/decrypted_sales.csv is the returned plaintext file.
    #!/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(client.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
      )

Use an SDK to encrypt and decrypt local files

You can use one of the following SDKs to implement envelope encryption:

  • KMS SDK

    Use KMS SDK to call the GenerateDataKey operation to generate a data key. Then, use a third-party encryption library and the data key to encrypt your data. After the encryption process is complete, encapsulate the ciphertext data key and the encrypted data in an envelope.

    For more information about the sample code of KMS SDK, see Code samples of KMS SDK for Java.

  • Encryption SDK

    Encryption SDK provides the best practices of envelope encryption. You can implement encryption and decryption with ease by using Encryption SDK.

    For more information about the sample code of Encryption SDK, see Quick start of Encryption SDK for Java.