通过密钥管理服务 KMS 托管的用户主密钥(Customer Master Key,简称 CMK)结合对象存储 OSS 客户端加密 SDK,在数据上传前于本地完成加密,确保数据在传输和存储过程中始终以密文形式存在,实现端到端的数据安全保护。本文介绍完整的操作流程,包括 KMS 密钥创建、访问控制 RAM 权限配置、SDK 集成、加密上传和解密下载。
背景信息
对象存储 OSS(Object Storage Service)客户端加密功能在数据上传到 OSS 之前,先在客户端(本地内存)完成加密操作,确保上传到 OSS 的数据始终为密文。结合密钥管理服务 KMS(Key Management Service)托管用户主密钥,可以避免在本地保管数据密钥,同时利用 KMS 提供的密钥轮转、访问控制、审计日志等能力,简化密钥管理流程。
客户端加密采用信封加密(Envelope Encryption)机制:使用数据密钥对文件进行本地加密,数据密钥本身由 KMS 托管的用户主密钥加密保护。加密后的数据密钥与密文文件一同上传到 OSS,解密时通过 KMS 解密数据密钥后再解密文件。
工作流程
加密并上传Object
获取加密密钥。
通过CMK ID,客户端向KMS请求数据密钥,KMS返回数据明文密钥和数据密文密钥。
加密数据并上传至OSS。
客户端使用数据明文密钥加密Object,并将加密后的Object和数据密文密钥上传至OSS。
下载并解密Object
下载Object。
客户端从OSS下载加密的Object和数据密文密钥。
解密Object。
客户端将数据密文密钥和CMK ID发送至KMS服务器,KMS解密并返回数据明文密钥。
客户端会为每一个上传的 Object 获取一个唯一的数据加密密钥。
为了保证数据的安全性,建议定期轮换或者更新 CMK。
本地需要维护 CMK ID 与 Object 之间的映射关系。
使用限制
地域一致性:KMS 密钥的地域属性必须与 OSS Bucket 的客户端加密配置保持一致,跨地域无法解密。
密钥状态:确保密钥处于启用状态。如果 CMK 被禁用或进入等待删除期,将无法解密数据。
权限管理:仅向必要的 RAM 用户授予 KMS 加密解密权限,遵循最小权限原则。
性能影响:客户端加密会增加额外的 KMS API 调用(每次上传下载均需调用 KMS API 一次),对大文件或高频场景可能影响性能。建议对大文件使用分片上传并结合客户端加密。
计费说明:客户端加密本身不产生额外费用,但 KMS API 调用(GenerateDataKey、Decrypt)会计入 KMS 的 API 调用量计费。创建软件保护的 CMK 也会产生实例费用。详情请参见计费说明。
数据密钥安全:SDK 在加密解密完成后会自动清除内存中的数据密钥明文,请勿在代码中额外保存数据密钥明文。
步骤一:创建密钥
在 KMS 控制台创建对称加密类型的密钥,用于加密和保护数据密钥。
KMS CMK 必须与 OSS Bucket 在同一地域,跨地域加密不支持。
步骤二:配置 RAM 权限
为执行客户端加密的应用程序授予访问控制 RAM(Resource Access Management)和 OSS 的最小权限。
创建 AccessKey
创建权限策略:
进入权限管理 > 权限策略页面,单击创建权限策略。
选择脚本编辑,并输入以下内容后,单击确定。
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:PutObject", "oss:GetObject", "oss:DeleteObject", "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:<bucket-name>/*" ] }, { "Effect": "Allow", "Action": [ "kms:GenerateDataKey", "kms:Decrypt", "kms:DescribeKey" ], "Resource": [ "acs:kms:<region>:<account-id>:key/<key-id>" ] } ] }
说明将
<bucket-name>替换为实际的 OSS Bucket 名称,将<key-id>替换为步骤一中创建的 CMK 的 Key ID,将<region>替换为 KMS 密钥所在地域,将<account-id>替换为阿里云账号 ID。KMS 密钥必须与 OSS Bucket 在同一地域。绑定权限:单击 RAM 用户操作列的新增授权,绑定上一步创建的权限策略。具体操作,请参见管理RAM用户的权限。
步骤三:配置环境变量
环境变量说明
环境变量 | 说明 | 示例 |
| RAM 用户的 AccessKey ID |
|
| RAM 用户的 AccessKey Secret |
|
| KMS 密钥所在地域 |
|
| OSS Bucket 所在地域 |
|
配置样例
本文以配置AccessKey为例,介绍如何将步骤二中获取的 RAM 用户的AccessKey信息配置到环境变量中。
Linux
在命令行界面执行以下命令来将环境变量设置追加到
~/.bashrc文件中。echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.bashrc echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.bashrc执行以下命令使变更生效。
source ~/.bashrc执行以下命令检查环境变量是否生效。
echo $OSS_ACCESS_KEY_ID echo $OSS_ACCESS_KEY_SECRET
macOS
在终端中执行以下命令,查看默认Shell类型。
echo $SHELL根据默认Shell类型进行操作。
Zsh
执行以下命令来将环境变量设置追加到
~/.zshrc文件中。echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.zshrc echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.zshrc执行以下命令使变更生效。
source ~/.zshrc执行以下命令检查环境变量是否生效。
echo $OSS_ACCESS_KEY_ID echo $OSS_ACCESS_KEY_SECRET
Bash
执行以下命令来将环境变量设置追加到
~/.bash_profile文件中。echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.bash_profile echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.bash_profile执行以下命令使变更生效。
source ~/.bash_profile执行以下命令检查环境变量是否生效。
echo $OSS_ACCESS_KEY_ID echo $OSS_ACCESS_KEY_SECRET
Windows
CMD
在CMD中运行以下命令。
setx OSS_ACCESS_KEY_ID "YOUR_ACCESS_KEY_ID" setx OSS_ACCESS_KEY_SECRET "YOUR_ACCESS_KEY_SECRET"运行以下命令,检查环境变量是否生效。
echo %OSS_ACCESS_KEY_ID% echo %OSS_ACCESS_KEY_SECRET%
PowerShell
在PowerShell中运行以下命令。
[Environment]::SetEnvironmentVariable("OSS_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_ID", [EnvironmentVariableTarget]::User) [Environment]::SetEnvironmentVariable("OSS_ACCESS_KEY_SECRET", "YOUR_ACCESS_KEY_SECRET", [EnvironmentVariableTarget]::User)运行以下命令,检查环境变量是否生效。
[Environment]::GetEnvironmentVariable("OSS_ACCESS_KEY_ID", [EnvironmentVariableTarget]::User) [Environment]::GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET", [EnvironmentVariableTarget]::User)
步骤四:安装 SDK 并集成客户端加密
本文以 Python 语言为例,介绍 OSS 如何集成 KMS 实现客户端加密。更多语言的 SDK 说明,请参见客户端加密。
安装 OSS Python SDK
pip install oss2完整示例代码
以下代码演示如何使用 KMS 托管的用户主密钥实现 OSS 客户端加密的加密上传和解密下载。
import os
import oss2
from oss2.crypto.kms import KMSCipherProvider
# 从环境变量中读取凭证和地域信息
access_key_id = os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID')
access_key_secret = os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
kms_region = os.getenv('KMS_REGION', 'cn-hangzhou')
oss_region = os.getenv('OSS_REGION', 'cn-hangzhou')
bucket_name = os.getenv('OSS_BUCKET_NAME')
cmk_id = os.getenv('CMK_ID')
# 初始化 OSS Endpoint
oss_endpoint = f'https://oss-{oss_region}.aliyuncs.com'
auth = oss2.Auth(access_key_id, access_key_secret)
# 配置 KMS 加密提供者
kms_provider = KMSCipherProvider(
kms_region=kms_region,
access_key_id=access_key_id,
access_key_secret=access_key_secret,
cmk=cmk_id,
)
# 创建支持客户端加密的 Bucket
crypto_bucket = oss2.CryptoBucket(
auth,
oss_endpoint,
bucket_name,
crypto_provider=kms_provider,
)
def upload_encrypted_object(object_name, file_path):
"""加密上传文件到 OSS"""
with open(file_path, 'rb') as f:
crypto_bucket.put_object(object_name, f)
print(f'加密上传成功: {object_name}')
def download_decrypted_object(object_name, file_path):
"""从 OSS 下载并解密文件"""
result = crypto_bucket.get_object(object_name)
with open(file_path, 'wb') as f:
for chunk in result:
f.write(chunk)
print(f'解密下载成功: {object_name}')
# 示例用法
if __name__ == '__main__':
local_file = 'example.txt'
object_key = 'encrypted-example.txt'
# 加密上传
upload_encrypted_object(object_key, local_file)
# 解密下载
download_decrypted_object(object_key, 'decrypted-example.txt')验证加密是否生效
加密上传和解密下载完成后,可通过以下方法验证加密是否生效。
查看 OSS 对象元数据:在 OSS 控制台查看上传对象的元数据,确认对象加密属性已正确设置。
检查 KMS 调用记录:在 KMS 控制台查看密钥的 API 调用记录,确认 GenerateDataKey 接口已被调用。
验证解密文件完整性:将加密文件下载到本地后,检查文件内容是否可读且完整,确认解密过程正常。
常见问题
解密时报 "Key is disabled" 错误
检查 CMK 状态,确保密钥在 KMS 控制台中处于启用状态。
RAM 用户无权执行加密操作
检查 RAM 策略是否正确授予了
kms:GenerateDataKey、kms:Decrypt权限,并且 Resource 指向了正确的密钥 ARN。