|
"""
secure_storage.py
OpenClaw 安全存储模块 - AES-256-GCM 加密
设计原则:
1. AES-256-GCM 提供认证加密(同时保证机密性和完整性)
2. 每次加密生成随机 IV(初始化向量),防止相同明文产生相同密文
3. 密钥从环境变量读取,绝不硬编码
4. 支持密钥版本化,方便轮转
"""
import os
import json
import base64
import hashlib
from typing import Dict, Optional, Any
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class SecureStorage:
"""
敏感数据加密存储管理器
使用 AES-256-GCM 认证加密算法:
- GCM 模式同时提供加密和完整性验证
- 256 位密钥,当前计算能力下暴力 破解不可行
- 每次加密使用随机 Nonce(12字节)
"""
def __init__(self, key_version: str = "v1"):
"""
初始化加密管理器
密钥从环境变量 OPENCLAW_ENCRYPTION_KEY 读取
支持多版本密钥(key_version)用于密钥轮转
"""
# 从环境变量读取主密钥
master_key_hex = os.environ.get("OPENCLAW_ENCRYPTION_KEY")
if not master_key_hex:
raise ValueError(
"? 未设置 OPENCLAW_ENCRYPTION_KEY 环境变量\n"
"???? 生成密钥: python3 -c \"import os; print(os.urandom(32).hex())\""
)
# 多版本密钥派生
self.key_version = key_version
self._keys = self._derive_keys(bytes.fromhex(master_key_hex))
self._aesgcm = AESGCM(self._keys[key_version])
def _derive_keys(self, master_key: bytes) -> Dict[str, bytes]:
"""从主密钥派生多版本密钥(支持密钥轮转)"""
keys = {}
for version in ["v1", "v2", "v3"]:
salt = f"openclaw-key-{version}".encode()
derived = hashlib.pbkdf2_hmac(
"sha256", master_key, salt, 100000, dklen=32
)
keys[version] = derived
return keys
def encrypt(self, plaintext: str) -> str:
"""
加密字符串
返回格式: base64(version || nonce || ciphertext)
前缀 version 用于解密时选择正确的密钥版本
"""
# 生成 12 字节随机 Nonce
nonce = os.urandom(12)
# 加密(AESGCM 自动附加认证标签)
plaintext_bytes = plaintext.encode("utf-8")
ciphertext = self._aesgcm.encrypt(nonce, plaintext_bytes, None)
# 打包:版本(2字节) + Nonce(12字节) + 密文
packed = (
self.key_version.encode() +
nonce +
ciphertext
)
return base64.b64encode(packed).decode("ascii")
def decrypt(self, encrypted: str) -> str:
"""
解密密文
自动识别密钥版本,支持密钥轮转期间的平滑过渡
"""
try:
packed = base64.b64decode(encrypted)
# 解析版本(前2字节)
version = packed[:2].decode()
if version not in self._keys:
raise ValueError(f"不支持的密钥版本: {version}")
nonce = packed[2:14]
ciphertext = packed[14:]
aesgcm = AESGCM(self._keys[version])
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
return plaintext.decode("utf-8")
except Exception as e:
raise DecryptionError(f"解密失败: {e}")
def encrypt_dict(self, data: Dict[str, Any]) -> str:
"""加密字典(自动序列化为 JSON)"""
return self.encrypt(json.dumps(data, ensure_ascii=False))
def decrypt_dict(self, encrypted: str) -> Dict[str, Any]:
"""解密密文并反序列化为字典"""
return json.loads(self.decrypt(encrypted))
class DecryptionError(Exception):
"""解密异常"""
pass
# ============================================
# 使用示例
# ============================================
if __name__ == "__main__":
# 1. 生成主密钥(一次性操作)
# python3 -c "import os; print(os.urandom(32).hex())"
store = SecureStorage()
# 2. 加密敏感数据
api_key = "sk-proj-this-is-a-secret-key-12345"
encrypted = store.encrypt(api_key)
print(f"???? 加密后: {encrypted[:50]}...")
# 3. 解密验证
decrypted = store.decrypt(encrypted)
print(f"???? 解密后: {decrypted}")
assert decrypted == api_key, "加解密不匹配!"
print("? 加解密验证通过")
# 4. 加密结构化数据
config = {
"openai_key": "sk-xxx",
"feishu_secret": "abc123",
"db_password": "p@ssw0rd"
}
encrypted_config = store.encrypt_dict(config)
print(f"???? 加密配置: {encrypted_config[:50]}...")
# 5. 相同明文产生不同密文(随机Nonce保证)
encrypted2 = store.encrypt(api_key)
print(f"???? 再次加密: {encrypted2[:50]}...")
print(f" 与上次不同: {encrypted != encrypted2}") # True
|