调整部分前端布局和后端增删改逻辑
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from .service import CRYPTO_RUNTIME, CryptoRuntimeService
|
||||
|
||||
__all__ = ['CRYPTO_RUNTIME', 'CryptoRuntimeService']
|
||||
@@ -0,0 +1,56 @@
|
||||
from importlib import import_module
|
||||
from typing import Any
|
||||
|
||||
|
||||
class CryptoInfrastructureGateway:
|
||||
"""
|
||||
传输加密基础设施网关。
|
||||
|
||||
该对象负责延迟加载 cryptography、环境配置和传输加密工具依赖,
|
||||
供传输加密运行时 facade 与其协作对象统一复用。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_serialization_module() -> Any:
|
||||
"""
|
||||
获取 cryptography 序列化模块。
|
||||
|
||||
:return: 序列化模块
|
||||
"""
|
||||
return import_module('cryptography.hazmat.primitives.serialization')
|
||||
|
||||
@staticmethod
|
||||
def get_rsa_module() -> Any:
|
||||
"""
|
||||
获取 RSA 非对称算法模块。
|
||||
|
||||
:return: RSA 模块
|
||||
"""
|
||||
return import_module('cryptography.hazmat.primitives.asymmetric.rsa')
|
||||
|
||||
@staticmethod
|
||||
def get_transport_crypto_config() -> Any:
|
||||
"""
|
||||
获取传输加密配置对象。
|
||||
|
||||
:return: 传输加密配置对象
|
||||
"""
|
||||
return import_module('config.env').TransportCryptoConfig
|
||||
|
||||
@staticmethod
|
||||
def get_transport_crypto_util() -> Any:
|
||||
"""
|
||||
获取传输加密工具类。
|
||||
|
||||
:return: 传输加密工具类
|
||||
"""
|
||||
return import_module('utils.transport_crypto_util').TransportCryptoUtil
|
||||
|
||||
@staticmethod
|
||||
def get_transport_key_provider() -> Any:
|
||||
"""
|
||||
获取传输加密密钥提供者。
|
||||
|
||||
:return: 传输加密密钥提供者
|
||||
"""
|
||||
return import_module('utils.transport_crypto_util').TransportKeyProvider
|
||||
@@ -0,0 +1,141 @@
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from .gateway import CryptoInfrastructureGateway
|
||||
from .support import CryptoDomainSupport, CryptoResultSupport
|
||||
|
||||
|
||||
class CryptoRuntimeService:
|
||||
"""
|
||||
传输加密运行时服务。
|
||||
|
||||
该服务作为传输加密运行时 facade,对外统一暴露配置校验、
|
||||
公钥导出、密钥生成和轮换辅助方案构建入口。
|
||||
|
||||
:param infrastructure_gateway: 传输加密基础设施网关
|
||||
:param domain_support: 传输加密领域支持对象
|
||||
:param result_support: 传输加密结果支持对象
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
infrastructure_gateway: CryptoInfrastructureGateway | None = None,
|
||||
domain_support: CryptoDomainSupport | None = None,
|
||||
result_support: CryptoResultSupport | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
初始化传输加密运行时服务。
|
||||
|
||||
:param infrastructure_gateway: 传输加密基础设施网关
|
||||
:param domain_support: 传输加密领域支持对象
|
||||
:param result_support: 传输加密结果支持对象
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway or CryptoInfrastructureGateway()
|
||||
self.domain_support = domain_support or CryptoDomainSupport(self.infrastructure_gateway)
|
||||
self.result_support = result_support or CryptoResultSupport()
|
||||
|
||||
def generate_crypto_key_pair(self, kid: str, key_size: int) -> dict[str, Any]:
|
||||
"""
|
||||
生成新的传输加密密钥对。
|
||||
|
||||
:param kid: 目标密钥版本标识
|
||||
:param key_size: RSA 密钥长度
|
||||
:return: 密钥生成结果
|
||||
"""
|
||||
|
||||
def _operation() -> dict[str, Any]:
|
||||
self.domain_support.validate_rsa_key_size(key_size)
|
||||
private_key_pem, public_key_pem = self.domain_support.generate_rsa_key_pair(key_size)
|
||||
return {
|
||||
'ok': True,
|
||||
'message': '传输加密密钥生成完成',
|
||||
'kid': kid,
|
||||
'keySize': key_size,
|
||||
'privateKey': private_key_pem,
|
||||
'publicKey': public_key_pem,
|
||||
'envPatch': self.domain_support.build_key_pair_env_patch(kid, private_key_pem, public_key_pem),
|
||||
}
|
||||
|
||||
return self.result_support.run_argument_guarded(_operation, failure_message='生成传输加密密钥失败')
|
||||
|
||||
def validate_crypto_config(self) -> dict[str, Any]:
|
||||
"""
|
||||
校验传输加密运行配置。
|
||||
|
||||
:return: 传输加密配置校验结果
|
||||
"""
|
||||
transport_key_provider = self.infrastructure_gateway.get_transport_key_provider()
|
||||
|
||||
def _operation() -> dict[str, Any]:
|
||||
transport_key_provider.validate_runtime_configuration()
|
||||
return {'ok': True, 'message': '传输加密配置校验通过'}
|
||||
|
||||
return self.result_support.run_runtime_guarded(_operation, failure_message='传输加密配置校验失败')
|
||||
|
||||
def export_public_key(self) -> dict[str, Any]:
|
||||
"""
|
||||
导出当前运行环境的传输加密公钥信息。
|
||||
|
||||
:return: 公钥导出结果
|
||||
"""
|
||||
transport_key_provider = self.infrastructure_gateway.get_transport_key_provider()
|
||||
transport_crypto_util = self.infrastructure_gateway.get_transport_crypto_util()
|
||||
|
||||
def _operation() -> dict[str, Any]:
|
||||
transport_key_provider.validate_runtime_configuration()
|
||||
return {'ok': True, 'publicKey': transport_crypto_util.build_public_key_payload()}
|
||||
|
||||
return self.result_support.run_runtime_guarded(_operation, failure_message='导出传输加密公钥失败')
|
||||
|
||||
def build_rotation_payload(self, next_kid: str, key_size: int) -> dict[str, Any]:
|
||||
"""
|
||||
生成传输加密密钥轮换辅助结果。
|
||||
|
||||
:param next_kid: 新密钥版本标识
|
||||
:param key_size: 新密钥的 RSA 长度
|
||||
:return: 轮换辅助结果
|
||||
"""
|
||||
transport_key_provider = self.infrastructure_gateway.get_transport_key_provider()
|
||||
|
||||
def _operation() -> dict[str, Any]:
|
||||
self.domain_support.validate_rsa_key_size(key_size)
|
||||
transport_key_provider.validate_runtime_configuration()
|
||||
current_key_pair = transport_key_provider.get_current_key_pair()
|
||||
if next_kid == current_key_pair.kid:
|
||||
raise ValueError('新密钥版本不能与当前版本相同')
|
||||
|
||||
private_key_pem, public_key_pem = self.domain_support.generate_rsa_key_pair(key_size)
|
||||
legacy_key_pairs = self.domain_support.load_existing_legacy_key_pairs()
|
||||
merged_legacy_key_pairs = self.domain_support.merge_rotation_legacy_key_pairs(
|
||||
legacy_key_pairs,
|
||||
current_key_pair,
|
||||
next_kid,
|
||||
)
|
||||
return {
|
||||
'ok': True,
|
||||
'message': '传输加密密钥轮换方案已生成',
|
||||
'currentKid': current_key_pair.kid,
|
||||
'nextKid': next_kid,
|
||||
'keySize': key_size,
|
||||
'generatedAt': datetime.now().isoformat(),
|
||||
'nextKeyPair': {
|
||||
'kid': next_kid,
|
||||
'privateKey': private_key_pem,
|
||||
'publicKey': public_key_pem,
|
||||
},
|
||||
'legacyKeyPairs': merged_legacy_key_pairs,
|
||||
'envPatch': self.domain_support.build_rotation_env_patch(
|
||||
next_kid,
|
||||
private_key_pem,
|
||||
public_key_pem,
|
||||
merged_legacy_key_pairs,
|
||||
),
|
||||
'applySteps': self.result_support.build_rotation_apply_steps(),
|
||||
}
|
||||
|
||||
return self.result_support.run_argument_guarded(_operation, failure_message='生成密钥轮换方案失败')
|
||||
|
||||
|
||||
CRYPTO_RUNTIME = CryptoRuntimeService()
|
||||
@@ -0,0 +1,234 @@
|
||||
import json
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from cli.exit_codes import ARGUMENT_ERROR, RUNTIME_ERROR
|
||||
|
||||
from .gateway import CryptoInfrastructureGateway
|
||||
|
||||
MIN_RSA_KEY_SIZE = 2048
|
||||
RSA_KEY_SIZE_STEP = 256
|
||||
|
||||
|
||||
class CryptoDomainSupport:
|
||||
"""
|
||||
传输加密领域支持对象。
|
||||
|
||||
该对象负责 RSA 密钥规则校验、密钥对生成和历史密钥规整,
|
||||
避免主运行时服务继续承载局部安全规则和数据拼装细节。
|
||||
|
||||
:param infrastructure_gateway: 传输加密基础设施网关
|
||||
"""
|
||||
|
||||
def __init__(self, infrastructure_gateway: CryptoInfrastructureGateway) -> None:
|
||||
"""
|
||||
初始化传输加密领域支持对象。
|
||||
|
||||
:param infrastructure_gateway: 传输加密基础设施网关
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway
|
||||
|
||||
@staticmethod
|
||||
def validate_rsa_key_size(key_size: int) -> None:
|
||||
"""
|
||||
校验 RSA 密钥长度是否合法。
|
||||
|
||||
:param key_size: RSA 密钥长度
|
||||
:return: None
|
||||
:raises ValueError: 密钥长度不满足约束时抛出异常
|
||||
"""
|
||||
if key_size < MIN_RSA_KEY_SIZE or key_size % RSA_KEY_SIZE_STEP != 0:
|
||||
raise ValueError('RSA 密钥长度必须大于等于 2048,且为 256 的整数倍')
|
||||
|
||||
def generate_rsa_key_pair(self, key_size: int) -> tuple[str, str]:
|
||||
"""
|
||||
生成 PEM 格式的 RSA 密钥对。
|
||||
|
||||
:param key_size: RSA 密钥长度
|
||||
:return: 私钥与公钥 PEM 字符串
|
||||
"""
|
||||
rsa_module = self.infrastructure_gateway.get_rsa_module()
|
||||
serialization_module = self.infrastructure_gateway.get_serialization_module()
|
||||
private_key = rsa_module.generate_private_key(public_exponent=65537, key_size=key_size)
|
||||
private_key_pem = private_key.private_bytes(
|
||||
encoding=serialization_module.Encoding.PEM,
|
||||
format=serialization_module.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization_module.NoEncryption(),
|
||||
).decode('utf-8')
|
||||
public_key_pem = (
|
||||
private_key.public_key()
|
||||
.public_bytes(
|
||||
encoding=serialization_module.Encoding.PEM,
|
||||
format=serialization_module.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
.decode('utf-8')
|
||||
)
|
||||
return private_key_pem, public_key_pem
|
||||
|
||||
def load_existing_legacy_key_pairs(self) -> list[dict[str, str]]:
|
||||
"""
|
||||
解析当前配置中的历史密钥列表。
|
||||
|
||||
:return: 历史密钥列表
|
||||
:raises ValueError: 历史密钥配置非法时抛出异常
|
||||
"""
|
||||
transport_crypto_config = self.infrastructure_gateway.get_transport_crypto_config()
|
||||
raw_legacy_key_pairs = transport_crypto_config.transport_crypto_legacy_key_pairs or '[]'
|
||||
parsed_legacy_key_pairs = json.loads(raw_legacy_key_pairs)
|
||||
if not isinstance(parsed_legacy_key_pairs, list):
|
||||
raise ValueError('TRANSPORT_CRYPTO_LEGACY_KEY_PAIRS 必须为 JSON 数组')
|
||||
return [item for item in parsed_legacy_key_pairs if isinstance(item, dict)]
|
||||
|
||||
@staticmethod
|
||||
def merge_rotation_legacy_key_pairs(
|
||||
legacy_key_pairs: list[dict[str, str]],
|
||||
current_key_pair: Any,
|
||||
next_kid: str,
|
||||
) -> list[dict[str, str]]:
|
||||
"""
|
||||
合并轮换后的历史密钥列表。
|
||||
|
||||
:param legacy_key_pairs: 当前历史密钥列表
|
||||
:param current_key_pair: 当前生效密钥对对象
|
||||
:param next_kid: 即将切换的新密钥版本
|
||||
:return: 合并后的历史密钥列表
|
||||
"""
|
||||
normalized_legacy_key_pairs: dict[str, dict[str, str]] = {
|
||||
str(item.get('kid')): item for item in legacy_key_pairs if item.get('kid')
|
||||
}
|
||||
normalized_legacy_key_pairs[current_key_pair.kid] = {
|
||||
'kid': current_key_pair.kid,
|
||||
'privateKey': current_key_pair.private_key_pem,
|
||||
'publicKey': current_key_pair.public_key_pem,
|
||||
}
|
||||
normalized_legacy_key_pairs.pop(next_kid, None)
|
||||
return list(normalized_legacy_key_pairs.values())
|
||||
|
||||
@staticmethod
|
||||
def build_key_pair_env_patch(kid: str, private_key_pem: str, public_key_pem: str) -> dict[str, str]:
|
||||
"""
|
||||
构建密钥对写回环境变量所需的补丁数据。
|
||||
|
||||
:param kid: 密钥版本标识
|
||||
:param private_key_pem: 私钥 PEM 文本
|
||||
:param public_key_pem: 公钥 PEM 文本
|
||||
:return: 环境变量补丁数据
|
||||
"""
|
||||
return {
|
||||
'TRANSPORT_CRYPTO_KID': kid,
|
||||
'TRANSPORT_CRYPTO_PRIVATE_KEY': private_key_pem,
|
||||
'TRANSPORT_CRYPTO_PUBLIC_KEY': public_key_pem,
|
||||
}
|
||||
|
||||
def build_rotation_env_patch(
|
||||
self,
|
||||
next_kid: str,
|
||||
private_key_pem: str,
|
||||
public_key_pem: str,
|
||||
merged_legacy_key_pairs: list[dict[str, str]],
|
||||
) -> dict[str, str]:
|
||||
"""
|
||||
构建密钥轮换写回环境变量所需的补丁数据。
|
||||
|
||||
:param next_kid: 新密钥版本标识
|
||||
:param private_key_pem: 新私钥 PEM 文本
|
||||
:param public_key_pem: 新公钥 PEM 文本
|
||||
:param merged_legacy_key_pairs: 合并后的历史密钥列表
|
||||
:return: 环境变量补丁数据
|
||||
"""
|
||||
env_patch = self.build_key_pair_env_patch(next_kid, private_key_pem, public_key_pem)
|
||||
env_patch['TRANSPORT_CRYPTO_LEGACY_KEY_PAIRS'] = json.dumps(merged_legacy_key_pairs, ensure_ascii=False)
|
||||
return env_patch
|
||||
|
||||
|
||||
class CryptoResultSupport:
|
||||
"""
|
||||
传输加密结果支持对象。
|
||||
|
||||
该对象负责参数错误/运行时错误结果翻译,以及固定轮换提示文案构建,
|
||||
避免主运行时服务继续承载重复异常映射与静态 payload 片段。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def build_argument_error_result(message: str, exc: Exception) -> dict[str, Any]:
|
||||
"""
|
||||
构建参数错误结果。
|
||||
|
||||
:param message: 失败消息
|
||||
:param exc: 原始异常
|
||||
:return: 标准参数错误结果
|
||||
"""
|
||||
return {
|
||||
'ok': False,
|
||||
'message': message,
|
||||
'error': str(exc),
|
||||
'exit_code': ARGUMENT_ERROR,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def build_runtime_error_result(message: str, exc: Exception) -> dict[str, Any]:
|
||||
"""
|
||||
构建运行时错误结果。
|
||||
|
||||
:param message: 失败消息
|
||||
:param exc: 原始异常
|
||||
:return: 标准运行时错误结果
|
||||
"""
|
||||
return {
|
||||
'ok': False,
|
||||
'message': message,
|
||||
'error': str(exc),
|
||||
'exit_code': RUNTIME_ERROR,
|
||||
}
|
||||
|
||||
def run_argument_guarded(
|
||||
self,
|
||||
operation: Callable[[], dict[str, Any]],
|
||||
*,
|
||||
failure_message: str,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
以统一参数/运行时错误翻译执行操作。
|
||||
|
||||
:param operation: 待执行操作
|
||||
:param failure_message: 失败消息
|
||||
:return: 操作结果
|
||||
"""
|
||||
try:
|
||||
return operation()
|
||||
except ValueError as exc:
|
||||
return self.build_argument_error_result(failure_message, exc)
|
||||
except Exception as exc:
|
||||
return self.build_runtime_error_result(failure_message, exc)
|
||||
|
||||
def run_runtime_guarded(
|
||||
self,
|
||||
operation: Callable[[], dict[str, Any]],
|
||||
*,
|
||||
failure_message: str,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
以统一运行时错误翻译执行操作。
|
||||
|
||||
:param operation: 待执行操作
|
||||
:param failure_message: 失败消息
|
||||
:return: 操作结果
|
||||
"""
|
||||
try:
|
||||
return operation()
|
||||
except Exception as exc:
|
||||
return self.build_runtime_error_result(failure_message, exc)
|
||||
|
||||
@staticmethod
|
||||
def build_rotation_apply_steps() -> list[str]:
|
||||
"""
|
||||
构建密钥轮换后的应用步骤说明。
|
||||
|
||||
:return: 轮换应用步骤列表
|
||||
"""
|
||||
return [
|
||||
'将 envPatch 中的新密钥写入目标环境配置',
|
||||
'发布后保留 legacyKeyPairs,确保旧请求在轮换窗口内可解密',
|
||||
'确认前端已刷新到 nextKid 后,再移除不再需要的历史密钥',
|
||||
]
|
||||
Reference in New Issue
Block a user