使用适用于 Java 的 DynamoDB 加密客户端 - AWS 数据库加密 SDK

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用适用于 Java 的 DynamoDB 加密客户端

注意

我们的客户端加密库已重命名为 AWS 数据库加密 SDK。以下主题提供有关适用于 Java 的 DynamoDB 加密客户端版本 1.x—2.x 以及适用于 Python 的 DynamoDB 加密客户端版本 1.x—3.x 的信息。有关更多信息,请参阅适用于 DynamoDB 的AWS 数据库加密 SDK 版本支持

本主题介绍了 Java 中的 DynamoDB 加密客户端的可能在其他编程语言实施中找不到的一些功能。

有关使用 DynamoDB 加密客户端进行编程的详细信息,请参阅 Java 示例、 GitHub上面的示例以及 DynamoDB 加密客户端的 Javadoc。aws-dynamodb-encryption-java repository

物品加密器: AttributeEncryptor 和 DynamodBenCryptor

Java 中的 DynamoDB 加密客户端有两个项目加密器:较低级别的 DynamodBenCryptor 和。AttributeEncryptor

AttributeEncryptor是一个帮助程序类,可帮助您在 DynamoDB 加密客户端中使用 DynamoDBMapper 和 AWS SDK for Java 。DynamoDB Encryptor如果您结合使用 AttributeEncryptorDynamoDBMapper,则当您保存项目时,它会透明对项目进行加密并签名。当您加载项目时,它还会透明地验证和解密您的项目。

配置保存行为

您可以使用 AttributeEncryptorDynamoDBMapper 来添加或替换具有仅已签名(或已加密和签名)的属性的表项目。对于这些任务,我们建议您将其配置为使用 PUT 保存行为,如以下示例所示。否则,您可能无法解密您的数据。

DynamoDBMapperConfig mapperConfig = DynamoDBMapperConfig.builder().withSaveBehavior(SaveBehavior.PUT).build(); DynamoDBMapper mapper = new DynamoDBMapper(ddb, mapperConfig, new AttributeEncryptor(encryptor));

如果您使用默认保存行为(仅更新在表项目中建模的属性),则未建模的属性将不会包含在签名中,且不会由表写入更改。因此,在以后读取所有的属性时,签名将无法验证,因为它不包括未建模的属性。

您也可以使用 CLOBBER 保存行为。该行为与 PUT 保存行为相同,只不过它将禁用乐观锁并覆盖表中的项目。

为防止签名错误,如果 AttributeEncryptor 与未配置 CLOBBERPUT 保存行为的 DynamoDBMapper 一起使用,则 DynamoDB 加密客户端会抛出运行时系统异常。

要查看示例中使用的此代码,请参阅使用 DynamoDBMapper和中aws-dynamodb-encryption-java存储库中的 AwsKmsEncryptedObject GitHub.java 示例。

Java 中的属性操作

属性操作确定加密并签名的属性值、仅签名的属性值以及忽略的属性值。您用于指定属性操作的方法取决于您使用的是 DynamoDBMapperAttributeEncryptor,还是低级别 DynamoDBEncryptor

重要

使用属性操作对表项进行加密后,在数据模型中添加或删除属性可能会导致签名验证错误,从而使您无法解密数据。有关详细说明,请参阅更改数据模型

当您使用 DynamoDBMapperAttributeEncryptor 时,使用注释指定属性操作。DynamoDB 加密客户端使用标准 DynamoDB 属性注释,该注释可定义属性类型以确定如何保护属性。默认情况下,除主键以外的所有属性均加密并签名,主键已签名但未加密。

注意

不要使用 @DynamoDB VersionAttribute 注解对属性的值进行加密,尽管你可以(也应该)对它们进行签名。否则,使用其值的情况将产生意外后果。

// Attributes are encrypted and signed @DynamoDBAttribute(attributeName="Description") // Partition keys are signed but not encrypted @DynamoDBHashKey(attributeName="Title") // Sort keys are signed but not encrypted @DynamoDBRangeKey(attributeName="Author")

要指定例外情况,请使用在适用于 Java 的 DynamoDB 加密客户端中定义的加密注释。如果您在类级别指定这些注释,它们将成为该类的默认值。

// Sign only @DoNotEncrypt // Do nothing; not encrypted or signed @DoNotTouch

例如,这些注释签署但未加密 PublicationYear 属性,而且未加密或签署 ISBN 属性值。

// Sign only (override the default) @DoNotEncrypt @DynamoDBAttribute(attributeName="PublicationYear") // Do nothing (override the default) @DoNotTouch @DynamoDBAttribute(attributeName="ISBN")

要在您直接使用 DynamoDBEncryptor 时指定属性操作,请创建一个 HashMap 对象,在该对象中,名称/值对表示属性名称和指定的操作。

有效值适用于在 EncryptionFlags 枚举类型中定义的属性操作。您可以结合使用 ENCRYPTSIGN,单独使用 SIGN,或同时忽略。但是,如果您单独使用 ENCRYPT,则 DynamoDB 加密客户端会抛出错误。您无法加密未签名的属性。

ENCRYPT SIGN
警告

请勿加密主键属性。它们必须保留为明文,以便 DynamoDB 查找项目而无需运行全表扫描。

如果您在加密上下文中指定一个主键,然后为主键属性的属性操作指定 ENCRYPT,则 DynamoDB 加密客户端会抛出异常。

例如,以下 Java 代码创建了一个actions HashMap 对record项目中的所有属性进行加密和签名。例外是分区键和排序键属性(这些属性已签名但未加密)以及 test 属性(该属性未签名或未加密)。

final EnumSet<EncryptionFlags> signOnly = EnumSet.of(EncryptionFlags.SIGN); final EnumSet<EncryptionFlags> encryptAndSign = EnumSet.of(EncryptionFlags.ENCRYPT, EncryptionFlags.SIGN); final Map<String, Set<EncryptionFlags>> actions = new HashMap<>(); for (final String attributeName : record.keySet()) { switch (attributeName) { case partitionKeyName: // no break; falls through to next case case sortKeyName: // Partition and sort keys must not be encrypted, but should be signed actions.put(attributeName, signOnly); break; case "test": // Don't encrypt or sign break; default: // Encrypt and sign everything else actions.put(attributeName, encryptAndSign); break; } }

然后,当您调用 DynamoDBEncryptorencryptRecord 方法时,将映射指定为 attributeFlags 参数的值。例如,这个对 encryptRecord 的调用使用 actions 映射。

// Encrypt the plaintext record final Map<String, AttributeValue> encrypted_record = encryptor.encryptRecord(record, actions, encryptionContext);

覆盖表名称

在 DynamoDB 加密客户端中,DynamoDB 表的名称是传递到加密和解密方法的 DynamoDB 加密上下文的元素。对表项目进行加密或签名时,DynamoDB 加密上下文(包括表名称)以加密方式绑定到加密文字。如果传递给解密方法的 DynamoDB 加密上下文与传递给加密方法的 DynamoDB 加密上下文不匹配,则解密操作将失败。

有时,表的名称会发生变化,例如备份表或执行point-in-time 恢复时。解密或验证这些项目的签名时,必须传递用于对项目进行加密和签名的相同 DynamoDB 加密上下文,包括原始表名称。不需要当前表名称。

使用 DynamoDBEncryptor 时,您将手动汇编 DynamoDB 加密上下文。但是,如果使用 DynamoDBMapperAttributeEncryptor 会为您创建 DynamoDB 加密上下文,包括当前表名称。要告知 AttributeEncryptor 使用其他表名称创建加密上下文,请使用 EncryptionContextOverrideOperator

例如,以下代码创建加密材料提供程序 (CMP) 和 DynamoDBEncryptor 的实例。然后,它调用 DynamoDBEncryptorsetEncryptionContextOverrideOperator 方法。它使用 overrideEncryptionContextTableName 运算符,该运算符将覆盖一个表名称。通过这种方式配置它后,AttributeEncryptor 会创建一个 DynamoDB 加密上下文,其中包含 newTableName 以代替 oldTableName。有关完整的示例,请参阅 EncryptionContextOverridesWithDynamoDBMapper.java

final DirectKmsMaterialProvider cmp = new DirectKmsMaterialProvider(kms, keyArn); final DynamoDBEncryptor encryptor = DynamoDBEncryptor.getInstance(cmp); encryptor.setEncryptionContextOverrideOperator(EncryptionContextOperators.overrideEncryptionContextTableName( oldTableName, newTableName));

当您调用 DynamoDBMapper 的加载方法(该方法解密并验证项目)时,您指定原始表名称。

mapper.load(itemClass, DynamoDBMapperConfig.builder() .withTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNameReplacement(oldTableName)) .build());

您还可以使用 overrideEncryptionContextTableNameUsingMap 运算符,该运算符将覆盖多个表名称。

表名称覆盖运算符通常在解密数据和验证签名时使用。但是,您可以使用它们在加密和签名时将 DynamoDB 加密上下文中的表名称设置为其他值。

如果使用 DynamoDBEncryptor,请不要使用表名称覆盖运算符。而是使用原始表名称创建一个加密上下文,并将其提交给解密方法。