1. 概述#
1.1 目标#
为 eet 增加云 KMS(Key Management Service)支持能力,使对称加密、HMAC、信封加密、非对称加密等操作可选择由云 KMS 管理密钥,同时最大限度减少对现有能力的侵入。
1.2 能力矩阵#
| 操作类型 | 密钥来源 | eet 行为 | KMS 行为 |
|---|
| AES/SM4 加密 | KMS GenerateDataKey | 本地加密 | 生成 DEK,返回明文+密文 |
| AES/SM4 解密 | KMS Decrypt | 本地解密 | 解密 DEK |
| 信封加密 | KMS GenerateDataKey | 本地加密数据 | 生成 DEK |
| 信封解密 | KMS Decrypt | 本地解密数据 | 解密 DEK |
| HMAC 计算 | KMS GenerateMac | 透传请求 | 生成 MAC(或返回 key 本地计算) |
| RSA/SM2 加密 | KMS GetPublicKey | 本地加密 | 返回公钥 |
| RSA/SM2 解密 | KMS Decrypt | 透传密文 | 解密 |
| RSA/SM2 签名 | KMS Sign | 透传摘要/消息 | 签名 |
| RSA/SM2 验签 | KMS GetPublicKey | 本地验签 | 返回公钥 |
1.3 设计原则#
- 零侵入默认路径:不指定 KMS 相关参数时,行为与当前完全一致
- 可选依赖:KMS 能力通过可选依赖实现,未安装时相关选项不生效
- 适配器模式:KMS 调用通过抽象接口,便于支持多云
- 最小改动:现有命令仅增加可选参数,核心逻辑通过「密钥获取层」抽象
1.4 KMS 输出格式原则:标准格式保持纯净,DEK 密文单独输出#
目标:有行业标准格式的密文/哈希(AES、SM4、HMAC)保持标准输出,不混入 encrypted_dek;eet 自定义格式(信封)则沿用现有设计。
| 操作类型 | 输出格式 | 说明 |
|---|
| 对称加密 (AES/SM4) | 密文:标准 ciphertext||tag;encrypted_dek、nonce 单独输出 | AES/SM4 密文有标准格式,需保持纯净 |
| HMAC | MAC:标准 hex;encrypted_dek 单独输出 | HMAC 输出有标准格式 |
| 信封加密 | 与当前一致,单一 blob(含 enc_dek、ciphertext 等) | 信封为 eet 自定义格式,无外部标准,可沿用现有结构 |
| 非对称加密 (RSA/SM2) | 密文:标准 RSA/SM2 密文 | 无 DEK |
- AES/SM4、HMAC:主输出为标准格式,encrypted_dek 单独输出
- 信封:格式与当前一致,KMS 时仅 wrap_key_type=0x02,enc_dek 为 KMS blob
2. 架构设计#
2.1 整体架构#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| ┌─────────────────────────────────────────────────────────────────────────┐
│ CLI 命令层 (aes, envelope, rsa, hmac, sm4...) │
└─────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────┴─────────────────┐
│ 密钥来源判断:-f/--key vs --kms-key-id │
└─────────────────┬─────────────────┘
│
┌────────────────────────────┼────────────────────────────┐
│ 本地密钥路径 (-f/-k) │ KMS 密钥 ID (--kms-key-id) │
▼ │ ▼
┌─────────────────────┐ │ ┌─────────────────────┐
│ common.load_*_key() │ │ │ kms_backend. │
│ random_str.* │ │ │ get_key / decrypt │
└─────────────────────┘ │ └─────────────────────┘
│
▼
┌─────────────────────────────┐
│ 密钥获取层 (KeyResolver) │
│ resolve_symmetric_key() │
│ resolve_public_key() │
│ resolve_dek_for_envelope() │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 密码学计算层 (现有不变) │
│ _aes_gcm_encrypt, etc. │
└─────────────────────────────┘
|
2.2 新增模块与职责#
| 模块 | 路径 | 职责 |
|---|
| KMS 抽象层 | easy_encryption_tool/kms/__init__.py | 定义 KMSBackend 协议、KeyResolver |
| KMS 适配器 | easy_encryption_tool/kms/backends/ | AWS、阿里云等具体实现 |
| 密钥解析器 | easy_encryption_tool/kms/resolver.py | 统一密钥获取入口,屏蔽本地/KMS 差异 |
2.3 侵入点最小化策略#
| 现有模块 | 侵入方式 | 侵入程度 |
|---|
aes_command.py | 增加 --kms-key-id、--kms-backend 可选参数;在密钥解析处调用 resolver | 低:仅入口处分支 |
sm4_command.py | 同上 | 低 |
hmac_command.py | 同上;或新增 hmac-kms 子命令 | 低 |
envelope_command.py | 增加 --kms-key-id 与 -f 互斥;_parse_envelope 增加 wrap_key_type 分支 | 中:需扩展解析逻辑 |
rsa_command.py | 增加 --kms-key-id 与 -f 互斥;decrypt/sign 调用 KMS API | 低 |
sm2_command.py | 同上 | 低 |
common.py | 不修改 | 无 |
envelope 格式 | 扩展 wrap_key_type 枚举,增加 KMS 相关元数据 | 向后兼容 |
3. 接口设计#
3.1 KMS 后端协议(Protocol)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
| # easy_encryption_tool/kms/protocol.py
from typing import Protocol, runtime_checkable
@runtime_checkable
class KMSBackend(Protocol):
"""云 KMS 后端协议。各云厂商实现此接口。"""
def get_public_key(self, key_id: str) -> bytes:
"""
获取非对称密钥公钥(PEM 或 DER)。
:param key_id: 密钥 ID(如 ARN、资源 ID)
:return: 公钥字节(PEM 或 SubjectPublicKeyInfo)
:raises KMSError: 密钥不存在、权限不足等
"""
...
def generate_data_key(
self,
key_id: str,
key_spec: str = "AES_256",
number_of_bytes: int | None = None,
) -> tuple[bytes, bytes]:
"""
生成数据加密密钥(DEK)。
:param key_id: 主密钥 ID(KEK)
:param key_spec: 密钥规格,如 "AES_256"
:param number_of_bytes: 可选,指定字节数(部分 KMS 用 key_spec)
:return: (plaintext_dek, encrypted_dek)
:raises KMSError: 密钥不存在、权限不足等
"""
...
def decrypt(self, key_id: str, ciphertext_blob: bytes) -> bytes:
"""
解密。用于:1) DEK 解密;2) 非对称 RSA/SM2 解密。
:param key_id: 密钥 ID
:param ciphertext_blob: 密文(KMS 返回的格式,含元数据)
:return: 明文
:raises KMSError: 解密失败
"""
...
def sign(
self,
key_id: str,
message: bytes,
message_type: str = "DIGEST",
algorithm: str | None = None,
) -> bytes:
"""
数字签名。
:param key_id: 签名密钥 ID
:param message: 消息或摘要(message_type 区分)
:param message_type: "DIGEST" | "RAW"
:param algorithm: 如 "RSASSA_PKCS1_V1_5_SHA_256"
:return: 签名字节
"""
...
def generate_mac(
self,
key_id: str,
message: bytes,
algorithm: str = "HMAC_SHA_256",
) -> bytes:
"""
生成 HMAC(若 KMS 支持)。
:param key_id: HMAC 密钥 ID
:param message: 消息
:param algorithm: HMAC 算法
:return: MAC 字节
"""
...
|
3.2 密钥解析器(KeyResolver)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
| # easy_encryption_tool/kms/resolver.py
from typing import Optional
from dataclasses import dataclass
@dataclass
class SymmetricKeyResult:
"""对称密钥解析结果"""
key_bytes: bytes
encrypted_key: Optional[bytes] = None # 解密时从 KMS 获取的密文 DEK
kms_key_id: Optional[str] = None # 解密时需回传 KMS
@dataclass
class PublicKeyResult:
"""公钥解析结果"""
key_bytes: bytes # PEM 或 DER
key_type: str # "rsa" | "sm2" | "ec"
def resolve_symmetric_key(
*,
key: Optional[str] = None,
key_b64: Optional[str] = None,
kms_key_id: Optional[str] = None,
kms_backend: Optional[str] = None,
kms_region: Optional[str] = None,
kms_endpoint: Optional[str] = None,
encrypted_key: Optional[bytes] = None,
for_encrypt: bool = True,
) -> SymmetricKeyResult:
"""
解析对称密钥。优先 KMS,否则本地。
- for_encrypt=True + kms_key_id: 调用 KMS GenerateDataKey
- for_encrypt=False + kms_key_id + encrypted_key: 调用 KMS Decrypt
- 否则: 从 key/key_b64 解析(现有逻辑)
"""
...
def resolve_public_key(
*,
file_path: Optional[str] = None,
kms_key_id: Optional[str] = None,
kms_backend: Optional[str] = None,
kms_region: Optional[str] = None,
kms_endpoint: Optional[str] = None,
) -> PublicKeyResult:
"""
解析公钥。kms_key_id 优先,否则从 file_path 加载。
"""
...
def resolve_dek_for_envelope_encrypt(
*,
public_key_path: Optional[str] = None,
kms_key_id: Optional[str] = None,
kms_backend: Optional[str] = None,
kms_region: Optional[str] = None,
kms_endpoint: Optional[str] = None,
) -> tuple[bytes, bytes, Optional[str], int]:
"""
信封加密时获取 DEK。
- 本地: (dek, enc_dek, None, wrap_key_type)
- KMS: (dek, enc_dek, kms_key_id, WRAP_KMS),需 kms_backend、kms_region;kms_endpoint 可选
返回: (plaintext_dek, encrypted_dek, kms_key_id_or_none, wrap_key_type)
"""
...
def resolve_dek_for_envelope_decrypt(
*,
private_key_path: Optional[str] = None,
private_key_password: Optional[bytes] = None,
kms_key_id: Optional[str] = None,
kms_backend: Optional[str] = None,
kms_region: Optional[str] = None,
kms_endpoint: Optional[str] = None,
encrypted_dek: bytes,
wrap_key_type: int,
) -> bytes:
"""
信封解密时获取 DEK。
- wrap_key_type=WRAP_RSA: 本地 RSA 解密
- wrap_key_type=WRAP_KMS: 调用 KMS Decrypt,需 kms_backend、kms_region;kms_endpoint 可选
"""
...
|
3.3 KMS 后端工厂#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # easy_encryption_tool/kms/backends/__init__.py
def get_backend(
backend_name: str,
region: str,
endpoint: str | None = None,
**kwargs,
) -> KMSBackend:
"""
根据名称获取 KMS 后端实例。
:param backend_name: "aws" | "aliyun" | "huawei"
:param region: 区域(必选)
:param endpoint: 自定义 endpoint;None 时由各 backend 根据 region 构造默认
:param kwargs: 各后端其他配置
"""
...
|
各 backend 实现需内置默认 endpoint 规则(见 5.1.1),endpoint is None 时自动构造。
3.4 错误与可选依赖#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # easy_encryption_tool/kms/errors.py
class KMSError(Exception):
"""KMS 相关错误基类"""
pass
class KMSNotAvailableError(KMSError):
"""KMS 可选依赖未安装"""
pass
# easy_encryption_tool/kms/availability.py
def is_kms_available() -> bool:
"""检查 KMS 可选依赖是否已安装"""
try:
import boto3 # 或 aliyun sdk
return True
except ImportError:
return False
|
4. 信封格式扩展#
4.1 wrap_key_type 扩展#
| 值 | 常量名 | 说明 |
|---|
| 0x01 | WRAP_RSA | 现有:RSA 加密 DEK |
| 0x02 | WRAP_KMS | 新增:KMS 加密 DEK,encrypted_dek 为 KMS 返回的密文 blob |
4.2 KMS 信封格式(wrap_key_type=0x02)#
KMS 信封沿用当前信封格式(单一二进制 blob),与 RSA 信封结构一致,仅 wrap_key_type 与 enc_dek 含义不同:
- wrap_key_type:0x02(WRAP_KMS)
- enc_dek:KMS GenerateDataKey 返回的 ciphertext_blob(KMS 密文格式)
- ciphertext、nonce、aad、plaintext_binding 等:与现有信封格式相同
解密时由用户通过 --kms-backend、--kms-key-id、--kms-region 传入,enc_dek 从信封内解析,无需单独传入。
4.3 AES/SM4 KMS 输出格式(密文与 DEK 分离)#
AES/SM4 密文有行业标准格式(ciphertext||tag),使用 KMS 时需保持纯净。密文按标准格式输出,encrypted_dek、nonce 单独输出,见第 5.2.1 节。信封为 eet 自定义格式,无此约束。
4.4 信封解密逻辑扩展#
信封格式与当前一致,_parse_envelope 解析单一 blob。解密分支:
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 伪代码
parsed = _parse_envelope(raw) # 与现有一致
if parsed["wrap_key_type"] == WRAP_RSA:
dek = _rsa_decrypt_dek(pri_key, parsed["enc_dek"])
elif parsed["wrap_key_type"] == WRAP_KMS:
if not kms_key_id or not kms_backend or not kms_region:
error("KMS envelope requires --kms-backend, --kms-key-id, --kms-region")
return
backend = get_backend(kms_backend, region=kms_region, endpoint=kms_endpoint)
dek = backend.decrypt(kms_key_id, parsed["enc_dek"])
else:
error(DECRYPT_FAILED_MSG)
# 后续 AES-GCM 解密、plaintext_binding 校验与现有一致
|
5. CLI 参数设计#
5.1 通用 KMS 参数(各命令复用)#
| 参数 | 类型 | 必选 | 说明 |
|---|
--kms-backend | choice | ✅ | 云厂商如aws |
--kms-key-id | string | ✅ | KMS 密钥 ID(ARN 或资源 ID) |
--kms-region | string | ✅ | 区域(如 cn-north-1、cn-hangzhou) |
--kms-endpoint | string | - | 自定义 endpoint,私有化/专有云时覆盖默认 |
--encrypted-dek-out | path | - | [AES/SM4、HMAC] 加密时:将 encrypted_dek 写入该文件 |
--encrypted-dek / --encrypted-dek-file | string/path | - | [AES/SM4、HMAC] 解密/校验时:encrypted_dek 内容或文件路径 |
--nonce-out / --nonce-file | path | - | [AES/SM4] 加密时写出 / 解密时读入 nonce |
注:信封加解密无需上述参数,enc_dek 已包含在信封 blob 内。
资源显式确认:backend、key-id、region 均需用户显式指定,eet 不做自动推断。
5.1.1 各云厂商 endpoint 默认值#
--kms-endpoint 为可选参数。未指定时,各 backend 根据 --kms-region 自动构造默认 endpoint,用户无需每次输入。
| backend | 默认 endpoint 规则 | 示例 |
|---|
aws | 国际:kms.{region}.amazonaws.com 中国:kms.{region}.amazonaws.com.cn | kms.cn-north-1.amazonaws.com.cn |
aliyun | kms.{region}.aliyuncs.com | kms.cn-hangzhou.aliyuncs.com |
huawei | kms.{region}.myhuaweicloud.com | kms.cn-north-4.myhuaweicloud.com |
azure | {vault-name}.vault.azure.net(中国:.vault.azure.cn) 注:Azure 模型不同,key_id 通常含 vault 信息 | - |
5.2 各命令集成方式#
5.2.1 AES/SM4 命令#
1
2
3
4
5
6
7
8
9
| # 加密:KMS 生成 DEK,密文标准输出,encrypted_dek 单独输出
eet aes encrypt -A encrypt -i "data" \
--kms-backend aws --kms-key-id arn:aws:kms:cn-north-1:123:key/xxx --kms-region cn-north-1 \
-o cipher.b64 --encrypted-dek-out dek.b64
# 解密:密文 -i,encrypted_dek、nonce 通过 --encrypted-dek-file、--nonce-file 传入
eet aes decrypt -A decrypt -i cipher.b64 \
--kms-backend aws --kms-key-id xxx --kms-region cn-north-1 \
--encrypted-dek-file dek.b64 --nonce-file nonce.b64
|
AES/SM4 KMS 输出格式(密文与 DEK 分离):
| 输出项 | 格式 | 说明 |
|---|
| 密文 (ciphertext) | 标准 base64(ciphertext||tag) | 与 eet aes 非 KMS 模式一致,可被标准工具解析 |
| nonce | base64,12 字节 | 单独输出,解密必需 |
| encrypted_dek | base64(KMS ciphertext_blob) | 单独输出,用户存储,解密时传入 |
- 默认 JSON 输出:
{ "ciphertext": "...", "nonce": "...", "encrypted_dek": "..." },各字段独立 --raw:仅输出密文(标准格式),encrypted_dek、nonce 通过 --encrypted-dek-out、--nonce-out 写入文件- 解密时:
-i 为密文,--encrypted-dek/--encrypted-dek-file 为 DEK 密文,--nonce/--nonce-file 为 nonce
5.2.2 信封命令#
KMS 模式下,信封格式与当前一致(单一 blob),仅 wrap_key_type=0x02,enc_dek 为 KMS blob。
1
2
3
4
5
6
7
| # 加密:与 RSA 信封用法一致,输出单一 base64 信封
eet envelope encrypt --kms-backend aws --kms-key-id arn:aws:kms:... --kms-region cn-north-1 \
-i "data" -o envelope.b64
# 解密:-i 为信封,-f 与 KMS 三参数互斥
eet envelope decrypt --kms-backend aws --kms-key-id arn:aws:kms:... --kms-region cn-north-1 \
-i envelope.b64 -o plain.txt
|
信封格式:与 ENVELOPE_FORMAT_SPEC.md 一致,RSA(0x01)与 KMS(0x02)共用同一结构,enc_dek 字段含义不同。
5.2.3 RSA/SM2 命令#
非对称加密无 DEK,公钥加密后密文即为标准输出,无需分离。
1
2
3
4
5
| # 加密:密文为标准 RSA/SM2 格式,与本地模式一致
eet rsa encrypt --kms-backend aws --kms-key-id arn:aws:kms:... --kms-region cn-north-1 -i "data"
# 解密(调用 KMS API)
eet rsa decrypt --kms-backend aws --kms-key-id arn:aws:kms:... --kms-region cn-north-1 -i <base64_cipher>
|
5.2.4 HMAC 命令#
1
2
3
4
5
6
7
| # 计算:MAC 标准 hex 输出,encrypted_dek 单独输出
eet hmac -i "data" --kms-backend aws --kms-key-id xxx --kms-region cn-north-1 \
--encrypted-dek-out dek.b64
# 校验:需传入 encrypted_dek 以还原 key
eet hmac verify -i "data" --mac xxx --kms-backend aws --kms-key-id xxx --kms-region cn-north-1 \
--encrypted-dek-file dek.b64
|
HMAC KMS 输出格式(MAC 与 DEK 分离):
| 输出项 | 格式 | 说明 |
|---|
| mac | 标准 hex 字符串 | 与 eet hmac 非 KMS 模式一致 |
| encrypted_dek | base64(KMS ciphertext_blob) | 单独输出,校验时需传入 |
- 默认 JSON:
{ "mac": "...", "encrypted_dek": "..." } --raw:仅输出 MAC(标准格式),encrypted_dek 通过 --encrypted-dek-out 写入文件
5.2.5 SM4 命令#
与 AES 类似,增加 --kms-backend、--kms-key-id、--kms-region(均为必选),--kms-endpoint 可选。
5.3 参数互斥与必选规则#
| 命令 | 互斥规则 | KMS 必选 |
|---|
| envelope encrypt | -f 与 KMS 模式互斥 | --kms-backend、--kms-key-id、--kms-region |
| envelope decrypt | -f 与 KMS 模式互斥 | 同上 |
| rsa encrypt/decrypt/sign | -f 与 KMS 模式互斥 | 同上 |
| aes encrypt/decrypt | -k/--key-b64/-r 与 KMS 模式互斥 | 同上 |
| hmac | -k/--key-b64/-r 与 KMS 模式互斥 | 同上 |
KMS 模式下 --kms-endpoint 可选,未指定时使用各 backend 默认 endpoint。
6. 实现步骤#
阶段一:基础设施(无侵入)#
| 步骤 | 内容 | 产出 |
|---|
| 1.1 | 创建 easy_encryption_tool/kms/ 目录 | 目录结构 |
| 1.2 | 实现 kms/protocol.py:KMSBackend 协议、KMSError | 接口定义 |
| 1.3 | 实现 kms/resolver.py:KeyResolver 骨架(仅本地逻辑) | 密钥解析逻辑 |
| 1.4 | 实现 kms/availability.py:is_kms_available() | 依赖检查 |
| 1.5 | 在 setup.py 增加 extras_require:kms = ["boto3"] 等 | 可选依赖声明 |
阶段二:AWS KMS 适配器#
| 步骤 | 内容 | 产出 |
|---|
| 2.1 | 实现 kms/backends/aws.py:AWSKMSBackend | 完整实现 |
| 2.2 | 实现 kms/backends/__init__.py:get_backend(“aws”) | 工厂 |
| 2.3 | 单元测试(可 mock boto3) | 测试覆盖 |
阶段三:信封加密集成(最小侵入)#
| 步骤 | 内容 | 产出 |
|---|
| 3.1 | 在 envelope_command.py 增加常量 WRAP_KMS = 0x02 | 常量 |
| 3.2 | 修改 envelope_encrypt:增加 --kms-key-id,与 -f 互斥 | 加密支持 KMS |
| 3.3 | 在加密逻辑中:若 kms_key_id,调用 resolver,使用 WRAP_KMS | 加密流程 |
| 3.4 | 修改 envelope_decrypt:增加 --kms-key-id,与 -f 互斥 | 解密支持 KMS |
| 3.5 | 在解密逻辑中:若 wrap_key_type==WRAP_KMS,调用 KMS Decrypt | 解密流程 |
| 3.6 | 更新 ENVELOPE_FORMAT_SPEC.md | 文档 |
阶段四:对称加密集成#
| 步骤 | 内容 | 产出 |
|---|
| 4.1 | 定义 AES/SM4 KMS 密文格式(见 5.2.1) | 格式规范 |
| 4.2 | 修改 aes_command.py:增加 --kms-key-id,密钥解析分支 | AES KMS |
| 4.3 | 修改 sm4_command.py:同上 | SM4 KMS |
阶段五:非对称加密集成#
| 步骤 | 内容 | 产出 |
|---|
| 5.1 | 修改 rsa_command.py:encrypt 增加 --kms-key-id,从 KMS 取公钥 | RSA 加密 |
| 5.2 | 修改 rsa_command.py:decrypt 增加 --kms-key-id,调用 KMS Decrypt | RSA 解密 |
| 5.3 | 修改 rsa_command.py:sign 增加 --kms-key-id,调用 KMS Sign | RSA 签名 |
| 5.4 | 修改 sm2_command.py:同上(若 KMS 支持 SM2) | SM2 集成 |
阶段六:HMAC 集成#
| 步骤 | 内容 | 产出 |
|---|
| 6.1 | 修改 hmac_command.py:增加 --kms-key-id,KMS GenerateDataKey 取 key | HMAC KMS |
阶段七:可选依赖与优雅降级#
| 步骤 | 内容 | 产出 |
|---|
| 7.1 | 各命令入口:若 --kms-key-id 且 not is_kms_available(),提示安装 eet[kms] | 友好错误 |
| 7.2 | 更新 README、CLI_COMMAND_REFERENCE | 文档 |
7. 侵入最小化:命令层集成模式#
各命令的改动遵循统一模式,仅在最前部增加「密钥解析」分支,后续计算逻辑不变。
7.1 信封加密示例(envelope_encrypt)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # 原逻辑:pub_key = _load_rsa_public_key(public_key)
# 新逻辑(仅此段为新增):
if kms_key_id:
if public_key: # -f 与 KMS 参数互斥
error("Cannot use both -f and --kms-key-id")
return
backend = get_backend(kms_backend, region=kms_region, endpoint=kms_endpoint)
dek, enc_dek, _, wrap_key_type = resolve_dek_for_envelope_encrypt(
kms_key_id=kms_key_id, kms_backend=kms_backend, kms_region=kms_region, kms_endpoint=kms_endpoint
)
else:
pub_key = _load_rsa_public_key(public_key)
if pub_key is None:
error("Public key must be RSA 2048/3072/4096 bits")
return
dek = random_str.generate_random_bytes(AES_DEK_LEN)
enc_dek = _rsa_encrypt_dek(pub_key, dek)
wrap_key_type = WRAP_RSA
# 以下与现有完全一致
nonce = random_str.generate_random_bytes(GCM_NONCE_LEN)
ciphertext = _aes_gcm_encrypt(dek, nonce, aad_bytes, plaintext)
plaintext_binding = hmac.new(dek, plaintext, hashlib.sha256).digest()
envelope = _build_envelope(..., enc_dek=enc_dek, wrap_key_type=wrap_key_type, ...)
|
7.2 RSA 加密示例(rsa_encrypt)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 原逻辑:pub_key = common.load_public_key(encoding=encoding, file_path=public_key)
# 新逻辑:
if kms_key_id:
if public_key:
error("Cannot use both -f and --kms-key-id")
return
backend = get_backend(kms_backend, region=kms_region, endpoint=kms_endpoint)
pub_key_bytes = backend.get_public_key(kms_key_id)
pub_key = serialization.load_der_public_key(pub_key_bytes) # 或 load_pem
else:
pub_key = common.load_public_key(encoding=encoding, file_path=public_key)
# 以下不变
if pub_key is None:
return
cipher = pub_key.encrypt(...)
|
7.3 参数校验(Click 层)#
使用 @click.option 的 required 保持为 False,在回调中做互斥校验:
1
2
3
4
5
6
7
| def validate_key_source(ctx, param, value):
kms = ctx.params.get("kms_key_id")
if value and kms:
raise click.BadParameter("-f/--public-key and --kms-key-id are mutually exclusive")
if not value and not kms:
raise click.BadParameter("Either -f/--public-key or --kms-key-id is required")
return value
|
或更简单:将原 -f 的 required=True 改为 required=False,在命令体内校验 if not public_key and not kms_key_id: error("Either -f or --kms-key-id is required")。
8. 文件变更清单#
8.1 新增文件#
1
2
3
4
5
6
7
8
9
| easy_encryption_tool/kms/
├── __init__.py
├── protocol.py
├── resolver.py
├── availability.py
├── errors.py
└── backends/
├── __init__.py
└── aws.py
|
8.2 修改文件(最小改动)#
| 文件 | 改动类型 |
|---|
envelope_command.py | 增加 --kms-key-id 选项,分支逻辑 |
aes_command.py | 增加 --kms-key-id 选项,密钥解析分支 |
sm4_command.py | 同上 |
hmac_command.py | 同上 |
rsa_command.py | 增加 --kms-key-id,encrypt/decrypt/sign 分支 |
sm2_command.py | 同上(若支持) |
setup.py / pyproject.toml | extras_require |
docs/ENVELOPE_FORMAT_SPEC.md | wrap_key_type 扩展说明 |
docs/CLI_COMMAND_REFERENCE.md | 新增参数说明 |
8.3 不修改文件#
common.pyrandom_str.pycrypto/ 下各加密实现output_builder.py、output_renderer.py 等输出层
9. 凭证与配置#
9.1 凭证(各云厂商)#
- AWS:环境变量
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY;配置文件 ~/.aws/credentials;IAM Role - 阿里云:环境变量
ALIBABA_CLOUD_ACCESS_KEY_ID、ALIBABA_CLOUD_ACCESS_KEY_SECRET;配置文件 - 华为云:环境变量
HUAWEICLOUD_ACCESS_KEY_ID、HUAWEICLOUD_SECRET_ACCESS_KEY;配置文件
9.2 配置优先级(region)#
- 显式 CLI 参数:
--kms-region(KMS 模式下必选,由用户指定) - 环境变量:
AWS_DEFAULT_REGION、AWS_REGION(仅作 fallback,非 KMS 必选场景)
9.3 密钥 ID 格式#
- AWS:
arn:aws:kms:region:account:key/key-id 或短格式 key-id - 阿里云:
acs:kms:region:account:key/key-id 或资源 ID - 华为云: 一般为 UUID 或资源 ID
9.4 Endpoint 默认值与覆盖#
各 backend 内置默认 endpoint 规则(见 5.1.1),用户通常无需指定 --kms-endpoint。
| 场景 | 行为 |
|---|
| 公有云标准区域 | 使用 backend 默认规则,无需 --kms-endpoint |
| 私有化 / 专有云 | 显式指定 --kms-endpoint https://kms.internal.example.com |
| 自定义 region 映射 | 部分专有云 region 需映射到特定 endpoint,可配置或通过 --kms-endpoint 覆盖 |
实现时,backend 构造函数接收 region 与 endpoint(可选),若 endpoint 为空则按 region 构造默认。
10. 兼容性与迁移#
10.1 向后兼容#
- 所有
--kms-key-id 均为可选,不指定时行为与当前完全一致 - 现有信封(wrap_key_type=0x01)解密逻辑不变
- 无 KMS 依赖时,相关选项传入会提示安装,不改变现有命令行为
10.2 迁移路径#
- 用户无需迁移,KMS 为纯增量能力
- 旧版 eet 无法解密 wrap_key_type=0x02 的信封,需升级