openGauss数据库源码解析 |安全管理源码解析(20)

CREATE COLUMN ENCRYPTION KEY语法相关数据结构:

/*  保存创建列加密密钥的语法信息  */

typedef struct CreateClientLogicColumn {

    NodeTag type;

    List *column_key_name;        /*  列加密密钥名称  */

    List *column_setting_params; /*  列加密密钥参数  */

} CreateClientLogicColumn;



/*  保存列加密密钥参数,保存在CreateClientLogicColumn的column_setting_params中  */

typedef struct ClientLogicColumnParam {

    NodeTag type;

    ClientLogicColumnProperty key;

    char *value;

    unsigned int len;

    List *qualname;

    int location;

} ClientLogicColumnParam;



/*  保存列加密密钥参数的key的枚举类型  */

typedef enum class ClientLogicColumnProperty {

    CLIENT_GLOBAL_SETTING,    /*  加密CEK的CMK  */

    CEK_ALGORITHM,             /*  加密用户数据的算法  */

    CEK_EXPECTED_VALUE,       /*  CEK密钥明文,可选参数  */

    COLUMN_COLUMN_FUNCTION,  /*  默认为encryption  */

} ClientLogicColumnProperty;



CREATE COLUMN ENCRYPTION KEY cek_1 WITH VALUES (CLIENT_MASTER_KEY = cmk_1, ALGORITHM = AEAD_AES_256_CBC_HMAC_SHA256);

上面命令的参数说明为:

(1) CLIENT_MASTER_KEY:指定用于加密CEK的CMK对象。
(2) ALGORITHM:CEK被用于加密用户数据,该参数指定加密用户数据的算法,即指定CEK的密钥类型。
(3) ENCRYPTED_VALUE:列加密密钥的明文,默认随机生成,也可由用户指定,用户指定时密钥长度范围为28256位。

列加密密钥创建语法是通过前端解析器将参数解析成CreateClientLogicColumn结构体后,通过校验指定用于加密CEK的CMK对象是否存在后加载CMK缓存,然后通过“HooksManager::ColumnSettings::pre_create”语句调用加密函数“EncryptionColumnHookExecutor::pre_create”来校验各参数并生成或加密ENCRYPTED_VALUE值,最后在“EncryptionPreProcess::set_new_query”函数中替换ENCRYPTED_VALUE参数为CEK密文,重构SQL查询语句。重构后的SQL语句发送给服务端后服务端解析为CreateClientLogicColumn结构体并检查用户namespace等权限,将CEK的信息保存在系统表中。创建CEK的总体流程如图9-40所示,组织结构如图9-41所示。

 

图9-40  列加密密钥CEK创建流程

 

图9-41  客户端主密钥CMK的组织结构

在对CEK参数进行解析后,使用CMK对ENCRYPTED_VALUE参数进行加密,加密完成后使用加密后的ENCRYPTED_VALUE参数和其他参数对创建CEK的语法进行重构。将输入的查询语句转换成加密查询语句的主要函数入口代码如下:

void EncryptionPreProcess::set_new_query(char **query, size_t query_size, StringArgs string_args, int location,
    int encrypted_value_location, size_t encrypted_value_size, size_t quote_num)
{
for (size_t i = 0; i < string_args.Size(); i++) {
    /*  从string_args中读取键值存到变量中  */
        char string_to_add[MAX_KEY_ADD_LEN];
        errno_t rc = memset_s(string_to_add, MAX_KEY_ADD_LEN, 0, MAX_KEY_ADD_LEN);
        securec_check_c(rc, "\0", "\0");
        size_t total_in = 0;
        if (string_args.at(i) == NULL) {
            continue;
        }
        const char *key = string_args.at(i)->key;
        const char *value = string_args.at(i)->value;
        const size_t vallen = string_args.at(i)->valsize;
        if (!key || !value) {
            Assert(false);
            continue;
        }
        Assert(vallen < MAX_KEY_ADD_LEN);
        /*  将key和value构造成encrypted_value = '密文值'的形式  */
        check_strncat_s(strncat_s(string_to_add, MAX_KEY_ADD_LEN, key, strlen(key)));
        total_in += strlen(key);
        check_strncat_s(strncat_s(string_to_add, MAX_KEY_ADD_LEN, "=\'", strlen("=\'")));
        total_in += strlen("=\'");
        check_strncat_s(strncat_s(string_to_add, MAX_KEY_ADD_LEN, value, vallen));
        total_in += vallen;
        check_strncat_s(strncat_s(string_to_add, MAX_KEY_ADD_LEN, "\'", strlen("\'")));
        total_in += strlen("\'");
        Assert(total_in < MAX_KEY_ADD_LEN);
        /*  encrypted_value_location不为空,则说明用户提供EXPECTED_VALUE,将明文值替换成密文值   */
        if (encrypted_value_location && encrypted_value_size) {
            *query = (char *)libpq_realloc(*query, query_size, query_size + vallen + 1);
            if (*query == NULL) {
                return;
            }
            check_memset_s(memset_s(*query + query_size, vallen + 1, 0, vallen + 1));
            char *replace_dest = *query + encrypted_value_location + strlen("\'");
            char *move_src =
                *query + encrypted_value_location + encrypted_value_size + quote_num + strlen("\'");
            char *move_dest = *query + encrypted_value_location + vallen + strlen("\'");
            check_memmove_s(memmove_s(move_dest,
                query_size - encrypted_value_location - encrypted_value_size - strlen("\'") + 1,
                move_src,
                query_size - encrypted_value_location - encrypted_value_size - strlen("\'")));
            query_size = query_size + vallen - encrypted_value_size;
            check_memcpy_s(memcpy_s(replace_dest, query_size - encrypted_value_location, value, vallen));
        } else { 
/*  EXPECTED_VALUE是随机生成的,则直接插入原先的语句中  */
            check_strcat_s(strcat_s(string_to_add, MAX_KEY_ADD_LEN, ","));
            size_t string_to_add_size = strlen(string_to_add);
            *query = (char *)libpq_realloc(*query, query_size, query_size + string_to_add_size + 1);
            if (*query == NULL) {
                return;
            }
            check_memmove_s(memmove_s(*query + location + string_to_add_size, query_size - location, *query + location,
                query_size - location));
            query_size += string_to_add_size;
            check_memcpy_s(memcpy_s(*query + location, query_size - location, string_to_add, string_to_add_size));
        }
        query[0][query_size] = '\0';
    }
    return;
}

接下来创建加密表。 

CREATE TABLE creditcard_info (id_number int, name text encrypted with (column_encryption_key = cek_1, encryption_type = DETERMINISTIC), gender varchar(10) encrypted with (column_encryption_key = cek_1, encryption_type = DETERMINISTIC), salary float4 encrypted with (column_encryption_key = cek_1, encryption_type = DETERMINISTIC),credit_card varchar(19) encrypted with (column_encryption_key = cek_1, encryption_type = DETERMINISTIC));

 创建加密表的SQL语句在语法解析后进入CreateStmt函数处理逻辑,在run_pre_create_statement函数中,对CreateStmt->tableElts中每个ListCell进行判断,当前加密表仍存在一定的约束,加密表列定义及约束处理函数段代码如下:

bool createStmtProcessor::run_pre_create_statement(const CreateStmt * const stmt, StatementData *statement_data)
{
    …
  /*  加密表列定义及约束处理  */
    foreach (elements, stmt->tableElts) {
        Node *element = (Node *)lfirst(elements);
        switch (nodeTag(element)) {
            case T_ColumnDef: {
                …
               /*  校验distribute by是否符合规格  */
                if (column->colname != NULL &&
                    !check_distributeby(stmt->distributeby, column->colname)) {
                    return false;
                }
               /*  列定义处理,存储加密类型,加密密钥等信息  */
                if (!process_column_defintion(column, element, &expr_vec, &cached_columns, 
                    &cached_columns_for_defaults, statement_data)) {
                    return false;
                }
                break;
            }
            /*  处理check, unique 或其他约束  */
            case T_Constraint: {
                Constraint *constraint = (Constraint*)element;
                if (constraint->keys != NULL) {
                    ListCell *ixcell = NULL;
                    foreach (ixcell, constraint->keys) {
                        char *ikname = strVal(lfirst(ixcell));
                        for (size_t i = 0; i < cached_columns.size(); i++) {
                            if (strcmp((cached_columns.at(i))->get_col_name(), ikname) == 0 && !check_constraint(
                                constraint, cached_columns.at(i)->get_data_type(), ikname, &cached_columns)) {
                                return false;
                            }
                        }
                    }
                } else if (constraint->raw_expr != NULL) {
                    if (!transform_expr(constraint->raw_expr, "", &cached_columns)) {
                        return false;
                    }
                }
                break;
            }
            default:
                break;
        }
    }
     …
    /*  加密约束中需要加密的明文数据  */
    if (!RawValues::get_raw_values_from_consts_vec(&expr_vec, statement_data, 0, &raw_values_list)) {
        return false;
    }
    return ValuesProcessor::process_values(statement_data, &cached_columns_for_defaults, 1,
        &raw_values_list);
}

在将创建加密表的查询语句发送给服务端后,服务端创建成功并返回执行成功的消息。数据加密驱动程序能够实现在数据发送到数据库之前透明地加密数据,数据在整个语句的处理过程中以密文形式存在,在返回结果时,解密返回的数据集,从而保证整个过程对用户是透明、无感知的。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值