使用 Cloud KMS 进行列级加密

您可以使用 Cloud Key Management Service (Cloud KMS) 来加密 BigQuery 表中的值。您可以将 AEAD 加密函数与 Cloud KMS 密钥集封装的密钥集结合使用,以在列级别提供第二层保护。

简介

为了提供额外一层保护,Cloud KMS 使用第二个 KEK(密钥加密密钥)对 DEK(数据加密密钥)进行加密。在 BigQuery 中,引用加密的密钥集而不是明文密钥集有助于降低密钥泄露的风险。KEK 是一个对称加密密钥集,存储在 Cloud KMS 中并使用 Identity and Access Management (IAM) 角色和权限进行管理。

BigQuery 支持确定性和非确定性加密函数。使用确定性加密时,如果存储的数据和其他经过身份验证的数据(可选)相同,则密文相同。这样便可支持基于加密列的聚合和联接。使用非确定性加密时,无论加密数据如何,存储的密文是唯一的,这会阻止聚簇、聚合和联接。

在查询执行时,您需要提供 KEK 的 Cloud KMS 资源路径以及封装的 DEK 中的密文。BigQuery 调用 Cloud KMS 来解封 DEK,然后使用该密钥对查询中的数据进行解密。未封装的 DEK 版本仅在查询期间存储在内存中,然后进行销毁。

如果您在支持 Cloud External Key Manager区域中使用 Cloud KMS,则可以在 Cloud KMS 中使用基于 Cloud EKM 的密钥。

使用场景

使用 Cloud KMS 密钥进行加密的用例包括:

  • 需要存储在 BigQuery 中且不以明文形式存储密钥集的外部加密数据。然后,可以从表导出数据,或使用 SQL 查询解密这些数据。
  • 对 BigQuery 中的加密数据的“双重访问权限控制”。用户必须获得表和加密密钥的权限,才能读取明文数据。
用户权限矩阵
表的权限 表中无权限
密钥的权限 读取和解密加密数据。 无访问权限。
没有密钥权限 读取加密数据。 无访问权限。

如果用户有权访问 KMS 密钥并可以访问封装的密钥集,则 SQL 函数可以解封密钥集并解密密文。用户还可以使用 Cloud KMS REST APICLI 解封密钥集。
以下查询示例使用 KMS SQL 函数来解密非确定性密文:

SELECT
  AEAD.DECRYPT_STRING(
    KEYS.KEYSET_CHAIN(@kms_resource_name, @first_level_keyset),
    ciphertext,
    additional_authenticated_data)
FROM
  ciphertext_table
WHERE
  ...

使用场景示例

假设邮政编码被视为敏感信息的实现。可以使用 AEAD 加密函数将邮政编码数据插入 BigQuery 表,从而加密 Zipcode 列。在此示例中,我们将 AEAD.ENCRYPT 函数与封装的密钥集管理功能结合使用。KEYS.KEYSET_CHAIN 函数使用 KEK 加密数字加密密钥,而 AEAD.ENCRYPT 函数将信息传递给 KMS。

用于加密和解密的密钥集链可确保使用 KEK 加密或封装数据加密密钥 (DEK),并使用该 KEK 传递。封装的 DEK 在 SQL 函数中进行解密或解封装,然后用于加密或解密数据。

AEAD 非确定性函数可以在对表上运行的查询中使用函数来解密数据。

映像

AEAD 确定性函数可以在表上运行的查询中使用函数解密数据,并且支持使用加密数据进行聚合和联接。

图片

非确定性函数语法

使用非确定性函数的受支持语法包括:

AEAD.ENCRYPT(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  plaintext,
  additional_authenticated_data)
AEAD.DECRYPT_STRING(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_authenticated_data)
AEAD.DECRYPT_BYTES(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_authenticated_data)

请参阅 AEAD.DECRYPT_BYTESAEAD.ENCRYPTAEAD.DECRYPT_STRINGKEYS.KEYSET_CHAIN 函数语法。

确定性函数语法

使用确定性函数的受支持语法包括:

DETERMINISTIC_ENCRYPT(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  plaintext,
  additional_data)
DETERMINISTIC_DECRYPT_STRING(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_data)
DETERMINISTIC_DECRYPT_BYTES(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_data)

请参阅 DETERMINISTIC_DECRYPT_BYTESDETERMINISTIC_ENCRYPTDETERMINISTIC_DECRYPT_STRINGKEYS.KEYSET_CHAIN 函数语法。

角色与权限

如需查看 Cloud KMS 角色的列表,请参阅 Cloud KMS 权限和角色

限制

使用 Cloud KMS 进行加密具有以下限制:

  • Cloud KMS 密钥仅限于与查询相同的区域或多区域。出于可靠性方面的原因,不允许使用全局 Cloud KMS 密钥。

  • 无法使用 KEYS.ROTATE_KEYSET 函数轮替封装的密钥集。

  • 诊断查询计划中的用户可以查看 BigQuery 查询中的常量参数。此因素可能会影响 KEYSET_CHAIN 函数的 kms_resource_namefirst_level_keyset 参数。密钥绝不会以纯文本公开,并且需要 Cloud KMS 密钥的权限才能解密封装的密钥集。此方法可以确保除非用户有权解密密钥集,否则密钥不会通过诊断查询计划公开。

  • 基于类型的安全分类时,列级加密具有以下限制:

    • 列级安全性:用户只能解密或加密允许他们访问的列中的数据。

    • 行级安全性:用户只能解密允许他们访问的行中的数据。

  • 与以明文形式发送密钥数据的原始加密函数的性能相比,列级 SQL 函数对性能没有明显影响。

准备工作

如需使用 Cloud KMS 密钥、密钥集、加密表、确定性和非确定性函数,您需要执行以下操作(如果您尚未这样做):

  1. 创建 Google Cloud 项目

  2. 创建 BigQuery 数据集

  3. 创建 Cloud KMS 密钥环

  4. 为具有软件或硬件安全模块 (HSM) 保护级别的已加密列创建 Cloud KMS 密钥

  5. 授予用户使用 Cloud KMS 密钥、加密和解密的权限

请注意以下概念,下一部分中引用了这些概念:

  • PROJECT_ID:Google Cloud 项目的名称。

  • DATASET_NAME:BigQuery 数据集的名称。

  • LOCATION_ID:BigQuery 数据集的位置。

  • TABLE_NAME:BigQuery 表的名称。

  • KEY_RING_ID:Cloud KMS 密钥环的名称。

  • KEY_ID:Cloud KMS 密钥的名称。

  • KMS_KEY:采用以下格式的 Cloud KMS 密钥 (KEK):

    'gcp-kms://projects/PROJECT_ID/locations/LOCATION_ID/keyRings/KEY_RING_ID/cryptoKeys/KEY_ID'
    

    以下是 Cloud KMS 密钥的示例:

    'gcp-kms://projects/myProject/locations/us/keyRings/myKeyRing/cryptoKeys/myKeyName'
    
  • KMS_KEY_SHORT:类似于 KMS_KEY,但采用以下格式:

    projects/PROJECT_ID/locations/LOCATION_ID/keyRings/KEY_RING_ID/cryptoKeys/KEY_ID
    
  • KEYSET_DECODED:以 BYTES 序列形式的解码的密钥集。输出类似于解码的封装密钥集的输出。

    虽然密钥集函数以字节形式返回密钥集,但用户输出以编码字符串的形式显示。如需将编码的密钥集转换为解码的密钥集,请参阅解码 Cloud KMS 密钥集

  • KEYSET_ENCODED:以 STRING 形式的解码的密钥集。输出类似于编码的封装密钥集的输出。

    如需将编码的密钥集转换为解码的密钥集,请参阅解码 Cloud KMS 密钥集

  • WRAPPED_KEYSET_DECODED:以 BYTES 序列形式的解码的封装密钥集。下面是一个此密钥集的输出示例:

    b'\x0a$\x00\xa6\xee\x12Y\x8d|l"\xf7\xfa\xc6\xeafM\xdeefy\xe9\x7f\xf2z\xb3M\
    xf6"\xd0\xe0Le\xa8\x8e\x0fR\xed\x12\xb7\x01\x00\xf0\xa80\xbd\xc1\x07Z\\
    \xd0L<\x80A0\x9ae\xfd(9\x1e\xfa\xc8\x93\xc7\xe8\...'
    

    虽然封装的密钥集函数以字节形式返回封装的密钥集,但用户输出以编码字符串的形式显示。如需将编码的封装密钥集转换为解码的封装密钥集,请参阅解码 Cloud KMS 密钥集

  • WRAPPED_KEYSET_ENCODED:以 STRING 形式的编码的封装密钥集。下面是一个此密钥集的输出示例:

    'CiQApu4SWTozQ7lNwITxpEvGlo5sT2rv1tyuSv3UAMtoTq/lhDwStwEA8KgwvX7CpVVzhWWMkRw
    WZNr3pf8uBIlzHeunCy8ZsQ6CofQYFpiBRBB6k/QqATbiFV+3opnDk/6dBL/S8OO1WoDC+DdD9
    uzEFwqt5D20lTXCkGWFv1...'
    

    如需将编码的封装密钥集转换为解码的封装密钥集,请参阅解码 Cloud KMS 密钥集

密钥管理

以下部分介绍了您可以使用 Cloud KMS 密钥执行的常见任务。

创建密钥集

您可以创建封装的密钥集或原始密钥集。如需创建密钥集,请完成后面小节中的步骤。

创建原始密钥集

运行以下查询以创建具有 DETERMINISTIC_AEAD_AES_SIV_CMAC_256 类型密钥的密钥集。

SELECT KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') AS raw_keyset

创建封装的密钥集

运行以下查询以创建密钥类型为 DETERMINISTIC_AEAD_AES_SIV_CMAC_256 的 Cloud KMS 封装的密钥集。

SELECT KEYS.NEW_WRAPPED_KEYSET(
  KMS_KEY,
  'DETERMINISTIC_AEAD_AES_SIV_CMAC_256')

解码密钥集

虽然返回密钥集的 SQL 函数会生成 BYTES 格式的密钥集,但用户显示的结果以 STRING 格式编码和显示。如果您要将此编码字符串转换为可用作字面量密钥加密函数的解码字节序列,请使用以下查询。

解码封装的密钥集

运行以下查询以解码 Cloud KMS 封装的密钥集。

SELECT FORMAT('%T', FROM_BASE64(WRAPPED_KEYSET_ENCODED'))

解码原始密钥集

请运行以下查询以解码原始密钥集。

SELECT FORMAT('%T', FROM_BASE64(KEYSET_ENCODED'))

重新封装已封装的密钥集

运行以下查询以使用新的 Cloud KMS 密钥重新封装 Cloud KMS 封装的密钥集。KMS_KEY_CURRENT 表示用于加密密钥集的新 KMS_KEYKMS_KEY_NEW 表示用于加密密钥集的新 KMS_KEY

SELECT KEYS.REWRAP_KEYSET(
  KMS_KEY_CURRENT,
  KMS_KEY_NEW,
  WRAPPED_KEYSET_DECODED)

轮替封装的密钥集

运行以下查询以轮替密钥类型为 DETERMINISTIC_AEAD_AES_SIV_CMAC_256 的 Cloud KMS 封装的密钥集。

SELECT KEYS.ROTATE_WRAPPED_KEYSET(
  KMS_KEY,
  WRAPPED_KEYSET_DECODED,
  'DETERMINISTIC_AEAD_AES_SIV_CMAC_256')

从封装的密钥集生成原始密钥集

某些加密函数需要原始密钥集。如需解密 Cloud KMS 封装的密钥集以生成原始密钥集,请完成以下步骤。

  1. 创建封装的密钥集

  2. 在 bp 命令行工具中,输入以下命令以将封装的密钥集保存在名为 keyset_to_unwrap 的文件中,解密其封装的密钥集,然后生成 KEYSET_DECODED 格式的输出:

    echo WRAPPED_KEYSET_ENCODED | base64 -d > /tmp/decoded_wrapped_key
    
    gcloud kms decrypt \
    --ciphertext-file=/tmp/decoded_wrapped_key \
    --key=KMS_KEY_SHORT \
    --plaintext-file=/tmp/keyset_to_unwrap.dec \
    --project=PROJECT_ID
    
    od -An --format=o1 /tmp/keyset_to_unwrap.dec | tr ' ' '\'
    

通过原始密钥集生成封装的密钥集

某些加密函数需要 Cloud KMS 封装的密钥集。如需加密原始密钥集以生成封装的密钥集,请完成以下步骤。

  1. 创建原始密钥集

  2. 在 bp 命令行工具中,输入以下命令以将原始密钥集保存在名为 keyset_to_wrap 的文件中,加密其原始密钥集,然后生成 WRAPPED_KEYSET_DECODED 格式的输出:

    echo KEYSET_ENCODED | base64 -d > /tmp/decoded_key
    
    gcloud kms encrypt \
    --plaintext-file=/tmp/decoded_key \
    --key=KMS_KEY_SHORT \
    --ciphertext-file=/tmp/keyset_to_wrap.dec \
    --project=PROJECT_ID
    
    od -An --format=o1 /tmp/keyset_to_wrap.dec | tr ' ' '\'
    

获取密钥集中的密钥数

运行下面的查询以获取原始密钥集中的密钥数。

  1. 如果您使用的是封装的密钥集,请先生成原始密钥集

  2. 使用原始密钥集运行下面的查询:

    SELECT KEYS.KEYSET_LENGTH(KEYSET_DECODED) as key_count;
    

获取密钥集的 JSON 表示形式

运行以下查询以查看原始密钥集的 JSON 表示形式。

  1. 如果您使用的是封装的密钥集,请先生成原始密钥集

  2. 使用原始密钥集运行下面的查询:

    SELECT KEYS.KEYSET_TO_JSON(KEYSET_DECODED);
    

加密和解密

您可以使用原始密钥集或封装的密钥集来加密表中的列。您还可以选择对列使用确定性加密或非确定性加密。本部分中的示例使用封装的密钥集,但您可以将封装的密钥集替换为原始密钥集。

使用封装的密钥集确定性地加密列

运行以下查询,以创建表并将具有确定性加密的 Cloud KMS 封装的密钥集存储在名为 encrypted_content 的列中。

  1. 创建封装的密钥集

  2. 使用封装的密钥集加密列。

    CREATE OR REPLACE TABLE DATASET_NAME.TABLE_NAME AS
      SELECT DETERMINISTIC_ENCRYPT(
        KEYS.KEYSET_CHAIN(KMS_KEY, WRAPPED_KEYSET_DECODED),
        'plaintext',
        '') AS encrypted_content
    

使用封装的密钥集确定性地解密列

运行以下查询,以使用 Cloud KMS 封装的密钥集确定性地解密包含加密内容的列。此查询假设您要引用包含名为 encrypted_content 的列的表。

SELECT DETERMINISTIC_DECRYPT_STRING(
  KEYS.KEYSET_CHAIN(KMS_KEY, WRAPPED_KEYSET_DECODED),
  encrypted_content,
  '')
FROM DATASET_NAME.TABLE_NAME

使用封装的密钥集非确定性地加密列

请参阅确定使用封装密钥集加密列,但将 DETERMINISTIC_ENCRYPT 替换为 AEAD.ENCRYPT。请确保您的密钥集的类型为 AEAD_AES_GCM_256

使用封装的密钥集非确定性地解密列

请参阅确定使用解密的密钥集解密列,但将 DETERMINISTIC_DECRYPT_STRING 替换为 AEAD.DECRYPT_STRING。请确保您的密钥集的类型为 AEAD_AES_GCM_256

后续步骤