密钥管理 #
背景 #
在数据加密、数据脱敏、数据防篡改等安全能力中,密钥通常需要被多个规则使用。如果直接将密钥明文散落在算法配置中,会带来如下问题:
- 密钥以明文形式出现在配置文件中,安全风险较高;
- 同一份密钥需要在多个算法中重复配置,运维成本较高;
- 密钥来源、存储方式和业务算法强耦合,不利于统一治理;
- 密钥加载和卸载缺少统一入口,难以保证一致性。
为解决上述问题,SphereEx-DBPlusEngine 提供了 Key Manager 能力。该能力以全局规则的形式对密钥的加载、存储、解密和分发进行统一管理,并通过 Connector 机制将密钥注入加密、脱敏等业务规则。
核心概念 #
Provider #
Provider 用于定义密钥的来源,负责按照密钥名称加载密文密钥,并在需要时将密文解密为明文。
当前内置实现包括:
Local:从本地配置中读取密钥,并使用内置加密方式保存密文
对于远程密钥服务场景,通常由各个密钥提供厂商实现对应的 Provider 实现。Provider 需要屏蔽底层厂商接口差异,对 Key Manager 暴露统一的密钥加载和解密能力。
Storage #
Storage 用于定义密钥的存储与从存储中进行密钥查询能力。Key Manager 在加载密钥前,会先通过 Storage 检查指定命名空间中是否已存在同名密钥,以避免重复加载。
当前实现中,内置的存储方式为:
ZooKeeper:基于治理中心注册的 ZooKeeper 进行秘钥的加密存储与内存解密查询
Namespace #
Namespace 是密钥的逻辑隔离单元。不同业务、不同规则或不同环境可以通过 Namespace 对密钥进行隔离管理。Key Manager 支持配置默认 Namespace,也支持在 DistSQL 中显式指定 Namespace。
Secret Key #
Secret Key 是 Key Manager 管理的最小单位,由密钥名称和密文值组成。密钥名称由用户自行指定,规则配置中持久化的是密文;在运行期,Key Manager 会按需解密,并将明文缓存到 Engine 内存中,供业务规则使用。
为了支持一列一密钥的管理模式,推荐使用 ${databaseName}.${schemaName}.${tableName}.${columnName}.${keyName} 格式作为密钥名称。
例如,可将加密列所使用的密钥命名为 encrypt.public.t_user.password.aes-key-value。通过这种方式,可以使密钥命名更加清晰,避免后续遗忘密钥的具体用途,并便于日常排查和维护。
Connector #
Connector 用于将 Key Manager 管理的密钥转化为具体算法所需的属性配置。加密和脱敏等业务规则并不直接感知 Provider 和 Storage,而是通过 Connector 获取最终可用的算法参数。
其中,SPHEREEX_KEY_Connector 会根据 namespace 和 keyMappings 从 Key Manager 的明文缓存中取值,并将其注入目标算法配置。这样可以在不暴露明文密钥的前提下,让加密、脱敏等规则使用统一管理的密钥。
系统实现 #
Key Manager 以全局规则的形式存在,其核心配置包括 providerType、storageType、defaultNamespace、providers、storages 和 secretKeys。
整体处理流程如下:
- 通过 YAML 或 DistSQL 定义 Key Manager 规则,指定默认 Provider、Storage 以及默认 Namespace
- 加载密钥时,Key Manager 先通过 Storage 检查目标 Namespace 中是否存在重复密钥
- 若校验通过,则调用 Provider 根据密钥名称获取密文,并将密文写入规则配置中的
secretKeys - 规则构建阶段,Key Manager 会对
secretKeys中的密文进行内存解密 - 业务规则初始化算法时,如发现配置中声明了
key-manager-Connector,则通过 Connector 解析真实秘钥值,再完成算法初始化
该设计将“密钥来源”“密钥存储”和“算法使用”三个职责解耦:
- Provider 负责从外部系统获取密钥
- Storage 负责密钥查重与持久化访问
- Connector 负责将密钥映射为业务算法配置
因此,密钥管理能力可以独立演进,而无需侵入加密、脱敏等具体规则实现。
典型流程 #
规则定义 #
Key Manager 支持通过全局规则声明默认 Provider 和 Storage。示例如下:
rules:
- !KEY_MANAGER
providerType: Local
storageType: ZooKeeper
defaultNamespace: default
providers:
Local:
type: Local
props:
encrypt.encrypt.t_user.user_name.aes-key-value: 1234560bc
encrypt.encrypt.t_user.password.aes-key-value: 123456a0c
storages:
ZooKeeper:
type: ZooKeeper
secretKeys:
default:
encrypt.encrypt.t_user.user_name.aes-key-value: <encrypted-value>
encrypt.encrypt.t_user.password.aes-key-value: <encrypted-value>
其中:
providers和storages用于声明可用实现providerType和storageType用于指定当前启用的实现secretKeys按 namespace 保存已加载的密文密钥,正常不需要配置,通过LOAD SECRET KEYS从 provider 里加载对应密钥。
密钥加载 #
密钥加载通常通过 DistSQL 完成。执行 LOAD SECRET KEYS 时,系统会根据密钥名称调用 Provider 获取密文,并写入当前规则配置。
LOAD SECRET KEYS(
namespace='default',
key_names(
'encrypt.encrypt.t_user.user_name.aes-key-value',
'encrypt.encrypt.t_user.password.aes-key-value'
)
);
若已经配置了 defaultNamespace,则可以省略 namespace 参数。
密钥查询 #
为了便于运维管理,Key Manager 提供了多组查询语句:
SHOW KEY MANAGER RULE;
SHOW KEY MANAGER PROVIDERS;
SHOW KEY MANAGER STORAGES;
SHOW KEY MANAGER SECRET KEYS;
其中,SHOW KEY MANAGER SECRET KEYS 返回的是当前运行时可见的明文密钥,主要用于查看密钥是否已经被正确加载和解密。
密钥卸载 #
执行 UNLOAD SECRET KEYS 时,系统会先校验目标密钥是否仍被业务规则引用;若仍在使用,则拒绝卸载,以避免运行中规则失效。
UNLOAD SECRET KEYS(
namespace='default',
KEY_NAMES('encrypt.encrypt.t_user.password.aes-key-value')
);
与业务规则集成 #
业务规则通过 key-manager-Connector 属性引用 Connector,再由 Connector 从 Key Manager 获取真实密钥。下面以加密算法为例:
CREATE ENCRYPT Key Manager t_user_name_Connector (
TYPE(
NAME='SPHEREEX_KEY_Connector',
PROPERTIES(
'namespace'='default',
'keyMappings'='aes-key-value=encrypt.encrypt.t_user.user_name.aes-key-value'
)
)
);
其中:
namespace用于指定当前 Connector 从哪个密钥命名空间中获取密钥。Connector 会根据该值到对应 namespace 下查找已经加载并完成解密的密钥。keyMappings用于指定“算法属性名”和“密钥名称”之间的映射关系。左侧的aes-key-value是算法真正需要的参数名,右侧的encrypt.encrypt.t_user.user_name.aes-key-value是 Key Manager 中管理的密钥名称。
例如,先执行如下语句加载密钥:
LOAD SECRET KEYS(
namespace='default',
key_names('encrypt.encrypt.t_user.user_name.aes-key-value')
);
执行完成后,Key Manager 会在 default 命名空间下持有名为 encrypt.encrypt.t_user.user_name.aes-key-value 的密钥。此时,当 t_user_name_Connector 被算法引用时,Connector 会按照 keyMappings 的定义,到 default 命名空间中查找这个密钥,并将查到的明文值填充到算法的 aes-key-value 属性中。这样,算法最终拿到的是自己需要的 aes-key-value,而不是直接感知 Key Manager 内部的密钥名称。
随后,算法配置只需引用该 Connector:
ENCRYPT_ALGORITHM(
TYPE(
NAME='AES',
PROPERTIES('key-manager-Connector'='t_user_name_Connector')
)
)
在该模式下,算法配置中不再直接出现明文密钥,密钥的获取、映射和轮换均由 Key Manager 统一接管。
使用指南 #
Key Manager 配置加密规则 #
假设需要对逻辑表 t_user 的 user_name、password、email 和 user_telephone 四个列分别配置独立密钥,并通过加密规则完成透明加密。操作步骤如下:
- 定义 Key Manager 全局规则;
- 通过 Provider 加载远程密钥;
- 为每个加密列创建 Connector,用于获取 Key Manager 里已经存储的秘钥;
- 创建加密规则;
- 创建业务表并执行增删改查。
1. 定义 Key Manager 全局规则 #
首先定义当前使用的 Provider、Storage 和默认 Namespace。
下面示例使用 Local 作为 Provider(生产环境需要使用真实的 Provider 提供远程密钥获取能力),ZooKeeper 作为 Storage,默认 Namespace 为 default。
ALTER KEY MANAGER RULE (
DEFAULT_NAMESPACE='default',
PROVIDER_TYPE='Local',
STORAGE_TYPE='ZooKeeper',
PROVIDERS(
(
NAME='Local',
TYPE(
NAME='Local',
PROPERTIES(
'encrypt.encrypt.t_user.user_name.aes-key-value'='1234560bc',
'encrypt.encrypt.t_user.password.aes-key-value'='123456a0c',
'encrypt.encrypt.t_user.email.aes-key-value'='123456ab0',
'encrypt.encrypt.t_user.user_telephone.aes-key-value'='023456abc'
)
)
)
),
STORAGES(
(
NAME='ZooKeeper',
TYPE(NAME='ZooKeeper')
)
)
);
执行完成后,可以通过以下语句确认规则已经生效:
SHOW KEY MANAGER RULE;
+---------------+--------------+-------------------+-----------+---------------+-------------+
| provider_type | storage_type | default_namespace | providers | storages | secret_keys |
+---------------+--------------+-------------------+-----------+---------------+-------------+
| Local | ZooKeeper | default | ["Local"] | ["ZooKeeper"] | |
+---------------+--------------+-------------------+-----------+---------------+-------------+
SHOW KEY MANAGER PROVIDERS;
+---------------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| provider_name | provider_type | provider_props |
+---------------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Local | Local | {"encrypt.encrypt.t_user.email.aes-key-value":"123456ab0","encrypt.encrypt.t_user.password.aes-key-value":"123456a0c","encrypt.encrypt.t_user.user_name.aes-key-value":"1234560bc","encrypt.encrypt.t_user.user_telephone.aes-key-value":"023456abc"} |
+---------------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
SHOW KEY MANAGER STORAGES;
+--------------+--------------+---------------+
| storage_name | storage_type | storage_props |
+--------------+--------------+---------------+
| ZooKeeper | ZooKeeper | |
+--------------+--------------+---------------+
-- 目前还没有加载密钥,所以密钥查询没有结果。
mysql> SHOW KEY MANAGER SECRET KEYS;
Empty set (0.00 sec)
2. 加载密钥 #
定义好规则后,需要将具体密钥按名称加载到 Key Manager 中。由于上一步已经配置了 DEFAULT_NAMESPACE='default',因此此处可以直接省略 namespace 参数。
LOAD SECRET KEYS(
key_names(
'encrypt.encrypt.t_user.user_name.aes-key-value',
'encrypt.encrypt.t_user.password.aes-key-value',
'encrypt.encrypt.t_user.email.aes-key-value',
'encrypt.encrypt.t_user.user_telephone.aes-key-value'
)
);
执行完成后,可通过以下语句查看当前已经加载的密钥:
SHOW KEY MANAGER SECRET KEYS;
+-----------+-----------------------------------------------------+--------------+
| namespace | secret_key | secret_value |
+-----------+-----------------------------------------------------+--------------+
| default | encrypt.encrypt.t_user.email.aes-key-value | 123456ab0 |
| default | encrypt.encrypt.t_user.user_name.aes-key-value | 1234560bc |
| default | encrypt.encrypt.t_user.password.aes-key-value | 123456a0c |
| default | encrypt.encrypt.t_user.user_telephone.aes-key-value | 023456abc |
+-----------+-----------------------------------------------------+--------------+
3. 为每个加密列创建 Connector #
密钥加载完成后,需要创建 Connector,将 Key Manager 中的密钥映射给具体加密算法使用。因为本例采用一列一密钥的方式,所以每个加密列创建一个独立 Connector。
-- keyMappings 指定当前算法使用的 aes-key-value 密钥对应的是 Key Manager 里存储的 encrypt.encrypt.t_user.user_name.aes-key-value
CREATE ENCRYPT KEY MANAGER t_user_user_name_aes_connector
(TYPE(NAME='SPHEREEX_KEY_CONNECTOR', PROPERTIES(
'namespace'='default',
'keyMappings'='aes-key-value=encrypt.encrypt.t_user.user_name.aes-key-value'
)));
CREATE ENCRYPT KEY MANAGER t_user_password_aes_connector
(TYPE(NAME='SPHEREEX_KEY_CONNECTOR', PROPERTIES(
'namespace'='default',
'keyMappings'='aes-key-value=encrypt.encrypt.t_user.password.aes-key-value'
)));
CREATE ENCRYPT KEY MANAGER t_user_email_aes_connector
(TYPE(NAME='SPHEREEX_KEY_CONNECTOR', PROPERTIES(
'namespace'='default',
'keyMappings'='aes-key-value=encrypt.encrypt.t_user.email.aes-key-value'
)));
CREATE ENCRYPT KEY MANAGER t_user_telephone_aes_connector
(TYPE(NAME='SPHEREEX_KEY_CONNECTOR', PROPERTIES(
'namespace'='default',
'keyMappings'='aes-key-value=encrypt.encrypt.t_user.user_telephone.aes-key-value'
)));
上述 connector 的作用是:当加密算法初始化时,自动从 default 命名空间中取出对应密钥,并将其注入算法的 aes-key-value 属性中。
4. 创建加密规则 #
完成 Connector 配置后,即可在加密规则中通过 key-manager-connector 引用这些 Connector,而不再直接配置明文密钥。
CREATE ENCRYPT RULE t_user (
COLUMNS(
(
NAME = user_name,
PLAIN = user_name_plain,
CIPHER = user_name_cipher,
LIKE_QUERY = user_name_like,
ENCRYPT_ALGORITHM(TYPE(NAME = 'AES', PROPERTIES(
'digest-algorithm-name' = 'SHA-1',
'key-manager-connector' = 't_user_user_name_aes_connector'
))),
LIKE_QUERY_ALGORITHM(TYPE(NAME = 'CHAR_TRANSFORM_LIKE', PROPERTIES(
'transform-indexes' = '1,2,3,5,7,14'
)))
),
(
NAME = password,
PLAIN = password_plain,
CIPHER = password_cipher,
ENCRYPT_ALGORITHM(TYPE(NAME = 'AES', PROPERTIES(
'digest-algorithm-name' = 'SHA-1',
'key-manager-connector' = 't_user_password_aes_connector'
)))
),
(
NAME = email,
PLAIN = email_plain,
CIPHER = email_cipher,
ENCRYPT_ALGORITHM(TYPE(NAME = 'AES', PROPERTIES(
'digest-algorithm-name' = 'SHA-1',
'key-manager-connector' = 't_user_email_aes_connector'
)))
),
(
NAME = user_telephone,
PLAIN = user_telephone_plain,
CIPHER = user_telephone_cipher,
LIKE_QUERY = user_telephone_like,
ENCRYPT_ALGORITHM(TYPE(NAME = 'AES', PROPERTIES(
'digest-algorithm-name' = 'SHA-1',
'key-manager-connector' = 't_user_telephone_aes_connector'
))),
LIKE_QUERY_ALGORITHM(TYPE(NAME = 'CHAR_TRANSFORM_LIKE', PROPERTIES(
'transform-indexes' = '1,2,3,5,7,14'
)))
)
),
QUERY_WITH_CIPHER_COLUMN=true
);
执行完成后,可通过以下语句查看加密规则:
SHOW ENCRYPT TABLE RULE t_user;
SHOW ENCRYPT KEY MANAGERS;
+--------------------------------+------------------------+-----------------------------------------------------------------------------------------------------------+
| name | type | props |
+--------------------------------+------------------------+-----------------------------------------------------------------------------------------------------------+
| t_user_user_name_aes_connector | SPHEREEX_KEY_CONNECTOR | {"keyMappings":"aes-key-value=encrypt.encrypt.t_user.user_name.aes-key-value","namespace":"default"} |
| t_user_password_aes_connector | SPHEREEX_KEY_CONNECTOR | {"keyMappings":"aes-key-value=encrypt.encrypt.t_user.password.aes-key-value","namespace":"default"} |
| t_user_email_aes_connector | SPHEREEX_KEY_CONNECTOR | {"keyMappings":"aes-key-value=encrypt.encrypt.t_user.email.aes-key-value","namespace":"default"} |
| t_user_telephone_aes_connector | SPHEREEX_KEY_CONNECTOR | {"keyMappings":"aes-key-value=encrypt.encrypt.t_user.user_telephone.aes-key-value","namespace":"default"} |
+--------------------------------+------------------------+-----------------------------------------------------------------------------------------------------------+
5. 创建业务表并执行业务 SQL #
通过 Proxy 创建加密表结构。
CREATE TABLE t_user (
user_id INT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL,
user_telephone VARCHAR(50) NOT NULL,
creation_date DATE NOT NULL
);
插入数据:
INSERT INTO t_user (user_id, user_name, password, email, user_telephone, creation_date) VALUES
(1, 'Alice', 'pass123', 'alice@example.com', '13800138001', '2024-01-01'),
(2, 'Bob', 'pass456', 'bob@example.com', '13900139002', '2024-01-02');
查询数据,测试功能正常:
mysql> select * from t_user;
+---------+-----------+----------+-------------------+----------------+---------------+
| user_id | user_name | password | email | user_telephone | creation_date |
+---------+-----------+----------+-------------------+----------------+---------------+
| 1 | Alice | pass123 | alice@example.com | 13800138001 | 2024-01-01 |
| 2 | Bob | pass456 | bob@example.com | 13900139002 | 2024-01-02 |
+---------+-----------+----------+-------------------+----------------+---------------+
总结 #
Key Manager 是 SphereEx-DBPlusEngine 在安全治理方向上的基础能力。它通过全局规则统一管理密钥来源、命名空间、存储和运行时分发机制,为加密、脱敏等规则提供标准化的密钥接入方式。