All Products
Search
Document Center

Key Management Service:Use envelope encryption to encrypt and decrypt local data

Last Updated:Mar 31, 2026

Encrypting large volumes of data directly through KMS is impractical — KMS is designed for key operations, not bulk data transfer. The solution is envelope encryption: KMS generates a data key and protects it with your customer master key (CMK), while your application uses that data key locally to encrypt or decrypt files. This keeps your raw data off the network and ensures the data key itself is never stored in plaintext.

How KMS and your application divide the work:

  • KMS: generates the data key, encrypts it with your CMK, and decrypts it on demand.

  • Your application: uses the plaintext data key locally to encrypt or decrypt files, then immediately discards it from memory.

Your raw data never leaves your environment. The data key is never stored in plaintext.

Important

Delete the plaintext data key from memory as soon as you finish encrypting or decrypting. Keeping it in memory longer than necessary increases exposure risk.

Use cases

Envelope encryption is suitable for encrypting large amounts of data, including the following scenarios:

  • Encrypting business data files.

  • Encrypting all data stored on local disks.

How it works

Encryption flow

Envelope encryption
  1. Create a CMK in the KMS console or by calling CreateKey.

  2. Call GenerateDataKey. KMS returns both the plaintext and the ciphertext of a new data key.

  3. Use the plaintext data key to encrypt your local files, then delete it from memory immediately.

  4. Store the ciphertext data key alongside the encrypted files. Keep them together — you need the ciphertext key to decrypt the files later.

Decryption flow

Envelope decryption
  1. Retrieve the ciphertext data key stored with your encrypted files.

  2. Call Decrypt. KMS decrypts the ciphertext data key and returns the plaintext. No CMK ID is required — KMS infers it from the ciphertext.

  3. Use the plaintext data key to decrypt the files, then delete it from memory immediately.

API operations

The following KMS API operations support envelope encryption. The actual file encryption and decryption happen locally in your application using standard cryptographic libraries — KMS handles only key operations.

API operationDescription
CreateKeyCreates a CMK.
CreateAliasCreates an alias for a CMK.
GenerateDataKeyGenerates a data key and encrypts it with the specified CMK. Returns both the plaintext and ciphertext of the data key.
DecryptDecrypts data that is encrypted in KMS, including the ciphertext data key generated by calling the GenerateDataKey operation. No CMK needs to be specified — KMS infers it from the ciphertext.

Encrypt and decrypt local files

The following example encrypts and decrypts a local CSV file using Alibaba Cloud CLI and Python.

Important

The AccessKey pair of an Alibaba Cloud account has permissions on all API operations, which makes it a high-risk credential. Use a RAM user to call API operations for routine tasks. Store credentials in environment variables rather than in your project code — hardcoded credentials risk exposing all resources under the account. For setup details, see Instantiate a client and configure a credential.

Prerequisites

Before you begin, ensure that you have:

  • An Alibaba Cloud account with KMS access

  • A RAM user with permissions to call CreateKey, GenerateDataKey, and Decrypt

  • Alibaba Cloud CLI installed and configured

  • Python installed with pycryptodome and aliyunsdkcore / aliyunsdkkms packages

Step 1: 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"
}

Step 2: Create an alias (optional)

An alias lets you reference a CMK by name instead of ID. If you skip this step, use the KeyId from Step 1 in subsequent commands.

aliyun kms CreateAlias --AliasName alias/Apollo/WorkKey --KeyId 1234abcd-12ab-34cd-56ef-12345678****
In this example, alias/Apollo/WorkKey identifies the CMK for the Apollo project. The subsequent code samples use this alias.

Step 3: Encrypt a local file

The script below generates a data key via KMS, uses it to encrypt ./data/sales.csv with AES-EAX, and writes the result to ./data/sales.csv.cipher.

The ciphertext file stores four base64-encoded values on separate lines:

  • Line 1: ciphertext data key

  • Line 2: IV (nonce)

  • Line 3: encrypted file content

  • Line 4: authentication tag

#!/usr/bin/env python
# coding=utf-8

import os
import json
import base64

from Crypto.Cipher import AES

from aliyunsdkcore import client
from aliyunsdkkms.request.v20160120 import GenerateDataKeyRequest


def kms_generate_data_key(kms_client, key_alias):
    request = GenerateDataKeyRequest.GenerateDataKeyRequest()
    request.set_accept_format('JSON')
    request.set_KeyId(key_alias)
    request.set_NumberOfBytes(32)  # 256-bit AES key
    response = json.loads(kms_client.do_action(request))

    datakey_encrypted = response['CiphertextBlob']
    datakey_plaintext = response['Plaintext']
    return datakey_plaintext, datakey_encrypted


def read_text_file(in_file):
    with open(in_file, 'r') as f:
        return f.read()


def write_text_file(out_file, lines):
    with open(out_file, 'w') as f:
        for ln in lines:
            f.write(ln)
            f.write('\n')


def local_encrypt(datakey_plaintext, datakey_encrypted, in_file, out_file):
    # Decode the base64-encoded plaintext key returned by KMS
    data_key_binary = base64.b64decode(datakey_plaintext)
    cipher = AES.new(data_key_binary, AES.MODE_EAX)

    in_content = read_text_file(in_file)
    ciphertext, tag = cipher.encrypt_and_digest(in_content.encode('utf-8'))

    # Store ciphertext key, IV, encrypted content, and authentication tag
    lines = [
        datakey_encrypted,
        base64.b64encode(cipher.nonce).decode('utf-8'),
        base64.b64encode(ciphertext).decode('utf-8'),
        base64.b64encode(tag).decode('utf-8'),
    ]
    write_text_file(out_file, lines)


# Load credentials from environment variables
kms_client = client.AcsClient(
    os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
    os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
    '<region-id>',  # Replace with your region ID, for example, cn-hangzhou
)

key_alias = 'alias/Apollo/WorkKey'
in_file = './data/sales.csv'
out_file = './data/sales.csv.cipher'

# Generate a data key and encrypt the file locally
datakey = kms_generate_data_key(kms_client, key_alias)
local_encrypt(datakey[0], datakey[1], in_file, out_file)

Replace the following placeholder:

PlaceholderDescriptionExample
<region-id>The region where your CMK is locatedcn-hangzhou

Step 4: Decrypt a local file

The script below reads ./data/sales.csv.cipher, calls KMS to decrypt the ciphertext data key, and uses the plaintext key to restore the original file to ./data/decrypted_sales.csv.

#!/usr/bin/env python
# coding=utf-8

import os
import json
import base64

from Crypto.Cipher import AES

from aliyunsdkcore import client
from aliyunsdkkms.request.v20160120 import DecryptRequest


def kms_decrypt(kms_client, ciphertext):
    request = DecryptRequest.DecryptRequest()
    request.set_accept_format('JSON')
    request.set_CiphertextBlob(ciphertext)
    response = json.loads(kms_client.do_action(request))
    return response.get('Plaintext')


def read_cipher_file(in_file):
    with open(in_file, 'r') as f:
        return [line.rstrip('\n') for line in f]


def write_text_file(out_file, content):
    with open(out_file, 'w') as f:
        f.write(content)


def local_decrypt(datakey_ciphertext, iv_b64, ciphertext_b64, tag_b64, out_file):
    # Decrypt the data key via KMS
    datakey_plaintext = kms_decrypt(kms_client, datakey_ciphertext)
    datakey_binary = base64.b64decode(datakey_plaintext)

    cipher = AES.new(datakey_binary, AES.MODE_EAX, base64.b64decode(iv_b64))
    plaintext = cipher.decrypt_and_verify(
        base64.b64decode(ciphertext_b64),
        base64.b64decode(tag_b64),
    ).decode('utf-8')
    write_text_file(out_file, plaintext)


# Load credentials from environment variables
kms_client = client.AcsClient(
    os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
    os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
    '<region-id>',  # Replace with your region ID, for example, cn-hangzhou
)

in_file = './data/sales.csv.cipher'
out_file = './data/decrypted_sales.csv'

# Read the cipher file (line 0: ciphertext key, 1: IV, 2: ciphertext, 3: tag)
in_lines = read_cipher_file(in_file)
local_decrypt(in_lines[0], in_lines[1], in_lines[2], in_lines[3], out_file)

Replace the following placeholder:

PlaceholderDescriptionExample
<region-id>The region where your CMK is locatedcn-hangzhou