使用客户端加密保护数据 - Amazon Simple Storage Service

使用客户端加密保护数据

客户端加密 是在将数据发送到 Amazon S3 之前加密数据的行为。要启用客户端加密,您可以选择以下方法:

  • 使用 AWS Key Management Service (AWS KMS) 中存储的客户主密钥 (CMK)。

  • 使用在应用程序中存储的主密钥。

以下 AWS 开发工具包支持客户端加密:

选项 1:使用 AWS KMS 中存储的 CMK

利用此选项,您可以在 Amazon S3 中上传或下载数据时使用 AWS KMS CMK 执行客户端加密。

  • 在上传对象时 — 通过使用 CMK ID,客户端先向 AWS KMS 发送请求以获取可用于加密对象数据的 CMK。AWS KMS 返回两个随机生成的数据密钥版本:

    • 客户端用于加密对象数据的数据密钥的纯文本版本。

    • 客户端将作为对象元数据上传到 Amazon S3 的同一数据密钥的密码 blob。

    注意

    客户端将为其上传的每个对象获取一个唯一的数据密钥。

  • 下载对象时 — 客户端首先从 Amazon S3 下载加密的对象以及作为对象元数据存储的数据密钥的密码 blob 版本。然后,客户端将密码 blob 发送到 AWS KMS 以获取密钥的纯文本版本,以便让客户端解密对象数据。

有关 AWS KMS 的更多信息,请参阅AWS Key Management Service Developer Guide中的什么是 AWS Key Management Service?

以下代码示例演示如何通过将 AWS KMS 与AWS SDK for Java结合使用来将对象上传到 Amazon S3。该示例使用 AWS 托管 CMK 对客户端上的数据进行加密,然后再在将其上传到 Amazon S3 中。如果您已经有一个 CMK,则可通过在示例代码中指定 keyId 变量的值来使用该 CMK。如果没有 CMK,或需要另一个 CMK,则可以通过 Java API 生成一个。示例代码会自动生成要使用的 CMK。

有关创建和测试有效示例的说明,请参阅测试 Amazon S3 Java 代码示例

AWSKMS kmsClient = AWSKMSClientBuilder.standard() .withRegion(Regions.DEFAULT_REGION) .build(); // create CMK for for testing this example CreateKeyRequest createKeyRequest = new CreateKeyRequest(); CreateKeyResult createKeyResult = kmsClient.createKey(createKeyRequest); // -- // specify an Amazon KMS customer master key (CMK) ID String keyId = createKeyResult.getKeyMetadata().getKeyId(); String s3ObjectKey = "EncryptedContent1.txt"; String s3ObjectContent = "This is the 1st content to encrypt"; // -- AmazonS3EncryptionV2 s3Encryption = AmazonS3EncryptionClientV2Builder.standard() .withRegion(Regions.US_WEST_2) .withCryptoConfiguration(new CryptoConfigurationV2().withCryptoMode(CryptoMode.StrictAuthenticatedEncryption)) .withEncryptionMaterialsProvider(new KMSEncryptionMaterialsProvider(keyId)) .build(); s3Encryption.putObject(bucket_name, s3ObjectKey, s3ObjectContent); System.out.println(s3Encryption.getObjectAsString(bucket_name, s3ObjectKey)); // schedule deletion of CMK generated for testing ScheduleKeyDeletionRequest scheduleKeyDeletionRequest = new ScheduleKeyDeletionRequest().withKeyId(keyId).withPendingWindowInDays(7); kmsClient.scheduleKeyDeletion(scheduleKeyDeletionRequest); s3Encryption.shutdown(); kmsClient.shutdown();

选项 2:使用在应用程序中存储的主密钥

利用此选项,您可以使用存储在应用程序中的主密钥执行客户端数据加密。

重要

客户端主密钥和未加密的数据从来不会发送到 AWS。务必安全地管理加密密钥。如果您丢失了加密密钥,您就不能解密数据了。

以下是具体工作原理:

  • 上传对象时 — 将客户端主密钥提供给 Amazon S3 加密客户端。该客户端仅使用该主密钥来加密客户端随机生成的数据加密密钥。

    以下步骤描述了此过程:

    1. Amazon S3 加密客户端在本地生成一个一次性对称密钥(也称为数据加密密钥数据密钥)。它使用数据密钥加密单个 Amazon S3 对象的数据。该客户端将为每个对象生成一个单独的数据密钥。

    2. 该客户端使用您提供的主密钥来加密数据加密密钥。客户端会将加密的数据密钥及其材料说明作为对象元数据的一部分上传。该客户端利用材料描述来确定要用于解密的客户端主密钥。

    3. 该客户端将加密数据上传到 Amazon S3 并在 Amazon S3 中将加密数据密钥保存为对象元数据 (x-amz-meta-x-amz-key)。

  • 下载对象时 — 该客户端从 Amazon S3 下载加密的对象。通过使用对象元数据中的材料说明,该客户端将确定要用于解密数据密钥的主密钥。该客户端将使用该主密钥解密数据密钥,然后使用该数据密钥对对象进行解密。

您提供的客户端主密钥可以是对称密钥,也可以是公有/私有密钥对。以下代码示例说明如何使用每种类型的密钥。

有关更多信息,请参阅使用AWS SDK for Java和 Amazon S3 进行的客户端数据加密

注意

首次使用加密 API 时,如果您收到密码加密错误消息,则您的 JDK 版本可能带有一个 Java Cryptography Extension (JCE) 区域策略文件,该文件将加密和解密转换的最大密钥长度限制为 128 位。AWS 开发工具包要求的最大密钥长度为 256 位。

要检查您的最大密钥长度,请使用 getMaxAllowedKeyLength() 类的 javax.crypto.Cipher 方法。要取消密钥长度限制,请安装 Java Cryptography Extension (JCE) 无限强度管辖权策略文件

以下代码示例演示如何执行这些任务:

  • 生成 256 位 AES 密钥。

  • 将数据发送到 Amazon S3 之前在客户端上使用 AES 密钥加密数据。

  • 使用 AES 密钥解密从 Amazon S3 接收的数据。

  • 输出已解密对象的字符串表示形式。

有关创建和测试有效示例的说明,请参阅测试 Amazon S3 Java 代码示例

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(256); // -- // generate a symmetric encryption key for testing SecretKey secretKey = keyGenerator.generateKey(); String s3ObjectKey = "EncryptedContent2.txt"; String s3ObjectContent = "This is the 2nd content to encrypt"; // -- AmazonS3EncryptionV2 s3Encryption = AmazonS3EncryptionClientV2Builder.standard() .withRegion(Regions.DEFAULT_REGION) .withClientConfiguration(new ClientConfiguration()) .withCryptoConfiguration(new CryptoConfigurationV2().withCryptoMode(CryptoMode.AuthenticatedEncryption)) .withEncryptionMaterialsProvider(new StaticEncryptionMaterialsProvider(new EncryptionMaterials(secretKey))) .build(); s3Encryption.putObject(bucket_name, s3ObjectKey, s3ObjectContent); System.out.println(s3Encryption.getObjectAsString(bucket_name, s3ObjectKey)); s3Encryption.shutdown();

以下代码示例演示如何执行这些任务:

  • 生成 2048 位 RSA 密钥对以便进行测试。

  • 将数据发送到 Amazon S3 之前在客户端上使用 RSA 密钥加密数据。

  • 使用 RSA 密钥解密从 Amazon S3 接收的数据。

  • 输出已解密对象的字符串表示形式。

有关创建和测试有效示例的说明,请参阅测试 Amazon S3 Java 代码示例

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); // -- // generate an asymmetric key pair for testing KeyPair keyPair = keyPairGenerator.generateKeyPair(); String s3ObjectKey = "EncryptedContent3.txt"; String s3ObjectContent = "This is the 3rd content to encrypt"; // -- AmazonS3EncryptionV2 s3Encryption = AmazonS3EncryptionClientV2Builder.standard() .withRegion(Regions.US_WEST_2) .withCryptoConfiguration(new CryptoConfigurationV2().withCryptoMode(CryptoMode.StrictAuthenticatedEncryption)) .withEncryptionMaterialsProvider(new StaticEncryptionMaterialsProvider(new EncryptionMaterials(keyPair))) .build(); s3Encryption.putObject(bucket_name, s3ObjectKey, s3ObjectContent); System.out.println(s3Encryption.getObjectAsString(bucket_name, s3ObjectKey)); s3Encryption.shutdown();