Logo
密钥管理

密钥管理 #

背景 #

在数据加密、数据脱敏、数据防篡改等安全能力中,密钥通常需要被多个规则使用。如果直接将密钥明文散落在算法配置中,会带来如下问题:

  1. 密钥以明文形式出现在配置文件中,安全风险较高;
  2. 同一份密钥需要在多个算法中重复配置,运维成本较高;
  3. 密钥来源、存储方式和业务算法强耦合,不利于统一治理;
  4. 密钥加载和卸载缺少统一入口,难以保证一致性。

为解决上述问题,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 会根据 namespacekeyMappings 从 Key Manager 的明文缓存中取值,并将其注入目标算法配置。这样可以在不暴露明文密钥的前提下,让加密、脱敏等规则使用统一管理的密钥。

系统实现 #

Key Manager 以全局规则的形式存在,其核心配置包括 providerTypestorageTypedefaultNamespaceprovidersstoragessecretKeys

整体处理流程如下:

  1. 通过 YAML 或 DistSQL 定义 Key Manager 规则,指定默认 Provider、Storage 以及默认 Namespace
  2. 加载密钥时,Key Manager 先通过 Storage 检查目标 Namespace 中是否存在重复密钥
  3. 若校验通过,则调用 Provider 根据密钥名称获取密文,并将密文写入规则配置中的 secretKeys
  4. 规则构建阶段,Key Manager 会对 secretKeys 中的密文进行内存解密
  5. 业务规则初始化算法时,如发现配置中声明了 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>

其中:

  • providersstorages 用于声明可用实现
  • providerTypestorageType 用于指定当前启用的实现
  • 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_useruser_namepasswordemailuser_telephone 四个列分别配置独立密钥,并通过加密规则完成透明加密。操作步骤如下:

  1. 定义 Key Manager 全局规则;
  2. 通过 Provider 加载远程密钥;
  3. 为每个加密列创建 Connector,用于获取 Key Manager 里已经存储的秘钥;
  4. 创建加密规则;
  5. 创建业务表并执行增删改查。

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 在安全治理方向上的基础能力。它通过全局规则统一管理密钥来源、命名空间、存储和运行时分发机制,为加密、脱敏等规则提供标准化的密钥接入方式。