定义了完整的加密表后,用户就可以用正常的方式将数据插入到表中。完整的加密过程见encrypt_data函数,其核心逻辑代码如下所示:
int encrypt_data(const unsigned char *plain_text, int plain_text_length, const AeadAesHamcEncKey &column_encryption_key,
EncryptionType encryption_type, unsigned char *result, ColumnEncryptionAlgorithm column_encryption_algorithm)
{
……
/* 得到16位的iv值 */
unsigned char _iv [g_key_size + 1] = {0};
unsigned char iv_truncated[g_iv_size + 1] = {0};
/* 确定性加密,则通过hmac_sha256生成iv */
if (encryption_type == EncryptionType::DETERMINISTIC_TYPE) {
hmac_sha256(column_encryption_key.get_iv_key(), g_auth_tag_size, plain_text, plain_text_length, _iv);
……
} else {
/* 随机加密,则随机生成iv */
if (encryption_type != EncryptionType::RANDOMIZED_TYPE) {
return 0;
}
int res = RAND_priv_bytes(iv_truncated, g_block_size);
if (res != 1) {
return 0;
}
}
int cipherStart = g_algo_version_size + g_auth_tag_size + g_iv_size;
/* 调用encrypt计算密文 */
int cipherTextSize = encrypt(plain_text, plain_text_length, column_encryption_key.get_encyption_key(), iv_truncated,
result + cipherStart, column_encryption_algorithm);
……
int ivStartIndex = g_auth_tag_size + g_algo_version_size;
res = memcpy_s(result + ivStartIndex, g_iv_size, iv_truncated, g_iv_size);
securec_check_c(res, "\0", "\0");
/* 计算 HMAC */
int hmacDataSize = g_algo_version_size + g_iv_size + cipherTextSize;
hmac_sha256(column_encryption_key.get_mac_key(), g_auth_tag_size,
result + g_auth_tag_size, hmacDataSize, result);
return (g_auth_tag_size + hmacDataSize);
}
openGauss密态数据库在进行等值查询的时候,整个查询过程对用户是无感知的,虽然存储在数据库中的数据是密文形式,但在展示数据给用户的时候会将密文数据进行解密处理。以从加密表中进行等值查询语句为例,整个语句处理过程如图9-42所示。客户端解析SELECT查询语句中的列属性信息,如果缓存已有则从缓存中提取列属性信息;如果缓存中找不到,需要从服务端查询该信息,并缓存。列加密密钥CEK是以密文形式存储在服务端,客户端需要解密CEK。然后用其加密SELECT查询语句中条件参数。加密后的SELECT查询语句发送给数据库服务端执行完成后,返回加密的查询结果集。客户端用解密后的列加密密钥CEK解密SELECT查询结果集,并返回解密后的明文结果集给应用端。
图9-42 SELECT语句时序图
等值查询处理run_pre_insert_statement函数,其核心逻辑代码如下所示:
bool Processor::run_pre_select_statement(const SelectStmt * const select_stmt, const SetOperation &parent_set_operation,
const bool &parent_all, StatementData *statement_data, ICachedColumns *cached_columns, ICachedColumns *cached_columns_parents)
{
bool select_res = false;
/* 处理SELECT语句中的集合操作 */
if (select_stmt->op != SETOP_NONE) {
select_res = process_select_set_operation(select_stmt, statement_data, cached_columns);
RETURN_IF(!select_res);
}
/* 处理WHERE子句 */
ExprPartsList where_expr_parts_list;
select_res = exprProcessor::expand_expr(select_stmt->whereClause, statement_data, &where_expr_parts_list);
RETURN_IF(!select_res);
……
/* 从FROM子句中获取缓存加密列 */
CachedColumns cached_columns_from(false, true);
select_res = run_pre_from_list_statement(select_stmt->fromClause, statement_data, &cached_columns_from,
cached_columns_parents);
……
/* 将查询的加密列放在cached_columns结构中 */
for (size_t i = 0; i < cached_columns_from.size(); i++) {
if (find_in_name_map(target_list, cached_columns_from.at(i)->get_col_name())) {
CachedColumn *target = new (std::nothrow) CachedColumn(cached_columns_from.at(i));
if (target == NULL) {
fprintf(stderr, "failed to new CachedColumn object\n");
return false;
}
cached_columns->push(target);
}
}
if (cached_columns_from.is_empty()) {
return true;
}
/* 加密列不支持ORDER BY(排序)操作 */
if (!deal_order_by_statement(select_stmt, cached_columns)) {
return false;
}
/* 将WHERE子句中加密的值进行加密处理 */
if (!WhereClauseProcessor::process(&cached_columns_from, &where_expr_parts_list, statement_data)) {
return false;
}
……
return true;
}
完整的客户端密文解密函数代码如下所示:
int decrypt_data(const unsigned char *cipher_text, int cipher_text_length,
const AeadAesHamcEncKey &column_encryption_key, unsigned char *decryptedtext,
ColumnEncryptionAlgorithm column_encryption_algorithm)
{
if (cipher_text == NULL || cipher_text_length <= 0 || decryptedtext == NULL) {
return 0;
}
/* 校验密文长度 */
if (cipher_text_length < min_ciph_len_in_bytes_with_authen_tag) {
printf("ERROR(CLIENT): The length of cipher_text is invalid, cannot decrypt.\n");
return 0;
}
/* 校验密文中的版本号 */
if (cipher_text[g_auth_tag_size] != '1') {
printf("ERROR(CLIENT): Version byte of cipher_text is invalid, cannot decrypt.\n");
return 0;
}
……
/* 计算MAC标签 */
unsigned char authenticationTag [g_auth_tag_size] = {0};
int HMAC_length = cipher_text_length - g_auth_tag_size;
int res = hmac_sha256(column_encryption_key.get_mac_key(), g_auth_tag_size,
cipher_text + g_auth_tag_size, HMAC_length, authenticationTag);
if (res != 1) {
printf("ERROR(CLIENT): Fail to compute a keyed hash of a given text.\n");
return 0;
}
/* 校验密文是否被篡改 */
int cmp_result = my_memcmp(authenticationTag, cipher_text, g_auth_tag_size);
/* 解密数据 */
int decryptedtext_len = decrypt(cipher_text + cipher_start_index, cipher_value_length,
column_encryption_key.get_encyption_key(), iv, decryptedtext, column_encryption_algorithm);
if (decryptedtext_len < 0) {
return 0;
}
decryptedtext[decryptedtext_len] = '\0';
return decryptedtext_len;
}
9.7 小结
随着信息安全的挑战越来越严重,保护系统安全可靠的运行,守护用户的数据隐私安全成为是当前数据库产品必须具备的能力。openGauss将逐步构建更加完备的安全能力,从身份认证、角色模型、权限管理、审计追踪、数据加解密等多维度来守护系统和数据安全。
本章节详细解析了openGauss的安全架构,并通过关键数据结构和关键函数代码解读描述每一种安全防护机制的实现细节,这些代码实现细节将有助于开发者了解openGauss的安全原理,并基于最新的安全标准来不断地优化和改善安全机制。