

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

# 适用于 PHP 的 Amazon SDK 版本 3 中的 Amazon S3 加密客户端迁移（从 V2 到 V3）
<a name="s3-encryption-migration-v2-v3"></a>

**注意**  
如果您使用的是 Amazon S3 加密客户端的版本 1 (V1)，则必须先迁移到版本 2 (V2)，然后才能迁移到版本 3 (V3)。请参阅[版本 3 中的 Amazon S3 加密客户端迁移（从 V1 到 V2） 适用于 PHP 的 Amazon SDK](s3-encryption-migration-v1-v2.md)。

本主题介绍如何将您的应用程序从亚马逊简单存储服务 (Amazon S3) Simple Service 加密客户端的版本 2 (V2) 迁移到版本 3 (V3)，并确保应用程序在整个迁移过程中的可用性。版本 3 引入了 AES GCM 以及密钥承诺和承诺策略，以增强安全性并防止数据密钥被篡改。

## 迁移概述
<a name="s3-encryption-migration-v2-v3-overview"></a>

此迁移分为两个阶段：

1. **更新现有客户端以读取新格式。**首先，将 适用于 PHP 的 Amazon SDK 的已更新版本部署到应用程序中。这允许现有的 V2 加密客户端解密新 V3 客户端写入的对象。如果您的应用程序使用多个 SDK Amazon SDKs，则必须单独升级每个 SDK。

2. **将加密和解密客户端迁移到 V3。**一旦您的所有 V2 加密客户端都能读取新格式，您就可以将现有的加密和解密客户端迁移到各自的 V3 版本。

## 理解 V3 的概念
<a name="s3-encryption-migration-v2-v3-concepts"></a>

Amazon S3 加密客户端第 3 版引入了两项关键安全增强功能：承诺策略和带密钥承诺算法的 AES GCM。了解这些概念对于成功迁移至关重要。

### 承诺政策
<a name="s3-encryption-migration-v2-v3-concepts-commitment-policy"></a>

承诺策略控制加密客户端在加密和解密操作期间如何处理密钥承诺。版本 3 提供了三个策略选项：

`FORBID_ENCRYPT_ALLOW_DECRYPT`  
**加密行为：**无需密钥承诺即可加密对象。  
**解密行为：**允许对使用或不使用密钥承诺进行加密的对象进行解密。  
**安全影响：**此策略不对新加密的对象强制执行密钥承诺，这可能会允许篡改数据密钥。仅在需要与 V2 客户端保持兼容性的初始迁移阶段使用此策略。  
**版本兼容性：**所有 V2 和 V3 实现均可读取使用此策略加密的对象。

`REQUIRE_ENCRYPT_ALLOW_DECRYPT`  
**加密行为：**使用`ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY`算法对具有密钥承诺的对象进行加密。  
**解密行为：**允许对使用或不使用密钥承诺进行加密的对象进行解密。  
**安全影响：**此策略为新加密的对象提供了增强的安全性，同时保持了读取现有对象的能力。这是大多数迁移场景的推荐策略。  
**版本兼容性：**使用此策略加密的对象只能由 V3 和最新的 V2 实现读取。  
**迁移注意事项：**在使用此策略之前，请确保所有需要读取加密对象的客户端都已升级到 V3 或最新的 V2。

`REQUIRE_ENCRYPT_REQUIRE_DECRYPT`  
**加密行为：**使用`ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY`算法对具有密钥承诺的对象进行加密。  
**解密行为：**仅允许解密使用密钥承诺加密的对象。未经密钥承诺加密的对象将无法解密。  
**安全影响：**该策略通过强制加密和解密密钥承诺来提供最高级别的安全性。只有在所有对象都已迁移后才使用此策略以使用密钥承诺。  
**版本兼容性：**只有 V3 实现才能使用此政策。尝试使用此策略解密 V1 或 V2 加密对象将失败。  
**迁移注意事项：**只有在完成完整迁移并使用密钥承诺重新加密所有现有对象之后，才应使用此策略。

### 带有关键承诺的 AES GCM
<a name="s3-encryption-migration-v2-v3-concepts-aes-gcm-kc"></a>

带密钥承诺的 AES GCM (`ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY`) 算法是 V3 中引入的一种新的加密算法，可防止数据密钥篡改攻击。

**安全性增强：**通过加密方式将数据密钥绑定到加密内容，`ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY`防止数据密钥被篡改。这可以防止攻击者在解密过程中替换不同的数据密钥，这可能会导致意外数据被解密。

**版本兼容性：**使用加密的对象`ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY`只能通过 V3 和 Amazon S3 加密客户端的最新 V2 实现进行解密。V1 客户端无法解密使用此算法加密的对象。

**重要**  
**升级要求：**在启用加密之前`ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY`（使用 REQUIRE\_ENCRYPT\_ALLOW\_DECRYPT 或 REQUIRE\_ENCRYPT 策略），必须确保所有需要读取加密对象的客户端都已升级到 V3。未能升级所有读取器将导致使用密钥承诺加密的对象解密失败。

## 更新现有客户端以读取新格式
<a name="s3-encryption-migration-v2-v3-update-clients"></a>

V3 加密客户端使用旧版本的客户端不支持的加密算法和密钥承诺功能。迁移的第一步是将 V2 解密客户端更新到最新的 SDK 版本。完成此步骤后，您的应用程序的 V2 客户端将能够解密由 V3 加密客户端加密的对象。有关每种安装方法的详细信息，请参阅下文 适用于 PHP 的 Amazon SDK。

### 构建并安装最新的 SDK 版本
<a name="s3-encryption-migration-v2-v3-update-clients-sdk"></a>

要完成此迁移，您必须使用`aws/aws-sdk-php`包含 V3 加密客户端支持的最新版本的软件包。

 **从 Composer 安装** 

对于使用 Composer 安装的项目，请在 Composer 文件中将 SDK 包更新到最新版本的 SDK，然后运行以下命令。

```
composer update aws/aws-sdk-php
```

 **使用 Phar 或 Zip 文件进行安装** 

使用以下方法之一。请务必将已更新的 SDK 文件放在代码所需位置，该位置由 require 语句确定。

对于使用 Phar 文件来安装的项目，请下载已更新的文件：[https://docs.amazonaws.cn/aws-sdk-php/v3/download/aws.phar](https://docs.amazonaws.cn/aws-sdk-php/v3/download/aws.phar)。

```
<?php
  require '/path/to/aws.phar';
?>
```

对于使用 Zip 文件来安装的项目，请下载已更新的文件：[https://docs.amazonaws.cn/aws-sdk-php/v3/download/aws.zip](https://docs.amazonaws.cn/aws-sdk-php/v3/download/aws.zip)。

```
<?php
  require '/path/to/aws-autoloader.php';
?>
```

### 构建、安装和部署应用程序
<a name="s3-encryption-migration-v2-v3-update-clients-deploy"></a>

更新 SDK 后，重新构建并重新部署您的应用程序，以确保所有组件都使用更新的版本。此步骤对于确保您的 V2 客户端可以读取由 V3 客户端加密的对象至关重要。

按照贵组织的标准部署程序推出更新的应用程序。在继续将加密和解密客户端迁移到 V3 之前，请确保应用程序的所有实例都已更新。

部署后，请验证您的应用程序是否仍然可以解密现有对象，并且在正常操作期间没有出现错误。这可以确认 SDK 更新已成功并且您的应用程序已准备好进入下一阶段的迁移。

## 将加密和解密客户端迁移到 V3
<a name="s3-encryption-migration-v2-v3-migrate"></a>

更新客户端以读取新的加密格式后，您可以将应用程序更新为 V3 加密和解密客户端。以下示例向您展示了如何成功地将代码从 V2 迁移到 V3。

### 使用 V3 加密客户端
<a name="s3-encryption-migration-v2-v3-using-v3-clients"></a>

V3 引入了该`S3EncryptionClientV3`类并`KmsMaterialsProviderV3`取代了 V2 的等效项。V3 的主要区别在于：
+ V3 使用`KmsMaterialsProviderV3`（与 V2 相同），但在解密调用中的对象时会验证加密上下文。`GetObject`
+ V3 引入了承诺策略来控制加密和解密行为。

 **示例：使用 KMS 加密从 V2 迁移到 V3** 

 *迁移前 (V2)* 

```
use Aws\S3\Crypto\S3EncryptionClientV2;
use Aws\S3\S3Client;
use Aws\Crypto\KmsMaterialsProviderV2;
use Aws\Kms\KmsClient;

$encryptionClient = new S3EncryptionClientV2(
   new S3Client([
      'profile' => 'default',
      'region' => 'us-east-1',
      'version' => 'latest',
   ])
);

$kmsKeyId = 'kms-key-id';
$materialsProvider = new KmsMaterialsProviderV2(
   new KmsClient([
      'profile' => 'default',
      'region' => 'us-east-1',
      'version' => 'latest',
   ]),
   $kmsKeyId
);

$bucket = 'the-bucket-name';
$key = 'the-file-name';
$cipherOptions = [
   'Cipher' => 'gcm',
   'KeySize' => 256,
];

$encryptionClient->putObject([
   '@MaterialsProvider' => $materialsProvider,
   '@CipherOptions' => $cipherOptions,
   '@KmsEncryptionContext' => ['context-key' => 'context-value'],
   'Bucket' => $bucket,
   'Key' => $key,
   'Body' => fopen('file-to-encrypt.txt', 'r'),
]);

$result = $encryptionClient->getObject([
   '@KmsAllowDecryptWithAnyCmk' => true,
   '@SecurityProfile' => 'V2_AND_LEGACY',
   '@CommitmentPolicy' => 'FORBID_ENCRYPT_ALLOW_DECRYPT',
   '@MaterialsProvider' => $materialsProvider,
   '@CipherOptions' => $cipherOptions,
   'Bucket' => $bucket,
   'Key' => $key,
]);
```

 *迁移期间（具有向后兼容性的 V3）* 

```
use Aws\S3\Crypto\S3EncryptionClientV3;
use Aws\S3\S3Client;
use Aws\Crypto\KmsMaterialsProviderV3;
use Aws\Kms\KmsClient;

// Create V3 encryption client
$encryptionClient = new S3EncryptionClientV3(
   new S3Client([
      'profile' => 'default',
      'region' => 'us-east-1',
      'version' => 'latest',
   ])
);

// Create encryption materials
$kmsKeyId = 'kms-key-id';
$materialsProvider = new KmsMaterialsProviderV3(
   new KmsClient([
      'profile' => 'default',
      'region' => 'us-east-1',
      'version' => 'latest',
   ]),
   $kmsKeyId
);

$bucket = 'the-bucket-name';
$key = 'the-file-name';
$cipherOptions = [
   'Cipher' => 'gcm',
   'KeySize' => 256,
];

$encryptionClient->putObject([
   '@MaterialsProvider' => $materialsProvider,
   '@CipherOptions' => $cipherOptions,
   '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_ALLOW_DECRYPT',
   '@KmsEncryptionContext' => ['context-key' => 'context-value'],
   'Bucket' => $bucket,
   'Key' => $key,
   'Body' => fopen('file-to-encrypt.txt', 'r'),
]);

$result = $encryptionClient->getObject([
   '@SecurityProfile' => 'V3_AND_LEGACY',
   '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_ALLOW_DECRYPT',
   '@MaterialsProvider' => $materialsProvider,
   '@CipherOptions' => $cipherOptions,
   'Bucket' => $bucket,
   'Key' => $key,
]);
```

 *迁移后（带有关键承诺的 V3）* 

```
use Aws\S3\Crypto\S3EncryptionClientV3;
use Aws\S3\S3Client;
use Aws\Crypto\KmsMaterialsProviderV3;
use Aws\Kms\KmsClient;
 
// Create V3 encryption client
$encryptionClient = new S3EncryptionClientV3(
   new S3Client([
      'profile' => 'default',
      'region' => 'us-east-1',
      'version' => 'latest',
   ])
);

// Create encryption materials
$kmsKeyId = 'kms-key-id';
$materialsProvider = new KmsMaterialsProviderV3(
   new KmsClient([
      'profile' => 'default',
      'region' => 'us-east-1',
      'version' => 'latest',
   ]),
   $kmsKeyId
);

$bucket = 'the-bucket-name';
$key = 'the-file-name';
$cipherOptions = [
   'Cipher' => 'gcm',
   'KeySize' => 256,
];

$encryptionClient->putObject([
   '@MaterialsProvider' => $materialsProvider,
   '@CipherOptions' => $cipherOptions,
   // Use the commitment policy (REQUIRE_ENCRYPT_REQUIRE_DECRYPT)
   // This encrypts with key commitment and does not decrypt V2 objects
   '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_REQUIRE_DECRYPT',
   '@KmsEncryptionContext' => ['context-key' => 'context-value'],
   'Bucket' => $bucket,
   'Key' => $key,
   'Body' => fopen('file-to-encrypt.txt', 'r'),
]);

$result = $encryptionClient->getObject([
   '@SecurityProfile' => 'V3',
   // Use the commitment policy (REQUIRE_ENCRYPT_REQUIRE_DECRYPT)
   // This encrypts with key commitment and does not decrypt V2 objects
   '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_REQUIRE_DECRYPT',
   '@MaterialsProvider' => $materialsProvider,
   '@CipherOptions' => $cipherOptions,
   'Bucket' => $bucket,
   'Key' => $key,
]);
```

V3 的主要区别：
+ 使用 `KmsMaterialsProviderV3` 代替 `KmsMaterialsProviderV2`
+ 该`@KmsEncryptionContext`参数仍然是`putObject`操作所必需的
+ 该`@KmsEncryptionContext`参数对于`getObject`操作来说是可选的，它将验证提供的加密上下文是否与对象中的加密上下文相匹配。
+ 该`@SecurityProfile`参数控制可以解密哪些加密版本。设置`'V3_AND_LEGACY'`为支持在迁移期间读取 V1 和 V2 加密的对象
+ 该`@CommitmentPolicy`参数控制此操作的承诺策略。设置`'FORBID_ENCRYPT_ALLOW_DECRYPT'`为支持在迁移期间读取非承诺加密对象

## 其他示例
<a name="s3-encryption-migration-v2-v3-examples"></a>

以下示例演示了 V3 中可用的其他配置选项，这些选项可以帮助您管理迁移过程和控制加密行为。

### 启用旧版 支持
<a name="s3-encryption-migration-v2-v3-examples-legacy"></a>

在迁移过程中，您可能需要解密使用 Amazon S3 加密客户端 V1 或 V2 加密的对象。该`@SecurityProfile`参数控制您的 V3 客户端可以解密哪些加密版本。

**何时使用此配置：**当您的应用程序需要读取由 V1 或 V2 客户端加密的对象时，请使用`'V3_AND_LEGACY'`安全配置文件。在迁移期间，当您的存储桶中混合使用新旧加密对象时，这种情况很常见。

```
use Aws\S3\Crypto\S3EncryptionClientV3;
use Aws\S3\S3Client;
use Aws\Crypto\KmsMaterialsProviderV3;
use Aws\Kms\KmsClient;

$kmsKeyId = 'kms-key-id';
$materialsProvider = new KmsMaterialsProviderV3(
    new KmsClient([
        'profile' => 'default',
        'region' => 'us-east-1',
        'version' => 'latest',
    ]),
    $kmsKeyId
);

$encryptionClient = new S3EncryptionClientV3(
    new S3Client([
        'profile' => 'default',
        'region' => 'us-east-1',
        'version' => 'latest',
    ])
);

$bucket = 'the-bucket-name';
$key = 'the-file-name';
$cipherOptions = [
    'Cipher' => 'gcm',
    'KeySize' => 256,
];

// Decrypt objects encrypted with V1, V2, or V3
$result = $encryptionClient->getObject([
    '@SecurityProfile' => 'V3_AND_LEGACY',
    '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_ALLOW_DECRYPT',
    '@MaterialsProvider' => $materialsProvider,
    '@CipherOptions' => $cipherOptions,
    'Bucket' => $bucket,
    'Key' => $key,
]);
```

`@SecurityProfile` 参数接受以下值：
+ `'V3'`（默认）：仅使用密钥承诺解密使用 V3 加密的对象
+ `'V3_AND_LEGACY'`: 解密使用 V1、V2 或 V3 加密的对象

**重要**  
完成迁移并使用 V3 重新加密所有对象后，应删除该`@SecurityProfile`参数或将其设置为，`'V3'`以确保最大限度地提高安全性。

### 配置存储方法
<a name="s3-encryption-migration-v2-v3-examples-storage"></a>

Amazon S3 加密客户端可以通过两种方式存储加密元数据：存储在对象的元数据标头中或单独的指令文件中。该`@MetadataStrategy`参数控制使用哪种存储方法。

**何时使用此配置：**`'INSTRUCTION_FILE'`在需要保留原始对象元数据或处理有元数据大小限制的对象时使用。使用`'METADATA'`（默认）进行更简单的部署，在这种部署中，加密元数据可以存储在对象旁边。

```
use Aws\S3\Crypto\S3EncryptionClientV3;
use Aws\S3\S3Client;
use Aws\Crypto\KmsMaterialsProviderV3;
use Aws\Kms\KmsClient;

$kmsKeyId = 'kms-key-id';
$materialsProvider = new KmsMaterialsProviderV3(
    new KmsClient([
        'profile' => 'default',
        'region' => 'us-east-1',
        'version' => 'latest',
    ]),
    $kmsKeyId
);

$encryptionClient = new S3EncryptionClientV3(
    new S3Client([
        'profile' => 'default',
        'region' => 'us-east-1',
        'version' => 'latest',
    ])
);

$bucket = 'the-bucket-name';
$key = 'the-file-name';
$cipherOptions = [
    'Cipher' => 'gcm',
    'KeySize' => 256,
];

// Store encryption metadata in a separate instruction file
$encryptionClient->putObject([
    '@MaterialsProvider' => $materialsProvider,
    '@CipherOptions' => $cipherOptions,
    '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_REQUIRE_DECRYPT',
    '@MetadataStrategy' => 'INSTRUCTION_FILE',
    '@KmsEncryptionContext' => ['context-key' => 'context-value'],
    'Bucket' => $bucket,
    'Key' => $key,
    'Body' => fopen('file-to-encrypt.txt', 'r'),
]);

// Store encryption metadata in object headers (default)
$encryptionClient->putObject([
    '@MaterialsProvider' => $materialsProvider,
    '@CipherOptions' => $cipherOptions,
    '@CommitmentPolicy' => 'REQUIRE_ENCRYPT_REQUIRE_DECRYPT',
    '@MetadataStrategy' => 'METADATA',
    '@KmsEncryptionContext' => ['context-key' => 'context-value'],
    'Bucket' => $bucket,
    'Key' => $key,
    'Body' => fopen('file-to-encrypt.txt', 'r'),
]);
```

`@MetadataStrategy` 参数接受以下值：
+ `'METADATA'`（默认）：将加密元数据存储在对象的元数据标头中
+ `'INSTRUCTION_FILE'`: 将加密元数据存储在带有后缀的单独指令文件中 `.instruction`

**注意**  
使用时`'INSTRUCTION_FILE'`，带有密钥承诺的 AES GCM 算法可提供额外的保护，防止数据密钥被篡改。使用`'METADATA'`存储空间的对象无法从这种额外保护中受益。