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 密文有标准格式,需保持纯净
HMACMAC:标准 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 扩展

常量名说明
0x01WRAP_RSA现有:RSA 加密 DEK
0x02WRAP_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 密文格式)
  • ciphertextnonceaadplaintext_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-backendchoice云厂商如aws
--kms-key-idstringKMS 密钥 ID(ARN 或资源 ID)
--kms-regionstring区域(如 cn-north-1cn-hangzhou
--kms-endpointstring-自定义 endpoint,私有化/专有云时覆盖默认
--encrypted-dek-outpath-[AES/SM4、HMAC] 加密时:将 encrypted_dek 写入该文件
--encrypted-dek / --encrypted-dek-filestring/path-[AES/SM4、HMAC] 解密/校验时:encrypted_dek 内容或文件路径
--nonce-out / --nonce-filepath-[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
aliyunkms.{region}.aliyuncs.comkms.cn-hangzhou.aliyuncs.com
huaweikms.{region}.myhuaweicloud.comkms.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 模式一致,可被标准工具解析
noncebase64,12 字节单独输出,解密必需
encrypted_dekbase64(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_dekbase64(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.5setup.py 增加 extras_requirekms = ["boto3"]可选依赖声明

阶段二:AWS KMS 适配器

步骤内容产出
2.1实现 kms/backends/aws.py:AWSKMSBackend完整实现
2.2实现 kms/backends/__init__.py:get_backend(“aws”)工厂
2.3单元测试(可 mock boto3)测试覆盖

阶段三:信封加密集成(最小侵入)

步骤内容产出
3.1envelope_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 DecryptRSA 解密
5.3修改 rsa_command.py:sign 增加 --kms-key-id,调用 KMS SignRSA 签名
5.4修改 sm2_command.py:同上(若 KMS 支持 SM2)SM2 集成

阶段六:HMAC 集成

步骤内容产出
6.1修改 hmac_command.py:增加 --kms-key-id,KMS GenerateDataKey 取 keyHMAC KMS

阶段七:可选依赖与优雅降级

步骤内容产出
7.1各命令入口:若 --kms-key-idnot 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.optionrequired 保持为 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

或更简单:将原 -frequired=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.tomlextras_require
docs/ENVELOPE_FORMAT_SPEC.mdwrap_key_type 扩展说明
docs/CLI_COMMAND_REFERENCE.md新增参数说明

8.3 不修改文件

  • common.py
  • random_str.py
  • crypto/ 下各加密实现
  • output_builder.pyoutput_renderer.py 等输出层

9. 凭证与配置

9.1 凭证(各云厂商)

  • AWS:环境变量 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY;配置文件 ~/.aws/credentials;IAM Role
  • 阿里云:环境变量 ALIBABA_CLOUD_ACCESS_KEY_IDALIBABA_CLOUD_ACCESS_KEY_SECRET;配置文件
  • 华为云:环境变量 HUAWEICLOUD_ACCESS_KEY_IDHUAWEICLOUD_SECRET_ACCESS_KEY;配置文件

9.2 配置优先级(region)

  1. 显式 CLI 参数:--kms-region(KMS 模式下必选,由用户指定)
  2. 环境变量:AWS_DEFAULT_REGIONAWS_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 构造函数接收 regionendpoint(可选),若 endpoint 为空则按 region 构造默认。


10. 兼容性与迁移

10.1 向后兼容

  • 所有 --kms-key-id 均为可选,不指定时行为与当前完全一致
  • 现有信封(wrap_key_type=0x01)解密逻辑不变
  • 无 KMS 依赖时,相关选项传入会提示安装,不改变现有命令行为

10.2 迁移路径

  • 用户无需迁移,KMS 为纯增量能力
  • 旧版 eet 无法解密 wrap_key_type=0x02 的信封,需升级