使用 SQL 宏封装加密业务逻辑

VoltageSecureProtectVoltageSecureAccess 函数的级别非常低,我们需要做足准备才能正确使用。这包括但不限于了解有关正在解密/加密的数据类型、要使用的 Voltage 格式、将输入值转换为 VARCHAR 然后恢复为所需的数据类型以及身份管理的信息。使用这么多动态组件编写查询可能很乏味。

为了简化此过程,您可以将此信息封装在 SQL 宏中,并使用 case 表达式以更动态的方式决定其行为。此方法有以下优势:

  • 您可以将 Voltage 格式与数据的用途相关联,Vertica 将相应地自动加密和解密该值。

  • 您可以自动执行所需的类型转换,从所需的数据类型转换为 VARCHAR。

  • THROW_ERROR 函数可以为宏提供一种输入验证形式。

  • 您可以在加密期间为给定的 case 表达式指定 身份,以便将解密权限限制为相同的身份。

要查看您的宏,请查询 USER_FUNCTIONS 系统表。

加密和解密宏模板

在以下宏中,data_purpose 参数描述了数据的用途(例如 temperature),value 参数指示了正在加密的值及其 data_type(例如 INT)。data_type 在整个宏中必须相同。

data_purpose 参数还与 voltage_formatvoltage_identity 参数相关联,后两个参数分别用于控制数据的加密/解密方式以及可以访问数据的身份。

如果将不具有相应 case 表达式的 data_purpose 传递给函数,则 THROW_ERROR 函数将返回用户指定的错误,该错误本身必须转换为函数的返回 data_type

请注意,如果遇到与指定转换不兼容的类型,标准转换运算符 :: 将完全终止查询,这在转换较大数据集时可能会出现问题。为防止查询终止,您可以使用 ::! 运算符首先尝试指定的转换,对于不兼容的类型则返回 NULL 值。有关详细信息,请参阅转换失败

使用 VoltageSecureProtect 的加密宏

=> CREATE FUNCTION encryptDataType(data_purpose VARCHAR, value data_type) RETURN data_type
    AS BEGIN
    RETURN(CASE
        WHEN (data_purpose='data_purpose')
            then VoltageSecureProtect(value::VARCHAR USING PARAMETERS
                format='voltage_format',
                identity='voltage_identity')::data_type
        ELSE
            THROW_ERROR('no matching data_purpose')::data_type
        END);
    END;

使用 VoltageSecureAccess 的解密宏

=> CREATE FUNCTION decryptDataType(data_purpose VARCHAR, value data_type) RETURN data_type
    AS BEGIN
    RETURN(CASE
        WHEN (data_purpose='data_purpose')
            then VoltageSecureAccess(value::VARCHAR USING PARAMETERS
                format='voltage_format',
                identity='voltage_identity')::data_type
        ELSE
            THROW_ERROR('no matching data_purpose')::data_type
        END);
    END;

示例

以下示例显示如何管理含出生日期的列的加密和解密。在这种情况下,您可能需要定义一个单独的身份来对客户和员工数据进行加密。

=> SELECT * FROM customer_dob;
   dates
-----------
 1955-11-04
 1991-12-01
 1977-07-07
(3 rows)

对于加密,定义以下宏。

=> CREATE FUNCTION encryptDOB(data_purpose VARCHAR, value DATE) RETURN DATE
    AS BEGIN
    RETURN
        (CASE
            --The data_purpose parameter controls which identity is used during encryption;
            WHEN (data_purpose='customer')
            --Format, identities, and casting from DATE to VARCHAR and back to DATE are all encapsulated in the case expression;
                then VoltageSecureProtect(value::VARCHAR USING PARAMETERS
                    format='birthday',
                    identity='customer_data@example.com')::DATE
            WHEN (data_purpose='employee')
                then VoltageSecureProtect(value::VARCHAR USING PARAMETERS
                    format='birthday',
                    identity='employee_data@example.com')::DATE
            ELSE
                THROW_ERROR('Unsupported data_purpose -- You must pass ''customer'' when
                encrypting customer data or ''employee'' when encrypting employee data')::DATE
                --Because the return type of this macro is DATE, THROW_ERROR must also be casted to type DATE;
        END);
    END;

对 customer_dob 表中的日期列进行加密:

=> SELECT encryptDOB('customer', dates) FROM customer_dob;
 encryptDOB
------------
 2048-08-09
 1917-03-05
 2022-01-07

单独对某个值进行加密:

=> SELECT encryptDOB('customer', '1955-11-04');
 encryptDOB
------------
 2048-08-09

您可以为解密定义一个匹配的宏。由于加密数据只能使用匹配的身份进行解密,因此这些 case 表达式使用相同的 data_purpose 和身份进行解密。

对于解密,定义以下宏:

=> CREATE FUNCTION decryptDOB(data_purpose VARCHAR, value DATE) RETURN DATE
    AS BEGIN
    RETURN
        (CASE
            --The case expressions and parameters must match the ones for encryption;
            WHEN (data_purpose='customer')
                then VoltageSecureAccess(value::VARCHAR USING PARAMETERS
                    format='birthday',
                    identity='customer_data@example.com')::DATE
            WHEN (data_purpose='employee')
                then VoltageSecureAccess(value::VARCHAR USING PARAMETERS
                    format='birthday',
                    identity='employee_data@example.com')::DATE
            ELSE
                THROW_ERROR('Unsupported data_purpose -- You must pass ''customer'' when
                decrypting customer data or ''employee'' when decrypting employee data')::DATE
        END);
    END;

对嵌套调用中的加密列进行解密:

=> SELECT decryptDOB('customer', encryptDOB('customer', dates)) FROM customer_dob;
 decryptDOB
------------
 1955-11-04
 1991-12-01
 1977-07-07
(3 rows)

单独对某个值进行解密:

=> SELECT decryptDOB('customer', '2048-08-09');
 decryptDOB
------------
 1955-11-04

删除这些宏:

=> DROP FUNCTION encryptDOB(VARCHAR, DATE);
DROP FUNCTION
=> DROP FUNCTION decryptDOB(VARCHAR, DATE);
DROP FUNCTION