GaussDB-透明数据加密
GaussDB-透明数据加密
透明加密提供表级数据加密存储功能。当用户使用本特性提供的语法创建加密表后,数据库向磁盘写入加密表数据前,会自动将其加密;同时,数据库从磁盘读取加密表数据后,会自动将其解密。向加密表中进行数据插入、更新、查询和删除等语法与非加密表一致。

- 透明加密基本原理请参见《特性描述》的“数据库安全 > 透明数据加密”章节。
- 使用透明加密前需联系管理员配置开启该功能。
查看透明加密基本配置
- 查看透明加密功能是否已开启
enable_tde取值为on时表示开启,取值为off是表示关闭。该参数由管理员设置。
gaussdb=# SHOW enable_tde; enable_tde ------------ on (1 row)
- 查看是否已设置访问密钥管理服务的参数
tde_key_info参数为空时表示未设置,tde_key_info不为空时表示已设置。该参数由管理员设置。
gaussdb=# show tde_key_info; tde_key_info ------------------------- keyType=...
操作加密表
- 创建加密表。
创建表时,通过在WITH子句中设置enable_tde=on参数,即可设置该表为加密表。
数据库默认使用'AES_128_CTR'算法对加密表进行加密,如需使用其他算法,可通过encrypt_algo参数设置。
当透明加密基本配置的tde_key_info中的keyType为third_kms时。可以设置dek_token作为数据密钥的ID,并会在透明加密时传给第三方加解密库。gaussdb=# CREATE TABLE t1 (c1 INT, c2 TEXT) WITH (enable_tde = on); CREATE TABLE gaussdb=# CREATE TABLE t2 (c1 INT, c2 TEXT) WITH (enable_tde = on, encrypt_algo = 'SM4_CTR'); CREATE TABLE
- 查看加密表基本信息。
加密表基本信息存储在pg_class系统表中的reloptions字段中。其中,dek_cipher为数据密钥密文,由数据库自动生成,并由密钥管理服务加密。每个加密表都有1个独立的数据密钥。
gaussdb=# SELECT relname,reloptions FROM pg_class WHERE relname = 't1'; relname | reloptions ---------+----------------------------------------------------------------------------------------------------------------------- t1 | {orientation=row,enable_tde=on,encrypt_algo=AES_128_CTR,compression=no,storage_type=USTORE,key_type=...,dek_cipher=...
- 向加密表写入数据。
操作加密表与非加密表的语法一致。数据库将表中数据写入磁盘前,才会自动对加密表的数据进行加密。
gaussdb=# INSERT INTO t1 VALUES (1, 'tde plain 123'); INSERT 0 1
- 从加密表查询数据。
对于合法用户而言,查询加密表与非加密表的语法一致,加解密操作由数据库自动实现。如果攻击者绕过数据库,直接读取磁盘上加密表对应的数据文件,会发现文件中的数据均已被加密。
gaussdb=# SELECT * FROM t1; c1 | c2 ----+--------------- 1 | tde plain 123 (1 row)
- 轮转加密表的密钥。
为提高安全性,建议定期使用以下语法轮转加密表的数据密钥,即使用新的密钥对数据进行加密。
gaussdb=# ALTER TABLE t1 ENCRYPTION KEY ROTATION; ALTER TABLE
轮转密钥后,数据库仍可以正常解密由旧密钥加密的数据。
- 加密表与非加密表转换。
透明加密支持将加密表转换为非加密表,以及将非加密表转换为加密表。建议在每次转换后,手动执行VACUUM FULL tablename命令,以强制同步转换表中所有数据。
gaussdb=# CREATE TABLE t3 (c1 INT, c2 TEXT); CREATE TABLE gaussdb=# ALTER TABLE t3 SET (enable_tde = on); ALTER TABLE gaussdb=# VACUUM FULL t3; VACUUM gaussdb=# ALTER TABLE t3 SET (enable_tde = off); ALTER TABLE gaussdb=# VACUUM FULL t3; VACUUM
- 删除加密表。
gaussdb=# DROP TABLE IF EXISTS t1, t2, t3; DROP TABLE
操作加密索引
- 创建加密表。
创建索引的基表,需确保基表也是加密表。
gaussdb=# CREATE TABLE t1 (c1 INT, c2 TEXT) WITH (enable_tde = on); CREATE TABLE
- 创建加密索引。
与创建加密表的方式相同,通过在WITH子句中设置enable_tde=on参数,即将索引设置为加密索引。
索引与基表使用相同的加密算法和密钥,对基表进行密钥轮转时,索引也会使用新密钥。
gaussdb=# CREATE INDEX i1 ON t1(c2) WITH (enable_tde = on); CREATE INDEX
- 查看加密索引基本信息。
与加密表一样,索引基本信息也存储在pg_class系统表中的reloptions字段中,索引的dek_cipher、encrypt_algo等参数与基表保持一致。
gaussdb=# SELECT relname,reloptions FROM pg_class WHERE relname = 'i1'; relname | reloptions ---------+----------------------------------------------------------------------------------------------------------------------- i1 | {orientation=row,enable_tde=on,encrypt_algo=AES_128_CTR,compression=no,storage_type=USTORE,key_type=...,dek_cipher=...
- 加密索引与非加密索引转换。
透明加密支持将非加密索引转换为加密索引,将加密索引转换为非加密索引。
gaussdb=# CREATE TABLE t2 (c1 INT, c2 TEXT) WITH (enable_tde = on); ALTER TABLE gaussdb=# CREATE INDEX i2 ON t2(c2); CREATE INDEX gaussdb=# ALTER INDEX i2 SET (enable_tde = on); ALTER INDEX gaussdb=# ALTER INDEX i2 SET (enable_tde = off); ALTER INDEX
- 自动对索引进行加密。
默认情况下,主动设置enable_tde参数才可创建加密索引。当设置GUC参数tde_index_default_encrypt=on,且以加密表为基表创建索引时,数据库会自动将索引转换为加密索引。示例如下:
gaussdb=# CREATE TABLE t3 (c1 INT, c2 TEXT) WITH (enable_tde = on); ALTER TABLE gaussdb=# CREATE INDEX i3 ON t3(c2); CREATE INDEX gaussdb=# SELECT relname,reloptions FROM pg_class WHERE relname = 'i3'; relname | reloptions ---------+----------------------------------------------------------------------------------------------------------------------- i1 | {orientation=row,enable_tde=on,encrypt_algo=AES_128_CTR,compression=no,storage_type=USTORE,key_type=...,dek_cipher=... -- 解释:虽然未指定i3为加密索引,但是开启了tde_index_default_encrypt=on,且基表t3是加密表,数据库自动将i3转换为加密索引
- 删除加密表和索引。
gaussdb=# DROP TABLE IF EXISTS t1, t2, t3; DROP TABLE
使用third_kms三方厂商加解密库
透明加密特性需要使用third_kms三方厂商加解密库前需要按照以下操作步骤进行配置:
- 向第三方厂商获取名字为libthird_crypto.so的加解密文件及其使用说明。
- 将加解密文件放置在$GAUSSHOME下的lib目录中,给予数据库进程可读权限。
- 管理员设置GUC参数crypto_module_info和tde_key_info。
其中module_third_msg为加密库加载时的必要信息,keyThirdMsg为加密库创建会话时的登录信息,这两项信息的格式和内容由第三方厂商定义,数据库仅通过crypto_module_init接口对该参数的合法性进行校验,建议第三方厂商通过该参数校验对应版本号的前后向兼容性。
gs_guc set -D $DATA_PATH -c "enable_tde=on" # 透明加密开关 gs_guc set -D $DATA_PATH -c "crypto_module_info='enable_crypto_module=on, module_third_msg=aaa'" gs_guc set -D $DATA_PATH -c "tde_key_info='keyType=third_kms, keyThirdMsg =aaa'"
- 重启数据库。
- 创建透明加密表及其列索引。
-- CREATE TABLE/INDEX语法中dek_token 为可选入参,可以用以传入数据密钥ID(hex格式)。不填则通过三方库密钥生成接口自动生成随机密钥。 gaussdb=# CREATE TABLE t1 (c1 INT, c2 TEXT) WITH (enable_tde = on, dek_token ='29a6b6ab63efa4687b4c62acb3746b90'); CREATE TABLE -- 可以通过ALTER SET语句设置新的密钥ID。 gaussdb=# ALTER TABLE t1 SET(dek_token = '29a6b6ab63efa4687b4c62acb3746b95'); ALTER TABLE -- 可以通过ALTER ENCRYPTION KEY ROTATION语句调取三方库密钥生成接口,自动生成新的随机密钥。 gaussdb=# ALTER TABLE t1 ENCRYPTION KEY ROTATION; ALTER TABLE gaussdb=# CREATE INDEX idx1 ON t1 USING ubtree(c2) WITH (enable_tde=on); CREATE INDEX
第三方厂商实现加解密接口注意事项
- 三方厂商需按照第三方厂商实现加解密接口头文件对接口进行实现并编译成以libthird_crypto.so命名的文件。
- 加密库链接时,仅可使用与数据库进程中相同版本的依赖库,不允许引入外部依赖库。
- 厂商提供加密库给客户时需保证编译环境与客户环境一致(例如:x86 or ARM,os-release版本等)。
- 在第三方厂商实现加解密接口头文件中,crypto_module_init的返回值SupportedFeature需要支持除MODULE_DETERMINISTIC_KEY外的所有算法,需要实现除my_cipher_deterministic_enc_dec外所有接口。(不实现确定性算法及其接口仅导致全密态等值查询不可使用该库,仍可供透明加密功能使用)
- 为保证兼容性,若涉及接口头文件升级,厂商需要基于升级后的头文件重新编译C函数共享库。
- 如果实现语言为C++,则需要为实现的函数加上extern "C",以保证数据库加载时能正确找到函数。
- 编译选项需要带上安全编译选项和优化选项,如trapv, Wl, relro, z, now, noexecstack, pie, PIC, stack-protector, O3等。

厂商应充分了解要实现的接口功能及入参范围,在调用前应检查参数合法性,避免出现空指针等可能导致程序crash的问题。厂商应避免接口调用过程中产生内存泄漏。
第三方厂商实现加解密接口头文件
#define CRYPT_MOD_OK 1 #define CRYPT_MOD_ERR 0 typedef enum { MODULE_AES_128_CBC = 0, MODULE_AES_128_CTR, MODULE_AES_128_GCM, MODULE_AES_256_CBC, MODULE_AES_256_CTR, MODULE_AES_256_GCM, MODULE_SM4_CBC, MODULE_SM4_CTR, MODULE_HMAC_SHA256, MODULE_HMAC_SM3, MODULE_DETERMINISTIC_KEY, MODULE_ALGO_MAX = 1024 } ModuleSymmKeyAlgo; typedef enum { MODULE_SHA256 = 0, MODULE_SM3, MODULE_DIGEST_MAX = 1024 } ModuleDigestAlgo; typedef enum { KEY_TYPE_INVALID, KEY_TYPE_PLAINTEXT, KEY_TYPE_CIPHERTEXT, KEY_TYPE_NAMEORIDX, KEY_TYPE_MAX } KeyType; typedef struct { KeyType key_type; int supported_symm[MODULE_ALGO_MAX]; // 不支持算法填入0或者支持算法填入1 int supported_digest[MODULE_DIGEST_MAX]; // 不支持算法填入0或者支持算法填入1 } SupportedFeature; /** 初始化密码模块 * * @param[in] * load_info 密码模块相关信息(硬件设备密码,硬件设备、硬件库路径等),通过kv方式传入 * * @param[out] * supported_feature 返回当前密码模块支持的加密方式,参考上述结构体 * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_module_init(char *load_info, SupportedFeature *supported_feature); /** 会话中连接密码模块 * * @param[in] * key_info 密码相关信息(用户密码等信息),通过kv方式传入 * * @param[out] * sess 会话信息 * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_module_sess_init(char *key_info, void **sess); /** 会话中断开连接密码模块 * * @param[in] * sess 会话信息 * * @param[out] * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ void crypto_module_sess_exit(void *sess); /** 创建密钥 * * @param[in] * sess 会话信息 * algo 密钥使用场景的算法 * * @param[out] * key_id 返回生成密钥/密钥ID/密钥密文 * key_id_size 返回生成内容长度 * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_create_symm_key(void *sess, ModuleSymmKeyAlgo algo, unsigned char *key_id, size_t *key_id_size); /** 密钥上下文初始化,后续进行加解密可直接使用上下文 * * @param[in] * sess 会话信息 * algo 加密算法 * enc 加密1、解密0 * key_id 密码信息 * key_id_size 密码信息长度 * @param[out] * ctx 返回使用密钥信息 * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_ctx_init(void *sess, void **ctx, ModuleSymmKeyAlgo algo, int enc, unsigned char *key_id, size_t key_id_size); /** 获取数据加解密后的数据长度 * * @param[in] * ctx 加解密上下文信息 * enc 加密1、解密0 * @param[out] * data_size 返回加解密结果长度 * @return 成功返回数据长度,失败返回-1 * */ int crypto_result_size(void *ctx, int enc, size_t data_size); /** 密钥上下文清理 * * @param[in] * ctx 加解密上下文信息 * @param[out] * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ void crypto_ctx_clean(void *ctx); /** 执行加解密 * * @param[in] * ctx 加解密上下文信息 * enc 加密1、解密0 * data 原数据信息 * data_size 原数据长度 * iv iv信息 * iv_size iv信息长度 * tag GCM模式的校验值 * @param[out] * result 返回结果信息 * result_size 返回结果信息长度 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_encrypt_decrypt(void *ctx, int enc, unsigned char *data, size_t data_size, unsigned char *iv, size_t iv_size, unsigned char *result, size_t result_size, unsigned char *tag); /** 计算摘要 * * @param[in] * sess 会话信息 * algo 摘要算法 * data 原数据信息 * data_size 原数据长度 * @param[out] * result 返回结果信息 * result_size 返回结果信息长度 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_digest(void *sess, ModuleDigestAlgo algo, unsigned char * data, size_t data_size,unsigned char *result, size_t *result_size); /** hmac初始化 * * @param[in] * sess 会话信息 * algo 摘要算法 * key_id 密码信息 * key_id_size 密码信息长度 * @param[out] * ctx 返回密钥上下文 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_hmac_init(void *sess, void **ctx, ModuleDigestAlgo algo, unsigned char *key_id, size_t key_id_size); /** hmac清理 * * @param[in] * ctx 密钥上下文信息 * * @param[out] * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ void crypto_hmac_clean(void *ctx); /** 执行hmac计算 * * @param[in] * ctx 密钥上下文信息 * data 原数据信息 * data_size 原数据长度 * @param[out] * result 返回结果信息 * result_size 返回结果信息长度 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_hmac(void *ctx, unsigned char * data, size_t data_size, unsigned char *result, size_t *result_size); /** 生成随机数 * * @param[in] * sess 会话信息 * size 申请的随机信息长度 * * @param[out] * buffer 返回随机信息 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_gen_random(void *sess, char *buffer, size_t size); /** 执行确定性加解密 * * @param[in] * sess 会话信息 * enc 加密1、解密0 * data 原数据信息 * data_size 原数据长度 * key_id 密钥信息 * key_id_size 密钥信息长度 * @param[out] * result 返回结果信息 * result_size 返回结果信息长度 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_deterministic_enc_dec(void *sess, int enc, unsigned char *data, unsigned char *key_id, size_t key_id_size, size_t data_size, unsigned char *result, size_t *result_size); /** 获取报错信息 * * @param[in] * sess 会话信息 * @param[out] * errmsg 返回结果信息,最长256字节 * * @return 成功返回CRYPT_MOD_OK,失败返回错误码 * */ int crypto_get_errmsg(void *sess, char *errmsg);