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

使用客户端加密保护数据

Client-side encryption(客户端加密)是在本地加密数据,以确保其在传递到 Amazon S3 服务时确保安全。Amazon S3 服务接收您的加密数据,并且不会影响对其进行的加密或解密操作。

要启用客户端加密,您可以选择以下方法:

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

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

注意

Simple Storage Service (Amazon S3) 仅支持对称加密 KMS 密钥,不支持非对称密钥。

AWS Encryption SDK

AWS Encryption SDK 是与特定语言的软件开发工具包分离的客户端加密库。您可以使用此加密库更轻松地在 Amazon S3 中实现加密最佳做法。与特定语言的 AWS 软件开发工具包中的 Amazon S3 加密客户端不同,AWS Encryption SDK 不与 Amazon S3 绑定,并且可用于加密或解密要存储在任何位置的数据。

AWS Encryption SDK 和 Amazon S3 加密客户端不兼容,因为它们生成具有不同数据格式的密文。有关 AWS Encryption SDK 的更多信息,请参阅 AWS Encryption SDK 开发人员指南

AWS 软件开发工具包支持 Amazon S3 客户端加密

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

有关信息以及示例,请参阅 AWS 一般参考中的 AWS 客户端加密开发工具包支持

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

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

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

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

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

    注意

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

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

有关 AWS KMS 的更多信息,请参阅 AWS Key Management Service 开发人员指南中的什么是 AWS Key Management Service?

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

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

AWSKMS kmsClient = AWSKMSClientBuilder.standard() .withRegion(Regions.DEFAULT_REGION) .build(); // create KMS key for for testing this example CreateKeyRequest createKeyRequest = new CreateKeyRequest(); CreateKeyResult createKeyResult = kmsClient.createKey(createKeyRequest); // -- // specify an AWS KMS key 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 KMS key 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 S3AWS 软件开发工具包对客户端加密的支持

注意

首次使用加密 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();