阿里云用户在云上部署IT资产,需要对敏感数据进行加密保护。如果被加密的数据对象较大,则可以通过KMS的密码运算API在线生成数据密钥,用离线数据密钥在本地加密大量数据。这类加密模式叫作“信封加密”。
场景概述
典型的场景包括(但不限于):
- 对业务数据文件的加密
- 对全磁盘数据加密
产品架构
使用KMS创建一个主密钥,使用主密钥生成一个数据密钥,再使用数据密钥在本地加解密数据。这种场景适用于大量数据的加解密。具体架构如下所示:
- 信封加密
操作流程如下:- 通过KMS控制台,或者调用CreateKey接口,创建一个用户主密钥。
- 调用KMS服务的GenerateDataKey接口创建一个数据密钥。KMS会返回一个明文的数据密钥和一个密文的数据密钥。
- 使用明文的数据密钥,加密文件,产生密文文件,然后删除本地的明文密钥。
- 用户将密文数据密钥和密文文件一同存储到持久化存储设备或服务中。
- 信封解密
图例 含义 用户主密钥(CMK) 明文数据密钥 密文数据密钥 明文文件 密文文件
操作流程如下:- 从本地文件中读取密文数据密钥。
- 调用KMS服务的Decrypt接口,将加密过的密钥解密为明文密钥。
- 用明文密钥为本地数据解密,再删除本地存储中的明文密钥。
相关API
您可以调用以下KMS API,在本地对数据进行加解密。
API名称 | 说明 |
---|---|
CreateKey | 创建用户主密钥(CMK)。 |
CreateAlias | 为指定用户主密钥创建一个别名。 |
GenerateDataKey | 在线生成数据密钥,用指定CMK加密数据密钥后,返回数据密钥的密文和明文。 |
Decrypt | 解密KMS直接加密的数据(包括GenerateDataKey产生的数据密钥的密文),不需要指定CMK。 |
加密/解密本地文件
- 信封加密
- 创建用户主密钥(对应架构图中的第1步,通过调用CreateKey完成创建。)
给主密钥添加别名(可选)$ 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" }
别名是用户主密钥的可选标识。如果用户不创建别名,也可以直接使用密钥的ID。$ aliyun kms CreateAlias --AliasName alias/Apollo/WorkKey --KeyId 1234abcd-12ab-34cd-56ef-1234567890ab
说明 其中,Apollo/WorkKey表示Apollo项目中的工作密钥(当前被用于加密的密钥),并在后续示例代码中使用此别名。即表示应用可以使用alias/Apollo/WorkKey调用加密API。 - 加密本地文件(对应架构图中的第3步,使用明文的数据密钥,加密文件,产生密文文件。)
示例代码中:
- 用户主密钥:别名为
alias/Apollo/WorkKey
- 明文数据文件:./data/sales.csv
- 输出的密文数据文件:./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)
- 用户主密钥:别名为
- 创建用户主密钥(对应架构图中的第1步,通过调用CreateKey完成创建。)
- 信封解密
解密本地文件(对应架构图中的第3步,用明文密钥为本地数据解密。)
示例代码中:- 密文数据文件:./data/sales.csv.cipher
- 输出的明文数据文件:./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 )