

# 配置安全访问和限制对内容的访问
<a name="SecurityAndPrivateContent"></a>

CloudFront 在保护其传输的内容方面提供了多种选择。可使用 CloudFront 通过以下方式来保护和限制对内容的访问：
+ 配置 HTTPS 连接
+ 阻止特定地理位置的用户访问内容
+ 要求用户使用 CloudFront 签名 URL 或签名 Cookie 来访问内容
+ 为特定的内容字段设置字段级加密
+ 使用 AWS WAF 控制对您的内容的访问

还应该为基础设施和应用程序实施 DDoS 弹性架构。有关更多信息，请参阅 [AWS Best Practices for DDoS Resiliency](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/aws-best-practices-ddos-resiliency.html)。

有关更多信息，请参阅以下内容：
+ [使用 CloudFront 保护您的内容分发](https://aws.amazon.com/cloudfront/security/)
+ [Amazon OpenSearch Service 上的 SIEM](https://github.com/aws-samples/siem-on-amazon-opensearch-service/blob/main/README.md)

**Topics**
+ [将 HTTPS 与 CloudFront 结合使用](using-https.md)
+ [使用备用域名和 HTTPS](using-https-alternate-domain-names.md)
+ [CloudFront 的双向 TLS 身份验证（查看器 mTLS）](mtls-authentication.md)
+ [源双向 TLS 与 CloudFront 结合使用](origin-mtls-authentication.md)
+ [使用签名 URL 和签名 Cookie 提供私有内容](PrivateContent.md)
+ [限制对AWS源的访问](private-content-restricting-access-to-origin.md)
+ [限制访问应用程序负载均衡器](restrict-access-to-load-balancer.md)
+ [限制您的内容的地理分配](georestrictions.md)
+ [使用字段级加密帮助保护敏感数据](field-level-encryption.md)

# 将 HTTPS 与 CloudFront 结合使用
<a name="using-https"></a>

您可以将 CloudFront 配置为要求查看器使用 HTTPS，以便在 CloudFront 与查看器通信时加密连接。您也可以将 CloudFront 配置为通过源使用 HTTPS，以便在 CloudFront 与源通信时加密连接。

如果您将 CloudFront 配置为要求使用 HTTPS 与查看器和您的源进行通信，当 CloudFront 接收请求时将发生以下操作。

1. 查看器向 CloudFront 提交 HTTPS 请求。查看器和 CloudFront 之间存在一些 SSL/TLS 协商。最后，查看器以加密格式提交请求。

1. 如果 CloudFront 边缘站点包含缓存响应，CloudFront 将为响应加密并将其返回给查看器，然后查看器对其进行解密。

1. 如果 CloudFront 边缘站点不包含缓存响应中，CloudFront 则与您的源执行 SSL/TLS 协商，当协商完成时，将请求以加密格式转发给您的源。

1. 您的源会对请求进行解密、处理（生成响应），加密响应，并将响应返回给 CloudFront。

1. CloudFront 对响应进行解密，并对其重新加密，然后将其转发给查看器。CloudFront 还将缓存边缘站点中的响应，以便在下次请求时可用。

1. 查看器对响应进行解密。

无论您的源是 Amazon S3 存储桶、MediaStore 还是 HTTP/S 服务器之类的自定义源，过程基本相同：

**注意**  
为了帮助阻止 SSL 重新协商类型的攻击，CloudFront 不支持查看方和源请求的重新协商。

或者，您可以为 CloudFront 分配启用双向身份验证。有关更多信息，请参阅 [CloudFront 的双向 TLS 身份验证（查看器 mTLS）源双向 TLS 与 CloudFront 结合使用](mtls-authentication.md)。

有关在查看器和 CloudFront 之间以及 CloudFront 和源之间如何使用 HTTPS 的信息，请参阅以下主题。

**Topics**
+ [要求在查看器和 CloudFront 之间使用 HTTPS](using-https-viewers-to-cloudfront.md)
+ [要求通过 HTTPS 连接到自定义源](using-https-cloudfront-to-custom-origin.md)
+ [要求通过 HTTPS 连接到 Amazon S3 源](using-https-cloudfront-to-s3-origin.md)
+ [查看器和 CloudFront 之间支持的协议和密码](secure-connections-supported-viewer-protocols-ciphers.md)
+ [CloudFront 与源之间受支持的协议和密码](secure-connections-supported-ciphers-cloudfront-to-origin.md)

# 要求在查看器和 CloudFront 之间使用 HTTPS 进行通信
<a name="using-https-viewers-to-cloudfront"></a>

您可以在 CloudFront 分配中配置一个或多个缓存行为，以便要求在查看器和 CloudFront 之间使用 HTTPS 进行通信。您也可以配置一个或多个缓存行为以允许 HTTP 和 HTTPS，以便 CloudFront 对某些对象要求使用 HTTPS，而对其他对象不做这一要求。配置步骤取决于您在对象 URL 中使用的域名：
+ 如果您使用 CloudFront 指派给您的域名（例如 d111111abcdef8.cloudfront.net），您可以针对要求 HTTPS 通信的一个或多个缓存行为更改 **查看器协议策略**设置。在该配置中，CloudFront 提供 SSL/TLS 证书。

  要使用 CloudFront 控制台更改**查看器协议策略**值，请参阅本节后面的步骤。

  有关如何使用 CloudFront API 更改 `ViewerProtocolPolicy` 元素值的信息，请参阅《Amazon CloudFront API 参考》**中的 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。
+ 如果您使用自己的域名（例如 example.com），则需要更改若干 CloudFront 设置。您还需要使用 AWS Certificate Manager (ACM) 提供的 SSL/TLS 证书，或将证书从第三方证书颁发机构导入到 ACM 或 IAM 证书存储。有关更多信息，请参阅 [使用备用域名和 HTTPS](using-https-alternate-domain-names.md)。

**注意**  
如果您希望确保在 CloudFront 从源获取对象并且查看器从 CloudFront 获取这些对象时，对象得到了加密，请始终在 CloudFront 和您的源之间使用 HTTPS。如果您最近在 CloudFront 和您的源之间从 HTTP 更改为 HTTPS，建议您使 CloudFront 边缘站点中的对象失效。CloudFront 将对象返回给查看器，而无论查看器使用的协议（HTTP 或 HTTPS）与 CloudFront 用于获取对象的协议是否匹配。有关删除或替换分配中的对象的更多信息，请参阅 [添加、删除或替换 CloudFront 分配的内容](AddRemoveReplaceObjects.md)。

## 要求查看器使用 HTTPS
<a name="configure-cloudfront-HTTPS-viewers"></a>

如果针对一个或多个缓存行为要求在查看器和 CloudFront 之间使用 HTTPS，请执行以下步骤。<a name="using-https-viewers-to-cloudfront-procedure"></a>

**将 CloudFront 配置为要求在查看器与 CloudFront 之间使用 HTTPS**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在 CloudFront 控制台的顶部窗格中，选择您要更新的分配的 ID。

1. 在**行为**选项卡中，选择要更新的缓存行为，然后选择**编辑**。

1. 为**查看器协议策略**指定以下值之一：  
**Redirect HTTP to HTTPS**  
查看器可以使用两种协议。HTTP `GET` 和 `HEAD` 请求会自动重定向到 HTTPS 请求。CloudFront 返回 HTTP 状态代码 301（永久移动）以及新的 HTTPS URL。然后，查看器会使用此 HTTPS URL 将请求重新提交到 CloudFront。  
如果您通过 HTTP 使用 HTTP 到 HTTPS 的缓存行为以及请求协议版本 HTTP 1.1 或更高版本发送 `POST`、`PUT`、`DELETE`、`OPTIONS` 或 `PATCH`，CloudFront 会使用 HTTP 状态代码 307（临时重定向）将请求重定向到 HTTPS 位置。这可确保使用相同的方法和正文负载将请求再次发送到新位置。  
如果您使用低于 HTTP 1.1 的请求协议版本通过 HTTP 到 HTTPS 的缓存行为发送 `POST`、`PUT`、`DELETE`、`OPTIONS` 或 `PATCH` 请求，CloudFront 将返回 HTTP 状态代码 403（禁止访问）。
在查看器发出将重定向到 HTTPS 请求的 HTTP 请求时，会产生针对这两个请求的 CloudFront 费用。对于 HTTP 请求，仅对该请求和 CloudFront 返回到查看器的标头计费。对于 HTTPS 请求，对该请求、标头和由源返回的对象计费。  
**仅 HTTPS**  
查看器只有使用 HTTPS 才能访问您的内容。如果查看器发送 HTTP 请求而不是 HTTPS 请求，则 CloudFront 将返回 HTTP 状态代码 403（禁止访问）且不会返回对象。

1. 选择**保存更改**。

1. 针对要求在查看器和 CloudFront 之间使用 HTTPS 的其他每个缓存行为，重复步骤 3 到 5。

1. 请确认以下内容，然后在生产环境中使用更新后的配置：
   + 每个缓存行为中的路径模式仅适用于您希望查看器使用 HTTPS 的请求。
   + 缓存行为按您希望 CloudFront 评估它们的顺序列出。有关更多信息，请参阅 [路径模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)。
   + 缓存行为将请求路由到正确的源。

# 要求在 CloudFront 与您的自定义源之间使用 HTTPS 进行通信
<a name="using-https-cloudfront-to-custom-origin"></a>

您可以要求使用 HTTPS 在 CloudFront 与您的源之间的通信

**注意**  
如果您的源是配置为网站终端节点的 Amazon S3 存储桶，则无法将 CloudFront 配置为将 HTTPS 与源结合使用，因为 Amazon S3 不支持对网站终端节点使用 HTTPS。

如需要求在 CloudFront 和您的源之间的需要使用 HTTPS，请按照本主题中的过程执行以下操作：

1. 在您的分配中，更改源的 **Origin Protocol Policy (源协议策略)** 设置

1. 在您的自定义源服务器上安装 SSL/TLS 证书（当您使用 Amazon S3 源或某些其他 AWS 源时，不需要执行此操作）。

**Topics**
+ [要求自定义源使用 HTTPS](#using-https-cloudfront-to-origin-distribution-setting)
+ [在自定义源上安装 SSL/TLS 证书](#using-https-cloudfront-to-origin-certificate)

## 要求自定义源使用 HTTPS
<a name="using-https-cloudfront-to-origin-distribution-setting"></a>

以下过程介绍了如何配置 CloudFront 以使用 HTTPS 与 Elastic Load Balancing 负载均衡器、Amazon EC2 实例或其他自定义源进行通信。有关使用 CloudFront API 更新分配的信息，请参阅《Amazon CloudFront API 参考》**中的 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。<a name="using-https-cloudfront-to-custom-origin-procedure"></a>

**将 CloudFront 配置为要求在 CloudFront 和您的自定义源之间使用 HTTPS**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在 CloudFront 控制台的顶部窗格中，选择您要更新的分配的 ID。

1. 在**行为**选项卡中，选择要更新的源，然后选择**编辑**。

1. 更新以下设置：  
**源协议策略**  
为您的分配中的适用源更改**源协议策略**：  
   + **仅 HTTPS** – CloudFront 仅使用 HTTPS 与自定义源进行通信。
   + **匹配查看器** – CloudFront 使用 HTTP 或 HTTPS 与自定义源进行通信，具体取决于查看器请求协议。例如，如果您**源协议策略**选择**匹配查看器**，并且查看器使用 HTTPS 从 CloudFront 请求对象，则 CloudFront 也会使用 HTTPS 将请求转发给您的源。

     只有在为**查看器协议策略**指定**将 HTTP 重定向到 HTTPS** 或**仅 HTTPS** 时，才能选择**匹配查看器**。

     请注意，CloudFront 仅缓存对象一次，即使查看器使用 HTTP 和 HTTPS 协议发出请求也是如此。  
**Origin SSL Protocols**  
为您的分配中的适用源选择 **Origin SSL Protocols**。由于 SSLv3 协议的安全性较低，因此，建议您仅在源不支持 TLSv1 或更高版本的情况下选择 SSLv3。TLSv1 握手与 SSLv3 向后和向前兼容，但 TLSv1.1 及更高版本不是这样。当您选择 SSLv3 时，CloudFront *仅* 发送 SSLv3 握手请求。

1. 选择**保存更改**。

1. 针对要求在 CloudFront 和您的自定义源之间使用 HTTPS 的其他每个源，重复步骤 3 到 5。

1. 请确认以下内容，然后在生产环境中使用更新后的配置：
   + 每个缓存行为中的路径模式仅适用于您希望查看器使用 HTTPS 的请求。
   + 缓存行为按您希望 CloudFront 评估它们的顺序列出。有关更多信息，请参阅 [路径模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)。
   + 缓存行为将请求路由到更改了**源协议策略**的源。

## 在自定义源上安装 SSL/TLS 证书
<a name="using-https-cloudfront-to-origin-certificate"></a>

您可以在自定义源上使用来自以下来源的 SSL/TLS 证书：
+ 如果您的源是 Elastic Load Balancing 负载均衡器，则可以使用 AWS Certificate Manager (ACM) 提供的证书。您也可以使用信任的第三方证书颁发机构签署并导入 ACM 的证书。
+ 对于 Elastic Load Balancing 负载均衡器之外的源，您必须使用由信任的第三方证书颁发机构 (CA)（例如，Comodo、DigiCert 或 Symantec）签署的证书。

从源返回的证书必须包括以下域名之一：
+ 源的**源域**字段中的域名（CloudFront API 中的 `DomainName` 字段）。
+ `Host` 标头中的域名，如果缓存行为配置为将 `Host` 标头转发至源。

当 CloudFront 使用 HTTPS 与您的源进行通信时，CloudFront 会验证信任的证书颁发机构颁发的证书。CloudFront 与 Mozilla 支持相同的证书颁发机构。有关当前列表，请参阅 [Mozilla 包含的 CA 证书列表](https://wiki.mozilla.org/CA/Included_Certificates)。您无法使用自签名证书在 CloudFront 和您的源之间进行 HTTPS 通信。

**重要**  
如果源服务器返回过期证书、无效证书或自签名证书，或者如果源服务器以错误顺序返回证书链，CloudFront 将中断 TCP 连接，向查看器返回 HTTP 错误代码 502（无效网关），并将 `X-Cache` 标头设置为 `Error from cloudfront`。此外，如果不存在完整的证书链（包括中间证书），则 CloudFront 将中断 TCP 连接。

# 要求在 CloudFront 与 Amazon S3 源之间使用 HTTPS 进行通信
<a name="using-https-cloudfront-to-s3-origin"></a>

当您的源是 Amazon S3 存储桶时，用于使用 HTTPS 以与 CloudFront 进行通信的选项取决于您使用存储桶的方式。如果您的 Amazon S3 存储桶配置为网站终端节点，则您无法将 CloudFront 配置为使用 HTTPS 与您的源进行通信，因为 Amazon S3 不支持该配置中的 HTTPS 连接。

当源是支持 HTTPS 通信的 Amazon S3 存储桶时，CloudFront 使用查看器用来提交请求的协议将请求转发到 S3。[协议（仅自定义源）](DownloadDistValuesOrigin.md#DownloadDistValuesOriginProtocolPolicy) 的默认设置为**匹配查看器**，并且不能更改。但是，如果为 Amazon S3 源启用来源访问控制（OAC），则 CloudFront 和 Amazon S3 之间使用的通信将取决于您的设置。有关更多信息，请参阅 [创建新的源访问控制](private-content-restricting-access-to-s3.md#create-oac-overview-s3)。

如果您希望要求在 CloudFront 和 Amazon S3 之间使用 HTTPS 进行通信，则必须将**查看器协议策略**的值更改为**将 HTTP 重定向到 HTTPS** 或**仅 HTTPS**。有关如何使用 CloudFront 控制台更改**查看器协议策略**的信息，请参阅本节后面的步骤。有关使用 CloudFront API 更新分配的 `ViewerProtocolPolicy` 元素的信息，请参阅 *Amazon CloudFront API 参考*中的 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。

当您将 HTTPS 与支持 HTTPS 通信的 Amazon S3 存储桶结合使用时，Amazon S3 会提供 SSL/TLS 证书，因此您无需提供。

## 要求 Amazon S3 源使用 HTTPS
<a name="configure-cloudfront-HTTPS-S3-origin"></a>

以下步骤演示如何将 CloudFront 配置为要求通过 HTTPS 连接到您的 Amazon S3 源。<a name="using-https-cloudfront-to-s3-origin-procedure"></a>

**将 CloudFront 配置为要求通过 HTTPS 连接到您的 Amazon S3 源**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在 CloudFront 控制台的顶部窗格中，选择您要更新的分配的 ID。

1. 在 **Behaviors** 选项卡中，选择要更新的缓存行为，然后选择 **Edit**。

1. 为**查看器协议策略**指定以下值之一：  
**将 HTTP 重定向到 HTTPS**  
查看器可使用两种协议，但 HTTP 请求将自动重定向到 HTTPS 请求。CloudFront 返回 HTTP 状态代码 301（永久移动）以及新的 HTTPS URL。然后，查看器会使用此 HTTPS URL 将请求重新提交到 CloudFront。  
CloudFront 不将 `DELETE`、`OPTIONS`、`PATCH`、`POST` 或 `PUT` 请求从 HTTP 重定向到 HTTPS。如果您将一个缓存行为配置为重定向到 HTTPS，CloudFront 会针对该缓存行为的 HTTP `DELETE`、`OPTIONS`、`PATCH`、`POST` 或 `PUT` 请求响应 HTTP 状态代码 403（禁止访问）。
在查看器发出将重定向到 HTTPS 请求的 HTTP 请求时，会产生针对这两个请求的 CloudFront 费用。对于 HTTP 请求，仅对该请求和 CloudFront 返回到查看器的标头计费。对于 HTTPS 请求，对该请求、标头和由源返回的对象计费。  
**HTTPS Only**  
查看器只有使用 HTTPS 才能访问您的内容。如果查看器发送 HTTP 请求而不是 HTTPS 请求，则 CloudFront 将返回 HTTP 状态代码 403（禁止访问）且不会返回对象。

1. 选择**是，编辑**。

1. 针对要求在查看器和 CloudFront 之间以及 CloudFront 和 S3 之间使用 HTTPS 的其他每个缓存行为，重复步骤 3 到 5。

1. 请确认以下内容，然后在生产环境中使用更新后的配置：
   + 每个缓存行为中的路径模式仅适用于您希望查看器使用 HTTPS 的请求。
   + 缓存行为按您希望 CloudFront 评估它们的顺序列出。有关更多信息，请参阅 [路径模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)。
   + 缓存行为将请求路由到正确的源。

# 查看器和 CloudFront 之间支持的协议和密码
<a name="secure-connections-supported-viewer-protocols-ciphers"></a>

当您[需要在查看器和 CloudFront 分配之间使用 HTTPS](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 时，必须选择一项[安全策略](DownloadDistValuesGeneral.md#DownloadDistValues-security-policy)来确定以下设置。
+ CloudFront 与查看器通信时使用的最低 SSL/TLS 协议。
+ CloudFront 可用于加密与查看器之间的通信的密码。

要选择安全策略，请为 [安全策略（最低 SSL/TLS 版本）](DownloadDistValuesGeneral.md#DownloadDistValues-security-policy) 指定合适的值。下表列出了每个安全策略中，CloudFront 可用的协议和密码。

查看器至少必须支持这些受支持的密码中的一个，才能与 CloudFront 建立 HTTPS 连接。CloudFront 按列出的顺序从查看器支持的密码中选择一种密码。另请参阅 [OpenSSL、s2n 和 RFC 密码名称](#secure-connections-openssl-rfc-cipher-names)。


|  | 安全策略 |  | SSLv3 | TLSv1 | TLSv1\$12016 | TLSv1.1\$12016 | TLSv1.2\$12018 | TLSv1.2\$12019 | TLSv1.2\$12021 | TLSv1.2\$12025 | TLSv1.3\$12025 | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 支持的 SSL/TLS 协议 | 
| TLSv1.3 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLSv1.2 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  | 
| TLSv1.1 | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| TLSv1 | ♦ | ♦ | ♦ |  |  |  |  |  |  | 
| SSLv3 | ♦ |  |  |  |  |  |  |  |  | 
| 支持的 TLSv1.3 密码 | 
| TLS\$1AES\$1128\$1GCM\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1AES\$1256\$1GCM\$1SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1CHACHA20\$1POLY1305\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  | ♦ | 
| 支持的 ECDSA 密码 | 
| ECDHE-ECDSA-AES128- GCM-SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  | 
| ECDHE-ECDSA-AES128-SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  | 
| ECDHE-ECDSA-AES128-SHA | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| ECDHE-ECDSA-AES256- GCM-SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  | 
| ECDHE-ECDSA-CHACHA20-POLY1305 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  | 
| ECDHE-ECDSA-AES256-SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  | 
| ECDHE-ECDSA-AES256-SHA | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| 支持的 RSA 密码 | 
| ECDHE-RSA-AES128- GCM-SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  | 
| ECDHE-RSA-AES128-SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  | 
| ECDHE-RSA-AES128-SHA | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| ECDHE-RSA-AES256- GCM-SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  | 
| ECDHE-RSA-CHACHA20-POLY1305 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  | 
| ECDHE-RSA-AES256-SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  | 
| ECDHE-RSA-AES256-SHA | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| AES128-GCM-SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  |  | 
| AES256-GCM-SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  |  | 
| AES128-SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ |  |  |  |  | 
| AES256-SHA | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| AES128-SHA | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| DES-CBC3-SHA | ♦ | ♦ |  |  |  |  |  |  |  | 
| RC4-MD5 | ♦ |  |  |  |  |  |  |  |  | 

## OpenSSL、s2n 和 RFC 密码名称
<a name="secure-connections-openssl-rfc-cipher-names"></a>

OpenSSL 和 [s2n](https://github.com/awslabs/s2n) 使用的密码名称与 TLS 标准使用的不同（[RFC 2246](https://tools.ietf.org/html/rfc2246)、[RFC 4346](https://tools.ietf.org/html/rfc4346)、[RFC 5246](https://tools.ietf.org/html/rfc5246) 和 [RFC 8446](https://tools.ietf.org/html/rfc8446)）。下表为每个密码列出了 OpenSSL 和 s2n 名称及对应的 RFC 名称。

CloudFront 同时支持经典密钥交换与量子安全密钥交换。对于使用椭圆曲线的经典密钥交换，CloudFront 支持：
+ `prime256v1`
+ `X25519`
+ `secp384r1`

对于量子安全密钥交换，CloudFront 支持：
+ `X25519MLKEM768`
+ `SecP256r1MLKEM768`
**注意**  
仅 TLS 1.3 支持量子安全密钥交换。TLS 1.2 及早期版本不支持量子安全密钥交换。

  有关更多信息，请参阅以下主题：
  + [Post-Quantum Cryptography](https://aws.amazon.com/security/post-quantum-cryptography/)
  + [Cryptography algorithms and AWS 服务](https://docs.aws.amazon.com/prescriptive-guidance/latest/encryption-best-practices/aws-cryptography-services.html#algorithms)
  + [Hybrid key exchange in TLS 1.3](https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/)

有关 CloudFront 的证书要求的更多信息，请参阅[在 CloudFront 中使用 SSL/TLS 证书的要求](cnames-and-https-requirements.md)。


| OpenSSL 和 s2n 密码名称 | RFC 密码名称 | 
| --- | --- | 
| 支持的 TLSv1.3 密码 | 
| TLS\$1AES\$1128\$1GCM\$1SHA256 | TLS\$1AES\$1128\$1GCM\$1SHA256 | 
| TLS\$1AES\$1256\$1GCM\$1SHA384 | TLS\$1AES\$1256\$1GCM\$1SHA384 | 
| TLS\$1CHACHA20\$1POLY1305\$1SHA256 | TLS\$1CHACHA20\$1POLY1305\$1SHA256 | 
| 支持的 ECDSA 密码 | 
| ECDHE-ECDSA-AES128- GCM-SHA256 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1128\$1GCM\$1SHA256 | 
| ECDHE-ECDSA-AES128-SHA256 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1128\$1CBC\$1SHA256 | 
| ECDHE-ECDSA-AES128-SHA | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1128\$1CBC\$1SHA | 
| ECDHE-ECDSA-AES256- GCM-SHA384 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1256\$1GCM\$1SHA384 | 
| ECDHE-ECDSA-CHACHA20-POLY1305 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1CHACHA20\$1POLY1305\$1SHA256 | 
| ECDHE-ECDSA-AES256-SHA384 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1256\$1CBC\$1SHA384 | 
| ECDHE-ECDSA-AES256-SHA | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1256\$1CBC\$1SHA | 
| 支持的 RSA 密码 | 
| ECDHE-RSA-AES128- GCM-SHA256 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1128\$1GCM\$1SHA256 | 
| ECDHE-RSA-AES128-SHA256 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA256  | 
| ECDHE-RSA-AES128-SHA | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA | 
| ECDHE-RSA-AES256- GCM-SHA384 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1256\$1GCM\$1SHA384  | 
| ECDHE-RSA-CHACHA20-POLY1305 | TLS\$1ECDHE\$1RSA\$1WITH\$1CHACHA20\$1POLY1305\$1SHA256 | 
| ECDHE-RSA-AES256-SHA384 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1256\$1CBC\$1SHA384  | 
| ECDHE-RSA-AES256-SHA | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1256\$1CBC\$1SHA | 
| AES128-GCM-SHA256 | TLS\$1RSA\$1WITH\$1AES\$1128\$1GCM\$1SHA256 | 
| AES256-GCM-SHA384 | TLS\$1RSA\$1WITH\$1AES\$1256\$1GCM\$1SHA384 | 
| AES128-SHA256 | TLS\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA256 | 
| AES256-SHA | TLS\$1RSA\$1WITH\$1AES\$1256\$1CBC\$1SHA | 
| AES128-SHA | TLS\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA | 
| DES-CBC3-SHA  | TLS\$1RSA\$1WITH\$13DES\$1EDE\$1CBC\$1SHA  | 
| RC4-MD5 | TLS\$1RSA\$1WITH\$1RC4\$1128\$1MD5 | 

## 查看器和 CloudFront 之间受支持的签名方案
<a name="secure-connections-viewer-signature-schemes"></a>

CloudFront 支持以下用于查看器和 CloudFront 之间的连接的签名方案。


|  | 安全策略 | 签名模式 | SSLv3 | TLSv1 | TLSv1\$12016 | TLSv1.1\$12016 | TLSv1.2\$12018 | TLSv1.2\$12019 |  TLSv1.2\$12021 | TLSv1.2\$12025 | TLSv1.3\$12025 | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PSS\$1PSS\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PSS\$1PSS\$1SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PSS\$1PSS\$1SHA512 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PSS\$1RSAE\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PSS\$1RSAE\$1SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PSS\$1RSAE\$1SHA512 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA512 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA224 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA512 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA224 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ |  |  | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SECP256R1\$1SHA256 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SECP384R1\$1SHA384 | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | ♦ | 
| TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA1 | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 
| TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA1 | ♦ | ♦ | ♦ | ♦ |  |  |  |  |  | 

# CloudFront 与源之间受支持的协议和密码
<a name="secure-connections-supported-ciphers-cloudfront-to-origin"></a>

如果您选择[要求在 CloudFront 和源之间使用 HTTPS](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesOriginProtocolPolicy)，则可以确定安全连接[允许使用哪项 SSL/TLS 协议](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesOriginSSLProtocols)，并且 CloudFront 可以使用下表中列出的任何 ECDSA 或 RSA 密码连接到源。您的源至少必须支持这些密码中的其中一个，才能使 CloudFront 与您的源建立 HTTPS 连接。

OpenSSL 和 [s2n](https://github.com/awslabs/s2n) 使用的密码名称与 TLS 标准使用的不同（[RFC 2246](https://tools.ietf.org/html/rfc2246)、[RFC 4346](https://tools.ietf.org/html/rfc4346)、[RFC 5246](https://tools.ietf.org/html/rfc5246) 和 [RFC 8446](https://tools.ietf.org/html/rfc8446)）。下表为每个密码列出了 OpenSSL 和 s2n 名称及对应的 RFC 名称。

对于使用椭圆曲线密钥交换算法的密码，CloudFront 支持以下椭圆曲线：
+ prime256v1
+ secp384r1
+ X25519


| OpenSSL 和 s2n 密码名称 | RFC 密码名称 | 
| --- | --- | 
| 支持的 ECDSA 密码 | 
| ECDHE-ECDSA-AES256- GCM-SHA384 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1256\$1GCM\$1SHA384 | 
| ECDHE-ECDSA-AES256-SHA384 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1256\$1CBC\$1SHA384 | 
| ECDHE-ECDSA-AES256-SHA | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1256\$1CBC\$1SHA | 
| ECDHE-ECDSA-AES128- GCM-SHA256 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1128\$1GCM\$1SHA256 | 
| ECDHE-ECDSA-AES128-SHA256 | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1128\$1CBC\$1SHA256 | 
| ECDHE-ECDSA-AES128-SHA | TLS\$1ECDHE\$1ECDSA\$1WITH\$1AES\$1128\$1CBC\$1SHA | 
| 支持的 RSA 密码 | 
| ECDHE-RSA-AES256- GCM-SHA384 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1256\$1GCM\$1SHA384 | 
| ECDHE-RSA-AES256-SHA384 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1256\$1CBC\$1SHA384 | 
| ECDHE-RSA-AES256-SHA | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1256\$1CBC\$1SHA | 
| ECDHE-RSA-AES128- GCM-SHA256 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1128\$1GCM\$1SHA256 | 
| ECDHE-RSA-AES128-SHA256 | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA256 | 
| ECDHE-RSA-AES128-SHA | TLS\$1ECDHE\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA | 
| AES256-SHA | TLS\$1RSA\$1WITH\$1AES\$1256\$1CBC\$1SHA | 
| AES128-SHA | TLS\$1RSA\$1WITH\$1AES\$1128\$1CBC\$1SHA | 
| DES-CBC3-SHA | TLS\$1RSA\$1WITH\$13DES\$1EDE\$1CBC\$1SHA | 
| RC4-MD5 | TLS\$1RSA\$1WITH\$1RC4\$1128\$1MD5 | 

**CloudFront 与源之间受支持的签名方案**

CloudFront 支持以下签名方案，用于 CloudFront 和源之间的连接。
+ TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA256
+ TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA384
+ TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA512
+ TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA224
+ TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA256
+ TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA384
+ TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA512
+ TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA224
+ TLS\$1SIGNATURE\$1SCHEME\$1RSA\$1PKCS1\$1SHA1
+ TLS\$1SIGNATURE\$1SCHEME\$1ECDSA\$1SHA1

# 使用备用域名和 HTTPS
<a name="using-https-alternate-domain-names"></a>

如果您希望在文件的 URL 中使用您自己的域名（例如 `https://www.example.com/image.jpg`），并希望您的查看器使用 HTTPS，则必须完成以下主题中的步骤。（如果您在 URL 中使用默认 CloudFront 分配域名，例如 `https://d111111abcdef8.cloudfront.net/image.jpg`，请按照以下主题中的指导进行操作：[要求在查看器和 CloudFront 之间使用 HTTPS 进行通信](using-https-viewers-to-cloudfront.md)。）

**重要**  
将某证书添加到您的分配时，CloudFront 会立即将该证书传播到其所有边缘站点。当新的边缘站点可用时，CloudFront 也会将该证书传播到这些位置。您不能限制 CloudFront 将证书传播到的边缘站点。

**Topics**
+ [选择 CloudFront 如何处理 HTTPS 请求](cnames-https-dedicated-ip-or-sni.md)
+ [在 CloudFront 中使用 SSL/TLS 证书的要求](cnames-and-https-requirements.md)
+ [在 CloudFront 中使用 SSL/TLS 证书的配额（仅在查看器和 CloudFront 之间使用 HTTPS）](cnames-and-https-limits.md)
+ [配置备用域名和 HTTPS](cnames-and-https-procedures.md)
+ [确定 SSL/TLS RSA 证书中公有密钥的大小](cnames-and-https-size-of-public-key.md)
+ [增加 SSL/TLS 证书的配额](increasing-the-limit-for-ssl-tls-certificates.md)
+ [轮换 SSL/TLS 证书](cnames-and-https-rotate-certificates.md)
+ [从自定义 SSL/TLS 证书恢复为默认 CloudFront 证书](cnames-and-https-revert-to-cf-certificate.md)
+ [将使用专用 IP 地址的自定义 SSL/TLS 证书切换到 SNI](cnames-and-https-switch-dedicated-to-sni.md)

# 选择 CloudFront 如何处理 HTTPS 请求
<a name="cnames-https-dedicated-ip-or-sni"></a>

如果希望您的查看器使用 HTTPS 并对您的文件使用备用域名，请为 CloudFront 处理 HTTPS 请求的方式选择下列选项之一：
+ 使用[服务器名称指示 (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) – 建议
+ 在每个边缘站点使用专用 IP 地址

本部分说明了每个选项的工作方式。

## 使用 SNI 处理 HTTPS 请求（适用于大多数客户端）
<a name="cnames-https-sni"></a>

[服务器名称指示 (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) 是对 TLS 协议的扩展，2010 年以后发布的浏览器和客户端均支持。如果您将 CloudFront 配置为使用 SNI 处理 HTTPS 请求，则 CloudFront 将备用域名与每个边缘站点中的 IP 地址关联。当查看器提交针对内容的 HTTPS 请求时，DNS 将该请求传送到正确边缘站点的 IP 地址。指向您域名的 IP 地址在 SSL/TLS 握手协商期间确定；IP 地址并非专用于您的分发。

在建立 HTTPS 连接的过程的早期便进行了 SSL/TLS 协商。如果 CloudFront 无法立即确定该请求所针对的域，则它会中断连接。在支持 SNI 的查看器针对您的内容提交 HTTPS 请求时，将发生以下操作：

1. 查看器自动从请求 URL 中获取域名，并将其添加到 TLS 客户端问候消息的 SNI 扩展中。

1. 当 CloudFront 收到 TLS 客户端问候时，它会使用 SNI 扩展中的域名来查找匹配的 CloudFront 分配并发回关联的 TLS 证书。

1. 查看器和 CloudFront 执行 SSL/TLS 协商。

1. CloudFront 将请求的内容返回到查看器。

有关支持 SNI 的浏览器的当前列表，请参阅 Wikipedia 条目[服务器名称指示](https://en.wikipedia.org/wiki/Server_Name_Indication)。

如果您希望使用 SNI，但您的某些用户的浏览器不支持 SNI，则您有以下几种选择：
+ 配置 CloudFront 以使用专用 IP 地址而不是 SNI 来提供 HTTPS 请求。有关更多信息，请参阅 [使用专用 IP 地址处理 HTTPS 请求（适用于所有客户端）](#cnames-https-dedicated-ip)。
+ 使用 CloudFront SSL/TLS 证书而不是自定义证书。这要求您在文件的 URL（例如 `https://d111111abcdef8.cloudfront.net/logo.png`）中为您的分配使用 CloudFront 域名。

  如果您使用默认 CloudFront 证书，查看器必须支持 SSL 协议 TLSv1 或更高版本。CloudFront 不支持 SSLv3 使用默认的 CloudFront 证书。

  您还必须将 CloudFront 使用的 SSL/TLS 证书从自定义证书更改为默认 CloudFront 证书：
  + 如果您尚未使用分配来分发内容，则只需更改配置。有关更多信息，请参阅 [更新分配](HowToUpdateDistribution.md)。
  + 如果您已使用分配来分发内容，则必须创建新的 CloudFront 分配并更改文件的 URL，以便减少或消除内容不可用的时间。有关更多信息，请参阅 [从自定义 SSL/TLS 证书恢复为默认 CloudFront 证书](cnames-and-https-revert-to-cf-certificate.md)。
+ 如果您可以控制用户使用的浏览器，请让用户将浏览器升级为支持 SNI 的浏览器。
+ 使用 HTTP 而不是 HTTPS。

## 使用专用 IP 地址处理 HTTPS 请求（适用于所有客户端）
<a name="cnames-https-dedicated-ip"></a>

服务器名称指示 (SNI) 是一种将请求与域关联的方法。另一种方法是使用专用 IP 地址。如果您的用户无法升级到 2010 年之后发布的浏览器或客户端，您可以使用专用 IP 地址为 HTTPS 请求提供服务。有关支持 SNI 的浏览器的当前列表，请参阅 Wikipedia 条目[服务器名称指示](https://en.wikipedia.org/wiki/Server_Name_Indication)。

**重要**  
如果您将 CloudFront 配置为使用专用 IP 地址提供 HTTPS 请求，则每月会产生额外的费用。一旦您将 SSL/TLS 证书与某个分配关联并启用该分配，就将开始计费。有关 CloudFront 定价的更多信息，请参阅 [Amazon CloudFront 定价](https://aws.amazon.com/cloudfront/pricing)。另请参阅[Using the Same Certificate for Multiple CloudFront Distributions](cnames-and-https-limits.md#cnames-and-https-same-certificate-multiple-distributions)。

当您将 CloudFront 配置为使用专用 IP 地址处理 HTTPS 请求时，CloudFront 会将您的证书与每个 CloudFront 边缘站点中的专用 IP 地址关联。在查看器针对您的内容提交 HTTPS 请求时，将发生以下操作：

1. DNS 将请求路由到适用边缘站点中您的分配的 IP 地址。

1. 如果客户端请求在 `ClientHello` 消息中提供 SNI 扩展，CloudFront 会搜索与该 SNI 关联的分配。
   + 如果存在匹配项，CloudFront 会使用 SSL/TLS 证书响应该请求。
   + 如果没有匹配项，CloudFront 会改用 IP 地址来识别您的分配，并确定将哪个 SSL/TLS 证书返回给查看器。

1. 查看器和 CloudFront 使用您的 SSL/TLS 证书执行 SSL/TLS 协商。

1. CloudFront 将请求的内容返回到查看器。

此方法适用于每个 HTTPS 请求，无论用户使用的是浏览器还是其他查看器。

**注意**  
专用 IP 不是静态 IP，可能会随着时间的推移而变化。为边缘站点返回的 IP 地址是从 [CloudFront 边缘服务器列表](LocationsOfEdgeServers.md)的 IP 地址范围动态分配的。  
CloudFront 边缘服务器的 IP 地址范围可能会更改。要收到 IP 地址更改的通知，请[通过 Amazon SNS 订阅 AWS 公有 IP 地址更改](https://aws.amazon.com/blogs/aws/subscribe-to-aws-public-ip-address-changes-via-amazon-sns/)。

## 请求使用三个或更多专用 IP SSL/TLS 证书的权限
<a name="cnames-and-https-multiple-certificates"></a>

如果您需要权限将三个或更多 SSL/TLS 专用 IP 证书与 CloudFront 永久关联，请执行以下步骤。有关 HTTPS 请求的更多信息，请参阅[选择 CloudFront 如何处理 HTTPS 请求](#cnames-https-dedicated-ip-or-sni)。

**注意**  
此过程在您的多个 CloudFront 分配之间使用三个或三个以上的专用 IP 证书。默认值是 2。请记住，您不能将多个 SSL 证书绑定到一个分配。  
您一次只能将一个 SSL/TLS 证书与一个 CloudFront 分配关联。此数字是在您的所有 CloudFront 分配中可以使用的专用 IP SSL 证书的总数。<a name="cnames-and-https-multiple-certificates-procedure"></a>

**请求权限以在 CloudFront 分配中使用三个或更多证书**

1. 转到[支持中心](https://console.aws.amazon.com/support/home?#/case/create?issueType=service-limit-increase&limitType=service-code-cloudfront-distributions)并创建案例。

1. 注明您需要多少个证书的使用授权，并在请求中描述具体情况。我们将尽快更新您的账户。

1. 继续执行下一个过程。

# 在 CloudFront 中使用 SSL/TLS 证书的要求
<a name="cnames-and-https-requirements"></a>

本主题中介绍了 SSL/TLS 证书的要求。除非另有说明，否则这些要求均适用于以下两项：
+ 在查看器和 CloudFront 之间使用 HTTPS 的证书 
+ 在 CloudFront 和您的源之间使用 HTTPS 的证书

**Topics**
+ [证书颁发者](#https-requirements-certificate-issuer)
+ [用于 AWS Certificate Manager 的 AWS 区域](#https-requirements-aws-region)
+ [证书格式](#https-requirements-certificate-format)
+ [中间证书](#https-requirements-intermediate-certificates)
+ [密钥类型](#https-requirements-key-type)
+ [私有密钥](#https-requirements-private-key)
+ [权限](#https-requirements-permissions)
+ [证书密钥大小](#https-requirements-size-of-public-key)
+ [支持的证书类型](#https-requirements-supported-types)
+ [证书到期日期和续订](#https-requirements-cert-expiration)
+ [CloudFront 分配和证书中的域名](#https-requirements-domain-names-in-cert)
+ [最低 SSL/TLS 协议版本](#https-requirements-minimum-ssl-protocol-version)
+ [支持的 HTTP 版本](#https-requirements-supported-http-versions)

## 证书颁发者
<a name="https-requirements-certificate-issuer"></a>

建议您使用由 [AWS Certificate Manager（ACM）](https://aws.amazon.com/certificate-manager/)颁发的公有证书。有关从 ACM 获取证书的信息，请参阅 [AWS Certificate Manager 用户指南](https://docs.aws.amazon.com/acm/latest/userguide/)**。要在 CloudFront 分配中使用 ACM 证书，请确保您在美国东部（弗吉尼亚州北部）区域 (`us-east-1`) 中请求（或导入）该证书。

 CloudFront 与 Mozilla 支持相同的证书颁发机构 (CA)，因此如果您不使用 ACM，请使用 [Mozilla 包含的 CA 证书列表](https://wiki.mozilla.org/CA/Included_Certificates)中的 CA 颁发的证书。

您为 CloudFront 分配指定的源使用的 TLS 证书也需要从 Mozilla 包含的 CA 证书列表上的 CA 颁发。

有关获取和安装证书的更多信息，请参考 HTTP 服务器软件文档和 CA 文档。

## 用于 AWS Certificate Manager 的 AWS 区域
<a name="https-requirements-aws-region"></a>

要在 AWS Certificate Manager（ACM）中使用证书以要求在查看器和 CloudFront 之间使用 HTTPS，请确保您在美国东部（弗吉尼亚州北部）区域（`us-east-1`）中请求（或导入）该证书。

如果您需要在 CloudFront 和您的源之间使用 HTTPS，并且正在 Elastic Load Balancing 中使用负载均衡器作为源，则您可以在任何 AWS 区域中请求或导入证书。

## 证书格式
<a name="https-requirements-certificate-format"></a>

证书必须采用 X.509 PEM 格式。这是您使用 AWS Certificate Manager 时的原定设置格式。

## 中间证书
<a name="https-requirements-intermediate-certificates"></a>

如果您使用的是第三方证书颁发机构 (CA)，请在 `.pem` 文件的证书链中列出所有中间证书，从为您的域签署证书的 CA 所颁发的证书开始。通常，您将在以适当的链顺序列出中间证书和根证书的 CA 网站上找到文件。

**重要**  
请不要包括以下内容：根证书，未在信任路径中的中间证书，或者 CA 的公有密钥证书。

示例如下：

```
-----BEGIN CERTIFICATE-----
Intermediate certificate 2
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
Intermediate certificate 1
-----END CERTIFICATE-----
```

## 密钥类型
<a name="https-requirements-key-type"></a>

CloudFront 支持 RSA 和 ECDSA 公有/私有密钥对。

CloudFront 支持使用 RSA 和 ECDSA 证书连接到查看器和源的 HTTPS 连接。借助 [AWS Certificate Manager（ACM）](https://console.aws.amazon.com/acm)，您可以请求和导入 RSA 或 ECDSA 证书，然后将它们与您的 CloudFront 分配相关联。

有关 CloudFront 支持的您可以在 HTTPS 连接中协商的 RSA 和 ECDSA 密码的列表，请参阅[查看器和 CloudFront 之间支持的协议和密码](secure-connections-supported-viewer-protocols-ciphers.md)和[CloudFront 与源之间受支持的协议和密码](secure-connections-supported-ciphers-cloudfront-to-origin.md)。

## 私有密钥
<a name="https-requirements-private-key"></a>

如果您使用的是来自第三方证书颁发机构 (CA) 的证书，请注意以下几点：
+ 私有密钥必须匹配证书中的公有密钥。
+ 私有密钥必须采用 PEM 格式。
+ 私有密钥无法使用密码加密。

如果 AWS Certificate Manager (ACM) 提供了证书，则 ACM 不会发布该私有密钥。私有密钥存储在 ACM 中，供与 ACM 集成的 AWS 服务使用。

## 权限
<a name="https-requirements-permissions"></a>

您必须具备使用和导入 SSL/TLS 证书的权限。如果您使用 AWS Certificate Manager (ACM)，建议您使用 AWS Identity and Access Management 权限以限制访问证书。有关更多信息，请参阅《AWS Certificate Manager 用户指南》**中的[身份和访问权限管理](https://docs.aws.amazon.com/acm/latest/userguide/security-iam.html)。

## 证书密钥大小
<a name="https-requirements-size-of-public-key"></a>

CloudFront 支持的证书密钥大小取决于密钥和证书类型。

**对于 RSA 证书：**  
CloudFront 支持 1024 位、2048 位、3072 位和 4096 位 RSA 密钥。用于 CloudFront 的 RSA 证书的最大密钥长度为 4096 位。  
 请注意，ACM 颁发密钥最多为 2048 位的 RSA 证书。要使用 3072 位或 4096 位 RSA 证书，您需要从外部获取该证书并将其导入 ACM，之后即可将其与 CloudFront 一起使用。  
有关如何确定 RSA 密钥大小的信息，请参阅[确定 SSL/TLS RSA 证书中公有密钥的大小](cnames-and-https-size-of-public-key.md)。

**对于 ECDSA 证书：**  
CloudFront 支持 256 位密钥。要在 ACM 中使用 ECDSA 证书以要求在查看器和 CloudFront 之间使用 HTTPS，请使用 prime256v1 椭圆曲线。

## 支持的证书类型
<a name="https-requirements-supported-types"></a>

CloudFront 支持可信的证书颁发机构颁发的所有类型的证书。

## 证书到期日期和续订
<a name="https-requirements-cert-expiration"></a>

如果您使用来自第三方证书颁发机构 (CA) 的证书，则您必须监控证书到期日期并在到期前续订导入到 AWS Certificate Manager (ACM) 或上载到 AWS Identity and Access Management 证书存储的证书。

**重要**  
为避免发生证书过期问题，请在当前证书的 `NotAfter` 值之前至少 24 小时续订或重新导入证书。如果您的证书将在 24 小时内过期，请从 ACM 申请新证书或将新证书导入 ACM。接下来，将新证书关联到 CloudFront 分配。  
在您的证书续订或重新导入过程中，CloudFront 可能会继续使用以前的证书。这是个异步过程，CloudFront 最长可能需要 24 小时才能显示您的更改。

如果您使用 ACM 提供的证书，则 ACM 会为您管理证书续订。有关更多信息，请参阅《AWS Certificate Manager 用户指南》**中的[托管续订](https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html)。

## CloudFront 分配和证书中的域名
<a name="https-requirements-domain-names-in-cert"></a>

当您使用自定义源时，源上 SSL/TLS 证书的**公用名**字段中包含域名，**使用者备用名称**字段中可能包含更多域名。（CloudFront 支持证书域名中的通配符。） 

证书中的其中一个域名必须与您为“源域名”指定的域名匹配。如果没有任何域名匹配，CloudFront 会向查看器返回 HTTP 状态代码 `502 (Bad Gateway)`。

**重要**  
在将备用域名添加到分配时，CloudFront 会检查备用域名是否涵盖在您附加的证书中。证书必须在证书的使用者备用名称 (SAN) 字段中覆盖备用域名。这意味着，SAN 字段必须包含备用域名的完全匹配项，或者包含与要添加的备用域名级别相同的通配符。  
有关更多信息，请参阅[使用备用域名的要求](CNAMEs.md#alternate-domain-names-requirements)。

## 最低 SSL/TLS 协议版本
<a name="https-requirements-minimum-ssl-protocol-version"></a>

如果您使用的是专用 IP 地址，请通过选择安全策略来为查看器和 CloudFront 之间的连接选择最低 SSL/TLS 协议版本。

有关更多信息，请参阅 [安全策略（最低 SSL/TLS 版本）](DownloadDistValuesGeneral.md#DownloadDistValues-security-policy)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。

## 支持的 HTTP 版本
<a name="https-requirements-supported-http-versions"></a>

如果您将一个证书与多个 CloudFront 分配关联，则与证书关联的所有分配都必须针对[支持的 HTTP 版本](DownloadDistValuesGeneral.md#DownloadDistValuesSupportedHTTPVersions)使用相同的选项。您在创建或更新 CloudFront 分配时指定此选项。

# 在 CloudFront 中使用 SSL/TLS 证书的配额（仅在查看器和 CloudFront 之间使用 HTTPS）
<a name="cnames-and-https-limits"></a>

请注意在 CloudFront 中使用 SSL/TLS 证书的以下配额。这些配额仅适用于您使用 AWS Certificate Manager (ACM) 预置的 SSL/TLS 证书，或导入 ACM 或上传到 IAM 证书存储供查看器和 CloudFront 之间进行 HTTPS 通信的 SSL/TLS 证书。

有关更多信息，请参阅 [增加 SSL/TLS 证书的配额](increasing-the-limit-for-ssl-tls-certificates.md)。

**每个 CloudFront 分配的最大证书数量**  
最多可以将一个 SSL/TLS 证书与每个 CloudFront 分配关联。

**您可以导入 ACM 或上载到 IAM 证书存储的证书的最大数量**  
如果您从第三方 CA 获得了 SSL/TLS 证书，则必须在下列位置之一存储证书：  
+ **AWS Certificate Manager** – 有关 ACM 证书数量的当前配额，请参阅《AWS Certificate Manager 用户指南》**中的[配额](https://docs.aws.amazon.com/acm/latest/userguide/acm-limits.html)。列出的限制是总数，包括您使用 ACM 预置的证书以及您导入 ACM 的证书。
+ **IAM 证书存储** – 有关您可以上载到 IAM 证书存储以供 AWS 账户使用的证书数的当前配额（以前称为限制），请参阅《IAM 用户指南》**中的 [IAM 和 STS 限制](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html)。您可以在服务配额控制台中申请更高的配额。

**每个 AWS 账户的证书的最大数量（仅限专用 IP 地址）**  
如果要使用专用 IP 地址处理 HTTPS 请求，请注意以下几点：  
+ 默认情况下，CloudFront 向您授权以供您的 AWS 账户使用两个证书，一个用于日常使用，另一个用于您需要轮换证书以满足多个分配的情况。
+ 如果您的 AWS 账户需要两个以上的自定义 SSL/TLS 证书，则可以在服务配额控制台中申请更高的配额。

**对使用不同AWS账户创建的 CloudFront 分配使用同一个证书**  
如果您使用的是第三方 CA，并且希望针对不同 AWS 账户创建的多个 CloudFront 分配使用同一个证书，则必须将证书导入 ACM，或者为每个 AWS 账户将其上传至 IAM 证书存储一次。  
如果您使用的是 ACM 提供的证书，则不能将 CloudFront 配置为使用其他 AWS 账户创建的证书。

**为 CloudFront 和其他AWS服务使用同一个证书**  
如果您从信任的证书颁发机构 (如 Comodo、DigiCert 或 Symantec) 购买了证书，则可以对 CloudFront 和其他 AWS 服务使用同一个证书。如果要将证书导入到 ACM，您只需要导入一次即可供多个 AWS 服务使用。  
如果您使用的是 ACM 提供的证书，这些证书会存储在 ACM 中。

**为多个 CloudFront 分配使用同一个证书**  
对于任何或所有您正用来提供 HTTPS 请求的 CloudFront 分配，您都可以使用同一个证书。请注意以下几点：  
+ 您可以使用同一个证书来使用专用 IP 地址处理请求以及使用 SNI 处理请求。
+ 只能将一个证书与每个分配关联。
+ 每个分配必须包含一个或多个备用域名，这些域名也会出现在证书的**公用名**字段或**主题备用名称**字段中。
+ 如果您正在使用专用 IP 地址提供 HTTPS 请求并且已使用同一个 AWS 账户创建您的所有分配，您可以为所有分配使用同一个证书，这样可以显著降低成本。CloudFront 对每个证书而不是每个分配收费。

  例如，假设您使用同一个 AWS 账户创建了三个分配，并且您对所有三个分配使用同一个证书。将仅针对您使用专用 IP 地址计算一次费用。

  但是，如果您正在使用专用 IP 地址处理 HTTPS 请求并且正在使用同一个证书在不同的 AWS 账户中创建 CloudFront 分配，则会向每个账户收取专用 IP 地址的使用费。例如，如果您使用三个不同的 AWS 账户创建三个分配，并且您为所有三个分配使用同一个证书，则向每个账户收取专用 IP 地址的完整使用费。

# 配置备用域名和 HTTPS
<a name="cnames-and-https-procedures"></a>

要在您的文件的 URL 中使用备用域名并在查看器和 CloudFront 之间使用 HTTPS，请执行适用步骤。

**Topics**
+ [获取 SSL/TLS 证书](#cnames-and-https-getting-certificates)
+ [导入 SSL/TLS 证书](#cnames-and-https-uploading-certificates)
+ [更新 CloudFront 分配](#cnames-and-https-updating-cloudfront)

## 获取 SSL/TLS 证书
<a name="cnames-and-https-getting-certificates"></a>

如果您还没有 SSL/TLS 证书，请获取一个。有关更多信息，请参阅相应文档：
+ 要使用 AWS Certificate Manager (ACM) 提供的证书，请参阅 [AWS Certificate Manager 用户指南](https://docs.aws.amazon.com/acm/latest/userguide/)。然后跳至 [更新 CloudFront 分配](#cnames-and-https-updating-cloudfront)。
**注意**  
建议您使用 ACM 在 AWS 托管资源上预置、管理和部署 SSL/TLS 证书。您必须在美国东部（弗吉尼亚州北部）区域申请 ACM 证书。
+ 要从第三方证书颁发机构 (CA) 获取证书，请参阅该证书颁发机构提供的文档。当您拥有该证书时，请继续执行下一个步骤。

## 导入 SSL/TLS 证书
<a name="cnames-and-https-uploading-certificates"></a>

如果您从第三方 CA 得到证书，请将证书导入 ACM，或者上传到 IAM 证书存储：

**ACM（推荐）**  
ACM 可让您从 ACM 控制台以及以编程方式导入第三方证书。有关将证书导入 ACM 的信息，请参阅《AWS Certificate Manager 用户指南》**中的[将证书导入 AWS Certificate Manager](https://docs.aws.amazon.com/acm/latest/userguide/import-certificate.html)。您必须在美国东部（弗吉尼亚州北部）区域导入证书。

**IAM 证书存储**  
（不推荐）请使用以下 AWS CLI 命令将您的第三方证书上载到 IAM 证书存储。  

```
aws iam upload-server-certificate \
        --server-certificate-name CertificateName \
        --certificate-body file://public_key_certificate_file \
        --private-key file://privatekey.pem \
        --certificate-chain file://certificate_chain_file \
        --path /cloudfront/path/
```
请注意以下几点：  
+ **AWS 账户** – 您必须使用创建 CloudFront 分配时使用的同一个 AWS 账户将证书上传到 IAM 证书存储。
+ **--path 参数** – 在将证书上传到 IAM 时，`--path` 参数（证书路径）的值必须以 `/cloudfront/` 开头，例如 `/cloudfront/production/` 或 `/cloudfront/test/`。该路径必须以 / 结尾。
+ **现有的证书** – 您必须指定 `--server-certificate-name` 和 `--path` 参数的值，这些值不同于与现有证书关联的值。
+ **使用 CloudFront 控制台** – 您在 AWS CLI 中为 `--server-certificate-name` 参数指定的值（例如 `myServerCertificate`）显示在 CloudFront 控制台的 **SSL 证书**列表中。
+ **使用 CloudFront API** – 记下 AWS CLI 返回的字母数字字符串，例如 `AS1A2M3P4L5E67SIIXR3J`。这是您将在 `IAMCertificateId` 元素中指定的值。您无需 IAM ARN（也由 CLI 返回）。
有关 AWS CLI 的更多信息，请参阅 [AWS Command Line Interface 用户指南](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)和 [AWS CLI 命令参考](https://docs.aws.amazon.com/cli/latest/reference/)。

## 更新 CloudFront 分配
<a name="cnames-and-https-updating-cloudfront"></a>

要更新您的分配的设置，请执行以下步骤：<a name="cnames-and-https-updating-cloudfront-procedure"></a>

**为备用域名配置您的 CloudFront 分配**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 为要更新的分配选择 ID。

1. 在 **General** 选项卡上，选择 **Edit**。

1. 更新以下值：  
**备用域名（CNAME）**  
选择**添加项目**，添加适用的备用域名。用逗号隔开多个域名，或在新行中键入每个域名。  
**自定义 SSL 证书**  
从下拉列表中选择证书。  
此处列出了多达 100 个证书。如果您有超过 100 个证书并且您没有看到要添加的证书，则可以在字段中键入一个证书 ARN 以选择它。  
如果您已将证书上传到 IAM 证书存储，但该证书没有被列出，并且您无法通过在字段中键入名称来选择它，请查看过程 [导入 SSL/TLS 证书](#cnames-and-https-uploading-certificates)以确认您已正确上传证书。  
在将您的 SSL/TLS 证书与您的 CloudFront 分配关联之后，在您从所有分配中删除该证书并且已部署所有分配之前，请勿从 ACM 或 IAM 证书存储中删除该证书。

1. 选择**保存更改**。

1. 将 CloudFront 配置为要求查看器与 CloudFront 之间的通信为 HTTPS：

   1. 在**行为**选项卡中，选择要更新的缓存行为，然后选择**编辑**。

   1. 指定 **Viewer Protocol Policy** 的以下值之一：  
**将 HTTP 重定向到 HTTPS**  
查看器可使用两种协议，但 HTTP 请求将自动重定向到 HTTPS 请求。CloudFront 将返回 HTTP 状态代码 `301 (Moved Permanently)` 以及新的 HTTPS URL。然后，查看器会使用此 HTTPS URL 将请求重新提交到 CloudFront。  
CloudFront 不将 `DELETE`、`OPTIONS`、`PATCH`、`POST` 或 `PUT` 请求从 HTTP 重定向到 HTTPS。如果您将一个缓存行为配置为重定向到 HTTPS，CloudFront 会针对该缓存行为的 HTTP `DELETE`、`OPTIONS`、`PATCH`、`POST` 或 `PUT` 请求响应 HTTP 状态代码 `403 (Forbidden)`。
在查看器发出将重定向到 HTTPS 请求的 HTTP 请求时，会产生针对这两个请求的 CloudFront 费用。对于 HTTP 请求，仅对该请求和 CloudFront 返回到查看器的标头计费。对于 HTTPS 请求，对该请求、标头和由您的源返回的文件计费。  
**仅 HTTPS**  
查看器只有使用 HTTPS 才能访问您的内容。如果查看器发送 HTTP 请求而不是 HTTPS 请求，则 CloudFront 将返回 HTTP 状态代码 `403 (Forbidden)` 且不会返回此文件。

   1. 选择**是，编辑**。

   1. 针对要求在查看器和 CloudFront 之间使用 HTTPS 的其他每个缓存行为，重复步骤 a 到 c。

1. 请确认以下内容，然后在生产环境中使用更新后的配置：
   + 每个缓存行为中的路径模式仅适用于您希望查看器使用 HTTPS 的请求。
   + 缓存行为按您希望 CloudFront 评估它们的顺序列出。有关更多信息，请参阅 [路径模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)。
   + 缓存行为将请求路由到正确的源。

# 确定 SSL/TLS RSA 证书中公有密钥的大小
<a name="cnames-and-https-size-of-public-key"></a>

如果您使用的是 CloudFront 备用域名和 HTTPS，则 SSL/TLS RSA 证书中公有密钥的最大大小为 4096 位。(这是密钥大小，不是指公有密钥中的字符数。) 在使用 AWS Certificate Manager 提供您的证书的情况下，虽然 ACM 支持更大的 RSA 密钥，但您无法在 CloudFront 中使用更大的密钥。

您可以通过运行以下 OpenSSL 命令来确定 RSA 公有密钥的大小：

```
openssl x509 -in path and filename of SSL/TLS certificate -text -noout 
```

其中：
+ `-in` 指定您的 SSL/TLS RSA 证书的路径和文件名。
+ `-text` 使 OpenSSL 以位为单位显示 RSA 公有密钥的长度。
+ `-noout` 阻止 OpenSSL 显示公有密钥。

输出示例：

```
Public-Key: (2048 bit)
```

# 增加 SSL/TLS 证书的配额
<a name="increasing-the-limit-for-ssl-tls-certificates"></a>

您可以导入到 AWS Certificate Manager（ACM）或上传至 AWS Identity and Access Management（IAM）的 SSL/TLS 证书的数量存在配额。在将 CloudFront 配置为使用专用 IP 地址处理 HTTPS 请求时，可以在一个AWS 账户中使用的 SSL/TLS 证书数也存在配额。但是，您可以请求提高配额。

**Topics**
+ [增加导入 ACM 的证书的配额](#certificates-to-import-into-acm)
+ [增加上传至 IAM 的证书的配额](#certificates-to-upload-into-iam)
+ [增加用于专用 IP 地址的证书配额](#certificates-using-dedicated-ip-address)

## 增加导入 ACM 的证书的配额
<a name="certificates-to-import-into-acm"></a>

有关可以导入到 ACM 的证书数量的配额，请参阅《AWS Certificate Manager 用户指南》**中的[配额](https://docs.aws.amazon.com/acm/latest/userguide/acm-limits.html)。

要申请更高的配额，您可以使用服务配额控制台。有关更多信息，请参阅《服务配额用户指南》**中的 [Requesting a quota increase](https://docs.aws.amazon.com/servicequotas/latest/userguide/request-quota-increase.html)。

## 增加上传至 IAM 的证书的配额
<a name="certificates-to-upload-into-iam"></a>

有关可上传到 IAM 的证书数量的配额（以前称为限制），请参阅《IAM 用户指南》**中的 [IAM 和 STS 限制](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html)。

要申请更高的配额，您可以使用服务配额控制台。有关更多信息，请参阅《服务配额用户指南》**中的 [Requesting a quota increase](https://docs.aws.amazon.com/servicequotas/latest/userguide/request-quota-increase.html)。

## 增加用于专用 IP 地址的证书配额
<a name="certificates-using-dedicated-ip-address"></a>

在使用专用 IP 地址处理 HTTPS 请求时，有关可用于每个AWS 账户的 SSL 证书的数量配额，请参阅 [SSL 证书的配额](cloudfront-limits.md#limits-ssl-certificates)。

要申请更高的配额，您可以使用服务配额控制台。有关更多信息，请参阅《服务配额用户指南》**中的 [Requesting a quota increase](https://docs.aws.amazon.com/servicequotas/latest/userguide/request-quota-increase.html)。

# 轮换 SSL/TLS 证书
<a name="cnames-and-https-rotate-certificates"></a>

当 SSL/TLS 证书即将到期时，您需要轮换它们，以确保分配的安全性，并避免查看器出现服务中断。可以通过以下方式轮换证书：
+ 对于 AWS Certificate Manager（ACM）提供的 SSL/TLS 证书，无需轮换。ACM *自动* 为您管理证书续订。有关更多信息，请参阅《AWS Certificate Manager User Guide》**中的 [Managed certificate renewal](https://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html)。
+ 如果您使用的是第三方证书颁发机构，并且已将证书导入到 ACM（推荐）或上传到 IAM 证书存储中，则有时必须将一个证书更换为另一个证书。

  

**重要**  
对于您从第三方证书颁发机构获取并导入 ACM 的证书，ACM 不会为其管理证书续订。
如果您将 CloudFront 配置为使用专用 IP 地址提供 HTTPS 请求，就您在轮换证书时使用一个或多个其他证书，可能会产生额外的按比例分摊的费用。建议您更新分配，以最大程度地降低额外费用。

## 轮换 SSL/TLS 证书
<a name="rotate-ssl-tls-certificate"></a>

要轮换证书，请执行以下过程。在您轮换证书以及轮换过程完成时，查看器可以继续访问您的内容。<a name="rotate-ssl-tls-certificates-proc"></a>

**轮换 SSL/TLS 证书**

1. [增加 SSL/TLS 证书的配额](increasing-the-limit-for-ssl-tls-certificates.md)以确定您是否需要权限来使用更多的 SSL 证书。如果需要，可请求权限并等待授予权限，然后继续执行步骤 2。

1. 将新证书导入 ACM 或者上传到 IAM。有关更多信息，请参阅《Amazon CloudFront 开发人员指南》**中的[导入 SSL/TLS 证书](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-procedures.html#cnames-and-https-uploading-certificates)。

1. （仅适用于 IAM 证书）请一次更新一项分配，使其使用新证书。有关更多信息，请参阅 [更新分配](HowToUpdateDistribution.md)。

1. （可选）从 ACM 或 IAM 中删除之前的证书。
**重要**  
在将 SSL/TLS 证书从所有分配中移除并且已更新的分配的状态变为 `Deployed` 之前，请勿删除该证书。

# 从自定义 SSL/TLS 证书恢复为默认 CloudFront 证书
<a name="cnames-and-https-revert-to-cf-certificate"></a>

如果您将 CloudFront 配置为在查看器和 CloudFront 之间使用 HTTPS，并将 CloudFront 配置为使用自定义 SSL/TLS 证书，则可以更改配置以使用默认的 CloudFront SSL/TLS 证书。此过程取决于您是否已使用分配来分发内容：
+ 如果您尚未使用分配来分发内容，则只需更改配置。有关更多信息，请参阅 [更新分配](HowToUpdateDistribution.md)。
+ 如果您已使用分配来分发内容，则必须创建新的 CloudFront 分配并更改文件的 URL，以便减少或消除内容不可用的时间。为此，请执行以下步骤。

## 恢复到默认 CloudFront 证书
<a name="revert-default-cloudfront-certificate"></a>

以下步骤演示如何从自定义 SSL/TLS 证书恢复为默认 CloudFront 证书。<a name="cnames-and-https-revert-to-cf-certificate-proc"></a>

**恢复到默认 CloudFront 证书**

1. 采用所需的配置创建一项新的 CloudFront 分配。对于 **SSL 证书**，请选择**默认 CloudFront 证书 (\$1.cloudfront.net)**。

   有关更多信息，请参阅 [创建分配](distribution-web-creating-console.md)。

1. 对于使用 CloudFront 分配的文件，请将您应用程序中的 URL 更新为使用 CloudFront 为新分配指定的域名。例如，将 `https://www.example.com/images/logo.png` 更改为 `https://d111111abcdef8.cloudfront.net/images/logo.png`。

1. 删除与自定义 SSL/TLS 证书关联的分配，或者更新分配以将 **SSL 证书**值更改为**默认 CloudFront 证书 (\$1.cloudfront.net)**。有关更多信息，请参阅 [更新分配](HowToUpdateDistribution.md)。
**重要**  
在您完成此步骤之前，AWS 会继续向您收取使用自定义 SSL/TLS 证书的费用。

1. （可选）删除您的自定义 SSL/TLS 证书。

   1. 运行 AWS CLI 命令 `list-server-certificates` 以获取要删除的证书的证书 ID。有关更多信息，请参阅 *AWS CLI命令参考*中的 [list-server-certificates](https://docs.aws.amazon.com/cli/latest/reference/iam/list-server-certificates.html)。

   1. 运行 AWS CLI 命令 `delete-server-certificate` 以删除该证书。有关更多信息，请参阅《AWS CLI 命令参考》**中的 [delete-server-certificate](https://docs.aws.amazon.com/cli/latest/reference/iam/delete-server-certificate.html)。

# 将使用专用 IP 地址的自定义 SSL/TLS 证书切换到 SNI
<a name="cnames-and-https-switch-dedicated-to-sni"></a>

如果您已将 CloudFront 配置为使用自定义 SSL/TLS 证书及专用 IP 地址，可以改为切换到使用自定义 SSL/TLS 证书及 SNI，并消除与专用 IP 地址关联的费用。

**重要**  
对您的 CloudFront 配置进行此更新不会影响支持 SNI 的查看器。查看器既可以在更改前后访问您的内容，也可以在将更改传播到 CloudFront 边缘站点时访问您的内容。不支持 SNI 的查看器在更改后无法访问您的内容。有关更多信息，请参阅 [选择 CloudFront 如何处理 HTTPS 请求](cnames-https-dedicated-ip-or-sni.md)。

## 从自定义证书切换到 SNI
<a name="cloudfront-switch-custom-cert-sni"></a>

以下步骤演示如何将使用专用 IP 地址的自定义 SSL/TLS 证书切换到 SNI。<a name="cnames-and-https-switch-dedicated-to-sni-proc"></a>

**使用专用 IP 地址从自定义 SSL/TLS 证书切换到 SNI**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择要查看或更新的分配的 ID。

1. 选择**分配设置**。

1. 在 **General** 选项卡上，选择 **Edit**。

1. 在**自定义 SSL 证书 – *可选***下，取消选择**传统客户端支持**。

1. 选择**是，编辑**。

# CloudFront 的双向 TLS 身份验证（查看器 mTLS）
<a name="mtls-authentication"></a>

双向 TLS 身份验证（双向传输层安全身份验证，mTLS）是一种安全协议，它在标准 TLS 身份验证的基础上进行了扩展，要求进行基于证书的双向身份验证，在该机制下，客户端与服务器必须先证实自己的身份，然后才能建立安全连接。借助双向 TLS，您可以确保只有提供了可信 TLS 证书的客户端，才能获得对 CloudFront 分配的访问权限。

## 工作原理
<a name="how-mtls-works"></a>

在标准 TLS 握手过程中，只有服务器需要提供证书来向客户端证明其身份。借助双向 TLS，身份验证过程会变为双向验证。当客户端尝试连接到您的 CloudFront 分配时，CloudFront 会在 TLS 握手过程中请求客户端证书。在建立安全连接之前，客户端必须提供有效的 X.509 证书，CloudFront 会根据您配置的信任存储来验证该证书。

CloudFront 在 AWS 边缘站点执行此证书验证操作，这既能将身份验证的复杂性从原始服务器中剥离，又能保留 CloudFront 的全球性能优势。您可以在两种模式下配置 mTLS：验证模式（要求所有客户端提供有效证书）或可选模式（在客户端提供证书时对证书进行验证，但也允许无证书的连接）。

## 使用案例
<a name="mtls-use-cases"></a>

CloudFront 的双向 TLS 身份验证可应对传统身份验证方法难以满足需求的关键安全场景：
+ **使用内容缓存进行设备身份验证**：您可以要求游戏主机、IoT 设备或公司硬件需先通过身份验证，之后才能访问固件更新、游戏下载资源或内部资源。每台设备均包含一个唯一证书，该证书既能证明设备的真实性，又能获益于 CloudFront 的缓存功能。
+ **API 到 API 身份验证**：可用于保障可信业务合作伙伴之间、支付系统之间或微服务之间的机器对机器通信的安全。基于证书的身份验证不仅消除了对共享密钥或 API 密钥的需求，而且为自动化数据交换提供了强大的身份验证能力。

**Topics**
+ [工作原理](#how-mtls-works)
+ [使用案例](#mtls-use-cases)
+ [信任存储和证书管理](trust-stores-certificate-management.md)
+ [为 CloudFront 分配启用双向 TLS](enable-mtls-distributions.md)
+ [关联 CloudFront 连接函数](connection-functions.md)
+ [配置其他设置](configuring-additional-settings.md)
+ [用于缓存策略并转发到源的查看器 mTLS 标头](viewer-mtls-headers.md)
+ [使用 CloudFront 连接函数和 KVS 实施吊销](revocation-connection-function-kvs.md)
+ [使用连接日志实现可观测性](connection-logs.md)

# 信任存储和证书管理
<a name="trust-stores-certificate-management"></a>

要对 CloudFront 实施双向 TLS 身份验证，必须创建并配置信任存储。信任存储包含证书颁发机构（CA）证书，CloudFront 在身份验证过程中使用这些证书来验证客户端证书。

## 什么是信任存储？
<a name="what-is-trust-store"></a>

信任存储是 CA 证书的存储库，CloudFront 在双向 TLS 身份验证过程中使用这些证书来验证客户端证书。信任存储包含根证书和中间 CA 证书，这些证书构成了用于验证客户端证书的信任链。

在对 CloudFront 实施双向 TLS 时，信任存储会指定您可信任哪些证书颁发机构来颁发有效的客户端证书。在 TLS 握手过程中，CloudFront 会根据信任存储对每个客户端证书进行验证。客户端只有在提供了链接到信任存储中的某个 CA 的证书时，才能成功完成身份验证。

CloudFront 中的信任存储是账户级资源，可将其与多个分配关联。这可让您在整个 CloudFront 部署中保持一致的证书验证策略，并简化 CA 证书管理。

## 证书颁发机构支持
<a name="ca-support"></a>

CloudFront 支持由 AWS 私有证书颁发机构和第三方私有证书颁发机构颁发的证书。这让您可以根据组织要求，灵活地选择使用现有证书基础设施或者利用 AWS 托管证书服务。
+ **AWS 私有证书颁发机构：**您可以使用由 AWS 私有 CA 颁发的证书，这提供了托管私有证书颁发机构服务。此集成既简化了证书生命周期管理，又提供了与其他 AWS 服务的无缝集成。
+ **第三方私有证书颁发机构：**您也可以使用来自现有的私有证书颁发机构基础设施的证书，包括企业 CA 或其他第三方证书提供商。这可让您在添加 CloudFront 的 mTLS 功能的同时保留当前证书管理流程。

## 证书要求和规范
<a name="certificate-requirements"></a>

信任存储对自身包含的 CA 证书有特定的要求：

### CA 证书格式要求
<a name="ca-cert-format-requirements"></a>
+ **格式：**PEM（隐私增强邮件）格式
+ **内容边界：**证书必须包含在 -----BEGIN CERTIFICATE----- 和 -----END CERTIFICATE----- 边界内
+ **注释：**必须以 \$1 字符开头，并且不得包含任何 - 字符
+ **换行符：**证书之间不允许存在空行

### 支持的证书规范
<a name="supported-cert-specs"></a>
+ **证书类型：**X.509v3
+ **公有密钥类型：**
  + RSA 2048、RSA 3072、RSA 4096
  + ECDSA：secp256r1、secp384r1
+ **签名算法：**
  + 采用 RSA 的 SHA256、SHA384、SHA512
  + 采用 EC 的 SHA256、SHA384、SHA512
  + 采用 RSASSA-PSS 与 MGF1 的 SHA256、SHA384、SHA512

### 证书捆绑包格式示例
<a name="example-cert-bundle"></a>

多个证书（PEM 编码）：

```
# Root CA Certificate
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKoK/OvD/XqiMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcwNzEyMTU0NzQ4WhcNMjcwNzEwMTU0NzQ4WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAuuExKvY1xzHFylsHiuowqpmzs7rEcuuylOuEszpFp+BtXh0ZuEtts9LP
-----END CERTIFICATE-----
# Intermediate CA Certificate
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKoK/OvD/XqjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcwNzEyMTU0NzQ4WhcNMjcwNzEwMTU0NzQ4WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAuuExKvY1xzHFylsHiuowqpmzs7rEcuuylOuEszpFp+BtXh0ZuEtts9LP
-----END CERTIFICATE-----
```

## 创建信任存储
<a name="create-trust-store"></a>

在创建信任存储之前，您必须将 PEM 格式的 CA 证书捆绑包上传到 Amazon S3 存储桶。此证书捆绑包应包含验证客户端证书所需的所有受信任的根证书和中间 CA 证书。

在创建信任存储时，仅从 S3 中读取一次 CA 证书捆绑包。如果后续对 CA 证书捆绑包进行修改，则需手动更新信任存储。信任存储和 S3 CA 证书捆绑包之间不会保持同步。

### 先决条件
<a name="trust-store-prerequisites"></a>
+ 来自证书颁发机构（CA）的证书捆绑包已上传到 Amazon S3 存储桶
+ 创建 CloudFront 资源所需的权限

### 创建信任存储（控制台）
<a name="create-trust-store-console"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**信任存储**。

1. 选择**创建信任存储**。

1. 对于**信任存储名称**，输入信任存储的名称。

1. 对于**证书颁发机构（CA）捆绑包**，输入 PEM 格式的 CA 证书捆绑包的 Amazon S3 路径。

1. 选择**创建信任存储**。

### 创建信任存储（AWS CLI）
<a name="create-trust-store-cli"></a>

```
aws cloudfront create-trust-store \
  --name MyTrustStore \
  --certificate-authority-bundle-s3-location Bucket=my-bucket,Key=ca-bundle.pem \
  --tags Items=[{Key=Environment,Value=Production}]
```

## 将信任存储与分配关联
<a name="associate-trust-store"></a>

创建信任存储后，您必须将其与 CloudFront 分配关联来启用双向 TLS 身份验证。

### 先决条件
<a name="associate-prerequisites"></a>
+ 现有 CloudFront 分配，已启用仅 HTTPS 查看器协议策略并且禁用了 HTTP3 支持。

### 关联信任存储（控制台）
<a name="associate-trust-store-console"></a>

在 CloudFront 控制台中，可通过以下两个途径关联信任存储：信任存储详细信息页面或分配设置页面。

**通过信任存储详细信息页面关联信任存储：**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**信任存储**。

1. 选择要关联的信任存储的名称。

1. 选择**关联到分配**。

1. 配置可用的查看器 mTLS 选项：
   + **客户端证书验证模式：**在“必需”模式和“可选”模式之间进行选择。在必需模式下，所有客户端都需要提供证书。在可选模式下，系统会对提供证书的客户端进行验证，而未提供证书的客户端也允许访问。
   + **公布信任存储 CA 名称：**选择在 TLS 握手过程中，是否向客户端公布信任存储中的 CA 名称。
   + **忽略证书到期日期：**选择是否允许使用到期证书建立连接（其他验证标准仍将适用）。
   + **连接函数：**可以关联一个可选的连接函数，来基于其他自定义标准允许/拒绝连接。

1. 选择要与信任存储关联的一个或多个分配。分配只有在禁用了 HTTP3 且采用仅 HTTPS 缓存行为后，才能够支持查看器 mTLS。

1. 选择**关联**。

**通过分配设置页面关联信任存储：**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择要关联的分配

1. 在**常规**选项卡下的**设置**容器中，选择右上角的**编辑**

1. 向下滚动到页面底部，在**连接性**容器中，打开**查看器 mTLS** 开关

1. 配置可用的查看器 mTLS 选项：
   + **客户端证书验证模式：**在“必需”模式和“可选”模式之间进行选择。在必需模式下，所有客户端都需要提供证书。在可选模式下，系统会对提供证书的客户端进行验证，而未提供证书的客户端也允许访问。
   + **公布信任存储 CA 名称：**选择在 TLS 握手过程中，是否向客户端公布信任存储中的 CA 名称。
   + **忽略证书到期日期：**选择是否允许使用到期证书建立连接（其他验证标准仍将适用）。
   + **连接函数：**可以关联一个可选的连接函数，来基于其他自定义标准允许/拒绝连接。

1. 选择右下角的**保存更改**。

### 关联信任存储（AWS CLI）
<a name="associate-trust-store-cli"></a>

通过 DistributionConfig.ViewerMtlsConfig 属性可以将信任存储与分配相关联。这意味着我们首先需要获取分配配置，然后在后续的 UpdateDistribution 请求中提供 ViewerMtlsConfig。

```
// First fetch the distribution
aws cloudfront get-distribution {DISTRIBUTION_ID}

// Update the distribution config, for example:
Distribution config, file://distConf.json: 
{
  ...other fields,
  ViewerMtlsConfig: {
    Mode: 'required',
    TrustStoreConfig: {
        AdvertiseTrustStoreCaNames: false,
        IgnoreCertificateExpiry: true,
        TrustStoreId: {TRUST_STORE_ID}
    }
  }
}

aws cloudfront update-distribution \
   --id {DISTRIBUTION_ID} \
   --if-match {ETAG} \
   --distribution-config file://distConf.json
```

## 管理信任存储
<a name="manage-trust-stores"></a>

### 查看信任存储详细信息
<a name="view-trust-store-details"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**信任存储**。

1. 选择信任存储的名称以查看其详细信息页面。

详细信息页面将显示以下内容：
+ 信任存储名称和 ID
+ CA 证书数量
+ 创建日期和上次修改日期
+ 关联的分配
+ 标签

### 修改信任存储
<a name="modify-trust-store"></a>

要替换 CA 证书捆绑包，请执行以下操作：

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**信任存储**。

1. 选择信任存储的名称。

1. 选择**操作**，然后选择**编辑**。

1. 对于**证书颁发机构（CA）捆绑包**，输入更新后的 CA 捆绑包 PEM 文件的 Amazon S3 位置。

1. 选择**更新信任存储**。

### 删除信任存储
<a name="delete-trust-store"></a>

**先决条件：**您必须先取消信任存储与所有 CloudFront 分配的关联。

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**信任存储**。

1. 选择信任存储的名称。

1. 选择**删除信任存储**。

1. 选择**删除**以确认。

### 后续步骤
<a name="trust-store-next-steps"></a>

在创建信任存储并将其与 CloudFront 分配关联后，您可以继续在分配中启用双向 TLS 身份验证并配置其他设置，例如将证书标头转发到源。有关在分配中启用 mTLS 的详细说明，请参阅[为 CloudFront 分配启用双向 TLS](enable-mtls-distributions.md)。

# 为 CloudFront 分配启用双向 TLS
<a name="enable-mtls-distributions"></a>

## 先决条件和要求
<a name="mtls-prerequisites-requirements"></a>

CloudFront 的双向 TLS 验证模式要求所有客户端在 TLS 握手过程中提供有效证书，并拒绝未使用有效证书的连接。在 CloudFront 分配上启用双向 TLS 之前，请确保：
+ 已使用证书颁发机构证书创建信任存储
+ 已将信任存储与 CloudFront 分配关联
+ 所有分配缓存行为都使用仅 HTTPS 查看器协议策略
+ 分配使用的是 HTTP/2（默认设置，HTTP/3 不支持查看器 mTLS）

**注意**  
双向 TLS 身份验证要求在查看器和 CloudFront 之间建立 HTTPS 连接。您无法在具有任何支持 HTTP 连接的缓存行为的分配上启用 mTLS。

## 启用双向 TLS（控制台）
<a name="enable-mtls-console"></a>

### 对于新的分配
<a name="enable-mtls-new-distributions"></a>

在 CloudFront 控制台中创建新分配的过程中，无法配置查看器 mTLS。首先，通过任意方式（控制台、CLI、API）创建分配，然后根据以下现有分配说明操作，编辑分配设置以启用查看器 mTLS。

### 对于现有分配
<a name="enable-mtls-existing-distributions"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 从分配列表中，选择要修改的分配。

1. 确保将所有缓存行为的查看器协议策略设置为**将 HTTP 重定向到 HTTPS** 或**仅 HTTPS**。（您可以选择**缓存行为**选项卡，查看和更新任何使用 HTTP 协议策略的缓存行为。）

1. 选择**常规**选项卡。

1. 在**设置**部分中，选择**编辑**。

1. 在**连接性**部分中，找到**查看器双向身份验证(mTLS)**。

1. 将**启用双向身份验证**切换为“开启”状态。

1. 对于**客户端证书验证模式**，选择**必需**（所有客户端都必须提供证书）或**可选**（客户端可以选择性地提供证书）。

1. 对于**信任存储**，选择您之前创建的信任存储。

1. （可选）如果您希望 CloudFront 在 TLS 握手过程中向客户端发送 CA 名称，请切换**公布信任存储 CA 名称**。

1. （可选）如果您想允许使用到期证书进行连接，请切换**忽略证书到期日期**。

1. 选择**保存更改**。

## 启用双向 TLS（AWS CLI）
<a name="enable-mtls-cli"></a>

### 对于新的分配
<a name="enable-mtls-cli-new"></a>

以下示例演示如何创建包含 mTLS 设置的分配配置文件（distribution-config.json）：

```
{
  "CallerReference": "cli-example-1",
  "Origins": {
    "Quantity": 1,
    "Items": [
      {
        "Id": "my-origin",
        "DomainName": "example.com",
        "CustomOriginConfig": {
          "HTTPPort": 80,
          "HTTPSPort": 443,
          "OriginProtocolPolicy": "https-only"
        }
      }
    ]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "my-origin",
    "ViewerProtocolPolicy": "https-only",
    "MinTTL": 0,
    "ForwardedValues": {
      "QueryString": false,
      "Cookies": {
        "Forward": "none"
      }
    }
  },
  "ViewerCertificate": {
    "CloudFrontDefaultCertificate": true
  },
  "ViewerMtlsConfig": {
    "Mode": "required", 
    "TrustStoreConfig": {
        "TrustStoreId": {TRUST_STORE_ID},
        "AdvertiseTrustStoreCaNames": true,
        "IgnoreCertificateExpiry": true
    }
  },
  "Enabled": true
}
```

使用以下示例命令创建启用了 mTLS 的分配：

```
aws cloudfront create-distribution --distribution-config file://distribution-config.json
```

### 对于现有分配
<a name="enable-mtls-cli-existing"></a>

使用以下示例命令获取当前分配配置：

```
aws cloudfront get-distribution-config --id E1A2B3C4D5E6F7 --output json > dist-config.json
```

编辑文件以添加 mTLS 设置。将以下示例部分配置添加到分配配置：

```
"ViewerMtlsConfig": {
    "Mode": "required", 
    "TrustStoreConfig": {
        "TrustStoreId": {TRUST_STORE_ID},
        "AdvertiseTrustStoreCaNames": true,
        "IgnoreCertificateExpiry": true
    }
}
```

从文件中删除 ETag 字段，但单独保存其值。

使用以下示例命令，以新配置更新分配：

```
aws cloudfront update-distribution \
    --id E1A2B3C4D5E6F7 \
    --if-match YOUR-ETAG-VALUE \
    --distribution-config file://dist-config.json
```

## 查看器协议策略
<a name="viewer-protocol-policies"></a>

在使用双向 TLS 时，必须为所有分配缓存行为配置仅 HTTPS 查看器协议策略：
+ **将 HTTP 重定向到 HTTPS**：在执行证书验证之前将 HTTP 请求重定向到 HTTPS。
+ **仅 HTTPS**：仅接受 HTTPS 请求并执行证书验证。

**注意**  
由于 HTTP 连接无法执行证书验证，因此双向 TLS 不支持 HTTP 和 HTTPS 查看器协议策略。

## 后续步骤
<a name="enable-mtls-next-steps"></a>

在 CloudFront 分配中启用查看器 TLS 后，您可以关联连接函数以实施自定义证书验证逻辑。利用连接函数，您可以通过自定义验证规则、证书吊销检查和日志记录来扩展内置 mTLS 身份验证功能。有关创建和关联连接函数的详细信息，请参阅[关联 CloudFront 连接函数](connection-functions.md)。

# 关联 CloudFront 连接函数
<a name="connection-functions"></a>

利用 CloudFront 连接函数，您可以在 TLS 握手过程中实施自定义证书验证逻辑，从而扩展内置 mTLS 身份验证功能。

## 什么是连接函数？
<a name="what-are-connection-functions"></a>

连接函数是指在客户端证书验证完成后，在 TLS 握手过程中运行的 JavaScript 函数。经过验证的客户端证书将传递给连接函数，此时连接函数可进一步决定是否授予访问权限。有关连接函数的详细信息，请参阅[使用 CloudFront Functions 在边缘进行自定义](cloudfront-functions.md)。

## 连接函数与 mTLS 的协作方式
<a name="how-connection-functions-work"></a>

当客户端尝试与 CloudFront 分配建立 mTLS 连接时，将遵循以下流程：

1. 客户端向 CloudFront 边缘站点发起 TLS 握手请求。

1. CloudFront 请求并接收客户端证书。

1. CloudFront 对信任存储执行标准证书验证。

1. 如果证书通过标准验证，CloudFront 将调用您的连接函数。如果在 **ViewerMtlsConfig** 中启用了 **IgnoreCertificateExpiry**，则虽已到期但其他条件均有效的证书也将传递至连接函数。如果客户端证书无效，则不会调用连接函数。

1. 您的连接函数接收经过解析的证书信息和连接详细信息。

1. 函数根据自定义逻辑做出允许/拒绝决定。

1. CloudFront 根据您的决定完成或终止 TLS 连接。

在验证模式和可选模式（当客户端提供证书时）下，都将调用连接函数。

## 创建连接函数
<a name="create-connection-function"></a>

您可以使用 CloudFront 控制台或 AWS CLI 创建连接函数。

### 创建连接函数（控制台）
<a name="create-connection-function-console"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择 **Functions**（函数）。

1. 选择**连接函数**选项卡，然后选择**创建连接函数**。

1. 输入在 AWS 账户中唯一的函数名称。

1. 选择**继续**。

1. 在函数编辑器中，编写用于证书验证的 JavaScript 代码。函数处理程序必须调用 allow 或 deny。

1. 可选：可将 KeyValueStore 与连接函数关联以实施吊销控制。

1. 选择**保存更改**。

### 创建连接函数（AWS CLI）
<a name="create-connection-function-cli"></a>

以下示例演示如何创建连接函数：

在单独的文件（例如 code.js）中编写函数代码：

```
function connectionHandler(connection) {
  connection.allow();
}
```

```
aws cloudfront create-connection-function \
  --name "certificate-validator" \
  --connection-function-config '{
      "Comment": "Client certificate validation function",
      "Runtime": "cloudfront-js-2.0"
  }' \
  --connection-function-code fileb://code.js
```

## 连接函数代码结构
<a name="connection-function-code-structure"></a>

连接函数会实施 connectionHandler 函数，该函数接收一个包含证书和连接信息的连接对象。您的函数必须使用 `connection.allow()` 或 `connection.deny()` 来决定是否允许该连接。

### 基本连接函数示例
<a name="basic-connection-function-example"></a>

以下示例展示了一个简单的连接函数，该函数用于验证客户端证书的主题字段：

```
function connectionHandler(connection) {
    // Only process if a certificate was presented
    if (!connection.clientCertificate) {
        console.log("No certificate presented");
        connection.deny();
    }
    
    // Check the subject field for specific organization
    const subject = connection.clientCertificate.certificates.leaf.subject;
    if (!subject.includes("O=ExampleCorp")) {
        console.log("Certificate not from authorized organization");
       connection.deny();
    } else {
        // All checks passed
        console.log("Certificate validation passed");
        connection.allow();
    }
}
```

此处提供了连接对象上可用的客户端证书属性的完整规范：

```
{
  "connectionId": "Fdb-Eb7L9gVn2cFakz7wWyBJIDAD4-oNO6g8r3vXDV132BtnIVtqDA==", // Unique identifier for this TLS connection
  "clientIp": "203.0.113.42", // IP address of the connecting client (IPv4 or IPv6)
  "clientCertificate": {
    "certificates": {
      "leaf": {
        "subject": "CN=client.example.com,O=Example Corp,C=US", // Distinguished Name (DN) of the certificate holder
        "issuer": "CN=Example Corp Intermediate CA,O=Example Corp,C=US", // Distinguished Name (DN) of the certificate authority that issued this certificate
        "serialNumber": "4a:3f:5c:92:d1:e8:7b:6c", // Unique serial number assigned by the issuing CA (hexadecimal)
        "validity": {
          "notBefore": "2024-01-15T00:00:00Z", // Certificate validity start date (ISO 8601 format)
          "notAfter": "2025-01-14T23:59:59Z"   // Certificate expiration date (ISO 8601 format)
        },
        "sha256Fingerprint": "a1b2c3d4e5f6...abc123def456", // SHA-256 hash of the certificate (64 hex characters)
      },
    },
  },
}
```

## 关联连接函数
<a name="associate-connection-function-section"></a>

创建连接函数后，必须将其发布到 LIVE 阶段并与分配相关联。

### 发布并关联连接函数（控制台）
<a name="publish-associate-console"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**函数**

1. 选择**连接函数**选项卡，然后选择您的连接函数。

1. 选择**发布**以将其移至 LIVE 阶段。

1. 在“发布”部分下，在关联的分配表中选择**添加关联**。

1. 选择要关联的已启用查看器 mTLS 的分配。

或者，您也可以从分配详细信息页面关联已发布的连接函数。

1. 导航到控制台主页，该页面上列出了您的所有分配。

1. 选择要关联的分配。

1. 选择**常规**选项卡。

1. 在**设置**部分中，选择**编辑**。

1. 在**连接性**部分中，找到**查看器双向身份验证(mTLS)**。

1. 对于**连接函数**，选择您的函数。

1. 选择**保存更改**。

### 关联连接函数（AWS CLI）
<a name="associate-connection-function-cli"></a>

以下示例演示如何将连接函数与分配相关联：

```
// DistributionConfig:
{
   ...other settings,
    "ConnectionFunctionAssociation": {
        "Id": "cf_30c2CV2elHwCoInb3LtcaUJkZeD"
    }
}
```

## 连接函数的使用案例
<a name="connection-function-use-cases"></a>

连接函数支持多种高级 mTLS 使用案例：
+ **证书属性验证**：验证客户端证书中的特定字段，例如组织单元要求或主题备用名称模式。
+ **证书吊销检查**：使用 KeyValueStore 存储已吊销证书的序列号，以实施自定义证书吊销检查。
+ **基于 IP 的证书策略**：根据客户端 IP 地址或地理限制应用不同的证书策略。
+ **多租户验证**：实施特定于租户的验证规则，根据主机名或证书属性应用不同的证书要求。

**注意**  
在 TLS 握手过程中，针对每个客户端连接运行一次连接函数。  
连接函数只能允许或拒绝连接，而不能修改 HTTP 请求/响应。  
仅 LIVE 阶段函数（已发布）能够与分配相关联。  
每个分配可拥有最多一个连接函数。

## 后续步骤
<a name="connection-function-next-steps"></a>

将连接函数与 CloudFront 分配关联后，您可以配置可选设置以自定义 mTLS 实施的行为。有关配置其他设置（例如，可选的客户端证书验证模式）的详细说明，请参阅[配置其他设置](configuring-additional-settings.md)。

# 配置其他设置
<a name="configuring-additional-settings"></a>

启用基本双向 TLS 身份验证后，您可以配置其他设置，针对特定使用案例和要求来自定义身份验证行为。

## 客户端证书验证可选模式
<a name="optional-mode"></a>

CloudFront 提供了一种备用的可选客户端证书验证模式，该模式可验证所提供的客户端证书，但会向未提供证书的客户端授予访问权限。

### 可选模式行为
<a name="optional-mode-behavior"></a>
+ 允许拥有有效证书的客户端建立连接（无效证书将被拒绝）。
+ 允许无证书的客户端建立连接。
+ 允许通过单个分配实施的混合客户端身份验证场景。

可选模式非常适合向 mTLS 身份验证逐步迁移，既能支持拥有证书的客户端与无证书的客户端，又能保持与传统客户端的向后兼容性。

**注意**  
在可选模式下，即使客户端未提供证书，连接函数也将被调用。这可让您实施自定义逻辑，例如记录客户端 IP 地址或根据是否提供了证书来应用不同的策略。

### 配置可选模式（控制台）
<a name="configure-optional-mode-console"></a>

1. 在分配设置中，导航至**常规**选项卡，选择**编辑**。

1. 滚动到**连接性**容器中的**查看器双向身份验证(mTLS)** 部分。

1. 对于**客户端证书验证模式**，选择**可选**。

1. 保存更改。

### 配置可选模式（AWS CLI）
<a name="configure-optional-mode-cli"></a>

以下示例演示如何配置可选模式：

```
"ViewerMtlsConfig": {
   "Mode": "optional",
   ...other settings
}
```

## 证书颁发机构公告
<a name="ca-advertisement"></a>

AdvertiseTrustStoreCaNames 字段可控制 CloudFront 是否在 TLS 握手过程中向客户端发送可信 CA 名称列表，从而帮助客户端选择适当的证书。

### 配置 CA 公告（控制台）
<a name="configure-ca-advertisement-console"></a>

1. 在分配设置中，导航至**常规**选项卡，选择**编辑**。

1. 滚动到**连接性**容器中的**查看器双向身份验证(mTLS)** 部分。

1. 选中或取消选中**公布信任存储 CA 名称**复选框。

1. 选择**保存更改**。

### 配置 CA 公告（AWS CLI）
<a name="configure-ca-advertisement-cli"></a>

以下示例演示如何启用 CA 公告：

```
"ViewerMtlsConfig": {
   "Mode": "required", // or "optional"
   "TrustStoreConfig": {
      "AdvertiseTrustStoreCaNames": true,
      ...other settings
   } 
}
```

## 证书到期处理
<a name="certificate-expiration-handling"></a>

IgnoreCertificateExpiry 属性决定 CloudFront 对到期的客户端证书的响应方式。默认情况下，CloudFront 会拒绝到期的客户端证书，但您可将其配置为在必要时接受此类证书。对于证书已到期且无法即刻更新的设备，通常会启用此配置。

### 配置证书到期处理（控制台）
<a name="configure-expiration-console"></a>

1. 在分配设置中，导航至**常规**选项卡，选择**编辑**。

1. 滚动到**连接性**容器的**查看器双向身份验证(mTLS)** 部分。

1. 选中或取消选中**忽略证书到期日期**复选框。

1. 选择**保存更改**。

### 配置证书到期处理（AWS CLI）
<a name="configure-expiration-cli"></a>

以下示例演示如何忽略证书到期情况：

```
"ViewerMtlsConfig": {
  "Mode": "required", // or "optional"
  "TrustStoreConfig": {
     "IgnoreCertificateExpiry": false,
     ...other settings
  }
}
```

**注意**  
**IgnoreCertificateExpiry** 仅适用于证书有效日期。所有其他证书验证检查仍将适用（信任链、签名验证）。

## 后续步骤
<a name="additional-settings-next-steps"></a>

配置其他设置后，您可以设置标头转发以将证书信息传递至源，使用连接函数和 KeyValueStore 实施证书吊销，并启用连接日志以进行监控。有关将证书信息转发至源的详细信息，请参阅[将标头转发到源](viewer-mtls-headers.md)。

# 用于缓存策略并转发到源的查看器 mTLS 标头
<a name="viewer-mtls-headers"></a>

使用双向 TLS 身份验证时，CloudFront 可以从客户端证书中提取信息，并将其作为 HTTP 标头转发到源。这可让原始服务器访问证书详细信息，而无需实施证书验证逻辑。

以下标头可用于创建缓存行为：


| 标头名称 | 说明 | 示例值 | 
| --- | --- | --- | 
| CloudFront-Viewer-Cert-Serial-Number | 证书序列号的十六进制表示形式 | 4a:3f:5c:92:d1:e8:7b:6c | 
| CloudFront-Viewer-Cert-Issuer | 颁发者的可分辨名称（DN）的 RFC2253 字符串表示形式 | CN=rootcamtls.com,OU=rootCA,O=mTLS,L=Seattle,ST=Washington,C=US | 
| CloudFront-Viewer-Cert-Subject | 主题的可分辨名称（DN）的 RFC2253 字符串表示形式 | CN=client\$1.com,OU=client-3,O=mTLS,ST=Washington,C=US | 
| CloudFront-Viewer-Cert-Present | 1（提供）或 0（未提供），用于指示是否提供了证书。在“必需”模式下，此值始终为 1。 | 1 | 
| CloudFront-Viewer-Cert-Sha256 | 客户端证书的 SHA256 哈希 | 01fbf94fef5569753420c349f49adbfd80af5275377816e3ab1fb371b29cb586 | 

对于源请求，除上述可供缓存行为使用的标头外，还会额外提供两个标头：


| 标头名称 | 说明 | 示例值 | 
| --- | --- | --- | 
| CloudFront-Viewer-Cert-Validity | notBefore 和 notAfter 日期的 ISO8601 格式 | CloudFront-Viewer-Cert-Validity：NotBefore=2023-09-21T01:50:17Z;NotAfter=2024-09-20T01:50:17Z | 
| CloudFront-Viewer-Cert-Pem | 叶证书的经 URL 编码的 PEM 格式 | CloudFront-Viewer-Cert-Pem：-----BEGIN%20CERTIFICATE-----%0AMIIG<...reduced...>NmrUlw%0A-----END%20CERTIFICATE-----%0A | 

## 配置标头转发
<a name="configure-header-forwarding"></a>

### 控制台
<a name="configure-headers-console"></a>

在验证模式下，CloudFront 会自动将 CloudFront-Viewer-Cert-\$1 标头添加到所有查看器请求中。要将这些标头转发到源，请执行以下操作：

1. 在主列表分配页面中，选择已启用查看器 mTLS 的分配，然后转至**行为**选项卡

1. 选择缓存行为，然后选择**编辑**

1. 在**源请求策略**部分中，选择**创建策略**或选择现有策略

1. 确保源请求策略包含以下标头：
   + CloudFront-Viewer-Cert-Serial-Number
   + CloudFront-Viewer-Cert-Issuer
   + CloudFront-Viewer-Cert-Subject
   + CloudFront-Viewer-Cert-Present
   + Cloudfront-Viewer-Cert-Sha256
   + CloudFront-Viewer-Cert-Validity
   + CloudFront-Viewer-Cert-Pem

1. 选择**创建**（对于新策略）或**保存更改**（对于现有策略）

1. 在缓存行为中选择策略并保存更改

### 使用 AWS CLI
<a name="configure-headers-cli"></a>

以下示例演示如何创建包含用于验证模式的 mTLS 标头的源请求策略：

```
aws cloudfront create-origin-request-policy \
  --origin-request-policy-config '{
    "Name": "MTLSHeadersPolicy",
    "HeadersConfig": {
      "HeaderBehavior": "whitelist",
      "Headers": {
        "Quantity": 5,
        "Items": [
          "CloudFront-Viewer-Cert-Serial-Number",
          "CloudFront-Viewer-Cert-Issuer",
          "CloudFront-Viewer-Cert-Subject",
          "CloudFront-Viewer-Cert-Validity",
          "CloudFront-Viewer-Cert-Pem"
        ]
      }
    },
    "CookiesConfig": {
      "CookieBehavior": "none"
    },
    "QueryStringsConfig": {
      "QueryStringBehavior": "none"
    }
  }'
```

## 标头处理注意事项
<a name="header-processing-considerations"></a>

使用证书标头时，请考虑以下最佳实践：
+ **标头验证：**作为一项额外的安全措施，在源中验证证书标头值
+ **标头大小限制：**PEM 证书标头可能较大，请确保您的原始服务器能够处理此类标头
+ **缓存注意事项：**在缓存键中使用证书标头会加剧缓存碎片化
+ **跨源请求：**如果您的应用程序使用 CORS，则可能需要将其配置为允许使用证书标头

## 后续步骤
<a name="headers-next-steps"></a>

配置标头转发后，您可以使用 CloudFront 连接函数和 KeyValueStore 实施证书吊销检查。有关实施吊销检查的详细信息，请参阅[使用 CloudFront 连接函数和 KVS 实施吊销](revocation-connection-function-kvs.md)。

# 使用 CloudFront 连接函数和 KVS 实施吊销
<a name="revocation-connection-function-kvs"></a>

您可以将 CloudFront 连接函数与 KeyValueStore 结合使用，来实施双向 TLS 身份验证的证书吊销检查。此方法提供了一种可扩展的实时证书吊销机制，可作为 CloudFront 的内置证书验证的有力补充。

连接函数是指在 TLS 连接建立过程中，在 CloudFront 边缘站点运行的 JavaScript 函数，此类函数可用于为 mTLS 身份验证实施自定义证书验证逻辑。有关连接函数的详细信息，请参阅[关联 CloudFront 连接函数](connection-functions.md)。

## 证书吊销如何与连接函数结合使用
<a name="how-revocation-works"></a>

CloudFront 的标准证书验证功能可验证证书链、签名和到期日期，但不包括内置的证书吊销检查。通过使用连接函数，您可以在 TLS 握手过程中实施自定义吊销检查。

证书吊销流程如下所述：

1. 将已吊销证书的序列号存储在 CloudFront KeyValueStore 中。

1. 当客户端提供证书时，系统将调用您的连接函数。

1. 该函数会根据 KeyValueStore 中的数据核对证书序列号。

1. 如果在存储中找到了序列号，则证书将被吊销。

1. 您的函数拒绝已吊销证书的连接。

此方法可在 CloudFront 的全球边缘网络中提供近乎实时的吊销检查。

## 针对已吊销的证书设置 KeyValueStore
<a name="setup-kvs-revoked-certs"></a>

首先，创建一个 KeyValueStore 来存储已吊销证书的序列号：

### 创建 KeyValueStore（控制台）
<a name="create-kvs-console"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**键值存储**。

1. 选择**创建键值存储**。

1. 输入键值存储的名称（例如，revoked-certificates）。

1. （可选）添加描述。

1. 选择**创建键值存储**。

### 创建 KeyValueStore（AWS CLI）
<a name="create-kvs-cli"></a>

以下示例演示如何创建 KeyValueStore：

```
aws cloudfront create-key-value-store \
  --name "revoked-certificates" \
  --comment "Store for revoked certificate serial numbers"
```

## 导入已吊销证书的序列号
<a name="import-revoked-serials"></a>

创建 KeyValueStore 之后，您需要导入已吊销证书的序列号：

### 准备吊销数据
<a name="prepare-revocation-data"></a>

创建包含已吊销证书序列号的 JSON 文件：

```
{
  "data": [
    {
      "key": "ABC123DEF456",
      "value": ""
    },
    {
      "key": "789XYZ012GHI",
      "value": ""
    }
  ]
}
```

### 从 S3 导入
<a name="import-from-s3"></a>

1. 将 JSON 文件上传到 S3 存储桶

1. 将该文件导入 KeyValueStore：

   ```
   aws cloudfront create-key-value-store \
     --name "revoked-certificates" \
     --import-source '{
       "SourceType": "S3",
       "SourceARN": "arn:aws:s3:::amzn-s3-demo-bucket1/revoked-serials.json"
     }'
   ```

## 创建用于吊销检查的连接函数
<a name="create-revocation-connection-function"></a>

创建连接函数，来根据 KeyValueStore 中的数据核对证书序列号：

### 连接函数代码示例
<a name="revocation-function-example"></a>

以下示例演示连接函数如何执行证书吊销检查：

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Get client certificate serial number
    const clientSerialNumber = connection.clientCertificate.certificates.leaf.serialNumber;
    
    // Check if the serial number exists in the KeyValueStore
    const isRevoked = await kvsHandle.exists(clientSerialNumber.replaceAll(':', ''));
    
    if (isRevoked) {
        console.log(`Certificate ${clientSerialNumber} is revoked. Denying connection.`);
        connection.logCustomData(`REVOKED:${clientSerialNumber}`);
        connection.deny();
    } else {
        console.log(`Certificate ${clientSerialNumber} is valid. Allowing connection.`);
        connection.allow();
    }
    
}
```

### 创建连接函数（AWS CLI）
<a name="create-revocation-function-cli"></a>

以下示例演示如何创建连接函数与 KeyValueStore 关联：

```
aws cloudfront create-connection-function \
  --name "revocation-checker" \
  --connection-function-config '{
      "Comment": "Certificate revocation checking function",
      "Runtime": "cloudfront-js-2.0",
      "KeyValueStoreAssociations": {
          "Quantity": 1,
          "Items": [
              {
                  "KeyValueStoreARN": "arn:aws:cloudfront::123456789012:key-value-store/revoked-certificates"
              }
          ]
      }
  }' \
  --connection-function-code fileb://revocation-checker.js
```

## 将函数与分配关联
<a name="associate-revocation-function"></a>

创建并发布连接函数后，将其与已启用 mTLS 的 CloudFront 分配相关联，如[关联 CloudFront 连接函数](connection-functions.md)部分中所述。

# 使用连接日志实现可观测性
<a name="connection-logs"></a>

CloudFront 连接日志提供了双向 TLS 身份验证事件的详细信息，让您可监控证书验证、跟踪连接尝试以及解决身份验证问题。

## 什么是连接日志？
<a name="what-are-connection-logs"></a>

连接日志为已启用双向 TLS 的分配捕获有关 TLS 握手和证书验证的详细信息。与记录 HTTP 请求信息的标准访问日志不同，连接日志专注于 TLS 连接建立阶段，包括：
+ 连接状态（成功/失败）
+ 客户端证书详细信息
+ TLS 协议和密码信息
+ 连接计时指标
+ 来自连接函数的自定义数据

通过查看这些日志，您可以全面了解基于证书的身份验证事件，从而更好地监控安全性、解决问题并满足合规性要求。

## 启用连接日志
<a name="enable-connection-logs"></a>

连接日志仅适用于已启用双向 TLS 身份验证的分配。您可以将连接日志发送到多个目标，包括 CloudWatch Logs、Amazon Data Firehose 和 Amazon S3。

### 先决条件
<a name="connection-logs-prerequisites"></a>

在启用连接日志之前：
+ 为 CloudFront 分配配置双向 TLS
+ 为 CloudFront 分配启用连接日志
+ 确保您拥有所选日志记录目标所需的权限
+ 对于跨账户传输，请配置适当的 IAM 策略

### 启用连接日志（控制台）
<a name="enable-connection-logs-console"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 从分配列表中，选择已启用 mTLS 的分配。

1. 选择**日志记录**选项卡。

1. 选择**添加**。

1. 选择要接收日志的服务：
   + **CloudWatch Logs**
   + **Firehose**
   + **Amazon S3**

1. 对于**目标**，为所选服务选择资源：
   + 对于 CloudWatch Logs，输入**日志组名称**
   + 对于 Firehose，输入 **Firehose 传输流**
   + 对于 Amazon S3，输入**存储桶名称**（可选择附带前缀）

1. （可选）配置其他设置：
   + **字段选择：**选择要包含的特定日志字段。
   + **输出格式：**从 JSON、Plain、w3c、Raw 或 Parquet（仅 S3）中进行选择。
   + **字段分隔符：**指定如何分隔日志字段。

1. 选择**保存更改**

### 启用连接日志（AWS CLI）
<a name="enable-connection-logs-cli"></a>

以下示例演示如何使用 CloudWatch API 启用连接日志：

```
# Step 1: Create a delivery source
aws logs put-delivery-source \
  --name "cf-mtls-connection-logs" \
  --resource-arn "arn:aws:cloudfront::123456789012:distribution/E1A2B3C4D5E6F7" \
  --log-type CONNECTION_LOGS

# Step 2: Create a delivery destination
aws logs put-delivery-destination \
  --name "s3-destination" \
  --delivery-destination-configuration \
  "destinationResourceArn=arn:aws:s3:::amzn-s3-demo-bucket1"

# Step 3: Create the delivery
aws logs create-delivery \
  --delivery-source-name "cf-mtls-connection-logs" \
  --delivery-destination-arn "arn:aws:logs:us-east-1:123456789012:delivery-destination:s3-destination"
```

**注意**  
在使用 CloudWatch API 时，您必须指定美国东部（弗吉尼亚州北部）区域（us-east-1），即使在将日志传输到其他区域时也是如此。

## 连接日志字段
<a name="connection-log-fields"></a>

连接日志包含有关每次 TLS 连接尝试的详细信息：


| 字段 | 说明 | 示例 | 
| --- | --- | --- | 
| eventTimestamp | 连接建立成功或失败时对应的 ISO 8601 时间戳 | 1731620046814 | 
| connectionId | TLS 连接的唯一标识符 | oLHiEKbQSn8lkvJfA3D4gFowK3\$1iZ0g4i5nMUjE1Akod8TuAzn5nzg== | 
| connectionStatus |  mTLS 连接尝试的状态。  | Success 或 Failed | 
| clientIp | 连接客户端的 IP 地址 | 2001:0db8:85a3:0000:0000:8a2e:0370:7334 | 
| clientPort | 客户端使用的端口 | 12137 | 
| serverIp | CloudFront 边缘服务器的 IP 地址 | 99.84.71.136 | 
| distributionId | CloudFront 分配 ID | E2DX1SLDPK0123 | 
| distributionTenantId | CloudFront 分配租户 ID（如果适用） | dt\$12te1Ura9X3R2iCGNjW123 | 
| tlsProtocol | 使用的 TLS 协议版本 | TLSv1.3 | 
| tlsCipher | 用于连接的 TLS 密码套件 | TLS\$1AES\$1128\$1GCM\$1SHA256 | 
| tlsHandshakeDuration | TLS 握手的持续时间（毫秒） | 153 | 
| tlsSni | 来自 TLS 握手的服务器名称指示值 | d111111abcdef8.cloudfront.net | 
| clientLeafCertSerialNumber | 客户端证书的序列号 | 00:b1:43:ed:93:d2:d8:f3:9d | 
| clientLeafCertSubject | 客户端证书的主题字段 | C=US, ST=WA, L=Seattle, O=Amazon.com, OU=CloudFront, CN=client.test.mtls.net | 
| clientLeafCertIssuer | 客户端证书的颁发者字段 | C=US, ST=WA, L=Seattle, O=Amazon.com, OU=CloudFront, CN=test.mtls.net | 
| clientLeafCertValidity | 客户端证书的有效期 | NotBefore=2025-06-05T23:28:21Z;NotAfter=2125-05-12T23:28:21Z | 
| connectionLogCustomData | 通过连接函数添加的自定义数据 | REVOKED:00:b1:43:ed:93:d2:d8:f3:9d | 

## 连接错误代码
<a name="connection-error-codes"></a>

```
Failed:ClientCertMaxChainDepthExceeded
Failed:ClientCertMaxSizeExceeded
Failed:ClientCertUntrusted
Failed:ClientCertNotYetValid
Failed:ClientCertExpired
Failed:ClientCertTypeUnsupported
Failed:ClientCertInvalid
Failed:ClientCertIntentInvalid
Failed:ClientCertRejected
Failed:ClientCertMissing
Failed:TcpError
Failed:TcpTimeout
Failed:ConnectionFunctionError
Failed:ConnectionFunctionDenied
Failed:Internal
Failed:UnmappedConnectionError
```

连接失败时，CloudFront 会记录具体的原因代码：


| 代码 | 说明 | 
| --- | --- | 
| ClientCertMaxChainDepthExceeded | 已超出最大证书链深度 | 
| ClientCertMaxSizeExceeded | 已超出最大证书大小 | 
| ClientCertUntrusted | 证书不可信 | 
| ClientCertNotYetValid | 证书尚未生效 | 
| ClientCertExpired | 证书已过期 | 
| ClientCertTypeUnsupported | 证书类型不受支持 | 
| ClientCertInvalid | 证书无效 | 
| ClientCertIntentInvalid | 证书意图无效 | 
| ClientCertRejected | 证书已被自定义验证拒绝 | 
| ClientCertMissing | 证书缺失 | 
| TcpError |  尝试建立连接时出错  | 
| TcpTimeout |  无法在超时时间内建立连接  | 
| ConnectionFunctionError |  连接函数执行过程中引发了未捕获的异常  | 
| Internal |  出现内部服务错误  | 
| UnmappedConnectionError |  发生了一个无法归入任何其他类别的错误  | 

# 源双向 TLS 与 CloudFront 结合使用
<a name="origin-mtls-authentication"></a>

双向 TLS 身份验证（双向传输层安全身份验证，mTLS）是一种安全协议，它在标准 TLS 身份验证的基础上进行了扩展，要求进行基于证书的双向身份验证，在该机制下，客户端与服务器必须先证实自己的身份，然后才能建立安全连接。

## 查看器 mTLS 与源 mTLS
<a name="viewer-mtls-vs-origin-mtls"></a>

可以在查看器与 CloudFront 分配（查看器 mTLS）之间启用双向身份验证（mTLS），并且/或者也可以在 CloudFront 分配和源（源 mTLS）之间启用双向身份验证。本文档与源 mTLS 配置有关。有关查看器 mTLS 配置，请参阅：[CloudFront 的双向 TLS 身份验证（查看器 mTLS）源双向 TLS 与 CloudFront 结合使用](mtls-authentication.md)。

源 mTLS 使 CloudFront 能够使用客户端证书向原始服务器对其自身进行身份验证。借助源 mTLS，您可以确保只有经过授权的 CloudFront 分配才能与应用程序服务器建立连接，从而有助于防止未经授权的访问尝试。

**注意**  
在源 mTLS 连接中，CloudFront 充当客户端，并在 TLS 握手期间将其客户端证书提供给原始服务器。CloudFront 不对客户端证书的有效性或吊销状态进行验证，这是原始服务器的责任。根据安全要求，源基础设施必须配置为针对客户端证书的信任存储验证客户端证书、检查证书到期以及执行吊销检查（例如 CRL 或 OCSP 验证）。CloudFront 的角色仅限提供证书；所有证书验证逻辑和安全策略均由原始服务器强制执行。

## 工作原理
<a name="how-origin-mtls-works"></a>

在 CloudFront 和源之间的标准 TLS 握手中，只有原始服务器向 CloudFront 提供证书来证明其身份。借助源 mTLS，身份验证过程会变为双向验证。当 CloudFront 尝试连接到原始服务器时，CloudFront 会在 TLS 握手期间提供客户端证书。在建立安全连接之前，原始服务器会针对证书的信任存储验证此证书。

## 使用案例
<a name="origin-mtls-use-cases"></a>

源 mTLS 可解决几种关键的安全场景，在此类场景中，传统的身份验证方法会产生运营开销：
+ **混合云和多云安全**：您可以保护 CloudFront 与在 AWS 外部托管的源或 AWS 上的公有源之间的连接。这样，就无需管理 IP 许可名单或自定义标头解决方案，从而在 AWS、本地数据中心和第三方提供商之间提供一致的基于证书的身份验证。运营分布式基础设施的媒体公司、零售商和企业可从其整个基础设施的标准化安全控制中受益。
+ **B2B API 和后端安全**：您可以保护您的后端 API 和微服务免受直接访问尝试的影响，同时保持 CloudFront 的性能优势。具有严格身份验证要求的 SaaS 平台、支付处理系统和企业应用程序可以验证 API 请求仅来自经授权的 CloudFront 分配，从而防止中间人攻击和未经授权的访问尝试。

## 重要：原始服务器要求
<a name="important-origin-server-requirements"></a>

源 mTLS 要求将原始服务器配置为支持双向 TLS 身份验证。源基础设施必须能够：
+ 在 TLS 握手期间请求和验证客户端证书
+ 使用颁发了 CloudFront 客户端证书的证书颁发机构证书维护信任存储
+ 记录和监控双向 TLS 连接事件
+ 管理证书验证策略和处理身份验证失败

CloudFront 负责提供客户端证书，但原始服务器负责验证这些证书并管理双向 TLS 连接。在 CloudFront 中启用源 mTLS 之前，请确保已正确地配置源基础设施。

## 开始使用
<a name="how-origin-mtls-getting-started"></a>

要在 CloudFront 中实现源 mTLS，您需要在 AWS Certificate Manager 中导入客户端证书，将原始服务器配置为要求双向 TLS，并在您的 CloudFront 分配中启用源 mTLS。以下各节提供每项配置任务的分步说明。

**Topics**
+ [查看器 mTLS 与源 mTLS](#viewer-mtls-vs-origin-mtls)
+ [工作原理](#how-origin-mtls-works)
+ [使用案例](#origin-mtls-use-cases)
+ [重要：原始服务器要求](#important-origin-server-requirements)
+ [开始使用](#how-origin-mtls-getting-started)
+ [使用 AWS Certificate Manager 管理证书](origin-certificate-management-certificate-manager.md)
+ [为 CloudFront 分配启用源双向 TLS](origin-enable-mtls-distributions.md)
+ [将 CloudFront Functions 与源双向 TLS 结合使用](origin-mtls-cloudfront-functions.md)

# 使用 AWS Certificate Manager 管理证书
<a name="origin-certificate-management-certificate-manager"></a>

[AWS Certificate Manager（ACM）](https://aws.amazon.com/certificate-manager/)存储 CloudFront 在源双向 TLS 身份验证期间向原始服务器提供的客户端证书。

## 证书颁发机构支持
<a name="origin-ca-support"></a>

CloudFront 源 mTLS 需要具有扩展密钥用法（EKU）的客户端证书来进行 TLS 客户端身份验证。由于此要求，您必须从您的证书颁发机构颁发证书并将其导入到 AWS Certificate Manager 中。ACM 的自动证书预置和续订功能不适用于源 mTLS 客户端证书。CloudFront 源 mTLS 支持来自两个来源的客户端证书：
+ **AWS 私有证书颁发机构：**您可以使用在“扩展密钥用法”字段中包含 TLS 客户端身份验证的证书模板（例如 EndEntityClientAuthCertificate 模板）从 AWS 私有 CA 颁发证书。从 AWS 私有 CA 颁发证书后，您必须将其导入到美国东部（弗吉尼亚州北部）（us-east-1）中的 ACM。这种方法既可提供 AWS 私有 CA 的安全优势，又可让您控制证书生命周期管理。
+ **第三方私有证书颁发机构：**也可以从现有的私有证书颁发机构基础设施颁发证书并将其导入到 ACM 中。这可让您在利用 CloudFront 的源 mTLS 功能的同时保留当前证书管理流程。证书必须在“扩展密钥用法”字段中包含 TLS 客户端身份验证，并且证书、私有密钥和证书链必须采用 PEM 格式。

**重要**  
对于 AWS 私有 CA 和第三方 CA，您负责监控证书到期日期，并在到期前将续订的证书导入到 ACM 中。ACM 的自动续订功能不适用于源 mTLS 所用的导入证书。

## 证书要求和规范
<a name="origin-certificate-requirements"></a>

### 客户端证书要求
<a name="origin-ca-cert-format-requirements"></a>
+ **格式：**PEM（隐私增强邮件）格式
+ **组件：**证书、私有密钥和证书链
+ **最大证书链深度：**3（叶证书 \$1 中间证书 \$1 根证书）
+ **最大证书链大小：**64 KB
+ **证书大小：**不能超过 96 KB
+ **最大私有密钥大小：**5 KB（ACM 限制）
+ **每个 CloudFront 分配创建或更新 API 调用可以添加或修改的最大唯一源 mTLS 证书 ARN：**5
+ **区域：**证书必须存储在美国东部（弗吉尼亚州北部）区域（us-east-1）的 ACM 中

### 支持的证书规范
<a name="origin-supported-cert-specs"></a>
+ **证书类型：**X.509v3
+ **公有密钥算法：**
  + RSA：2048 位
  + ECDSA：P-256
+ **签名算法：**
  + 采用 RSA 的 SHA256、SHA384、SHA512
  + 采用 ECDSA 的 SHA256、SHA384、SHA512
  + 采用 RSASSA-PSS 与 MGF1 的 SHA256、SHA384、SHA512
+ **扩展密钥用法（必需）：**证书要求将扩展密钥用法（EKU）扩展设置为 TLS 客户端身份验证，确保其被授权用于 mTLS 目的

### 服务器证书要求
<a name="origin-server-certificate-requirements"></a>

在双向 TLS 握手期间，原始服务器必须提供来自公开信任的证书颁发机构的证书。有关原始服务器证书要求的完整详细信息，请参阅[将 SSL/TLS 证书与 CloudFront 结合使用的要求](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-https-cloudfront-to-custom-origin.html#using-https-cloudfront-to-origin-certificate)。

### 请求或导入证书
<a name="origin-request-import-certificate"></a>

在启用源 mTLS 之前，您必须在 ACM 中拥有可用的客户端证书。

#### 从 AWS 私有 CA 请求并导入证书
<a name="request-certificate-aws-private-ca"></a>

先决条件：
+ 在您的账户中配置的 AWS 私有证书颁发机构
+ 从 AWS 私有 CA 中颁发证书的权限
+ 将证书导入到 ACM 中的权限
+ 具有适合使用案例的 `Extended key usage:TLS web client authentication` 的[证书模板](https://docs.aws.amazon.com/privateca/latest/userguide/UsingTemplates.html) ARN
+ 安装 OpenSSL、AWS CLI 和 jq（用于解析 JSON）。

##### 从 PCA 请求证书并导入到 ACM（AWS CLI）
<a name="request-certificate-cli"></a>

1. 在变量中设置您的私有 CA ARN，以便于重用。

   ```
   PCA_ARN="arn:aws:acm-pca:region:account:certificate-authority/12345678..."
   ```

1. 使用 OpenSSL 生成 ECDSA P-256 私有密钥（prime256v1 曲线）和证书签名请求（CSR），确保使用 -nodes 标志按照 ACM 导入要求将私有密钥保持为未加密。

   ```
   openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
       -keyout private.key \
       -out request.csr \
       -subj "/CN=client.example.com"
   ```

1. 将 CSR 提交给 AWS 私有 CA 以颁发证书，这会返回新颁发的证书的 ARN。

   ```
   CERT_ARN=$(aws acm-pca issue-certificate \
       --certificate-authority-arn "$PCA_ARN" \
       --csr fileb://request.csr \
       --signing-algorithm "SHA256WITHECDSA" \
       --validity Value=365,Type="DAYS" \
       --template-arn arn:aws:acm-pca:::template/EndEntityCertificate/V1 \
       --query 'CertificateArn' --output text)
   ```

1. 使用 get-certificate 命令从 AWS PCA 检索证书捆绑包，该命令同时返回叶证书和链，然后使用 jq 将它们分成不同的文件。

   ```
   # Retrieve the full certificate bundle in JSON format
   aws acm-pca get-certificate \
       --certificate-authority-arn "$PCA_ARN" \
       --certificate-arn "$CERT_ARN" \
       --output json > full_cert.json
   
   # Split into Leaf and Chain
   jq -r '.Certificate' full_cert.json > leaf_cert.pem
   jq -r '.CertificateChain' full_cert.json > cert_chain.pem
   ```

1. 使用 fileb:// 协议在 CLI 中正确处理二进制文件数据，以便将未加密的私有密钥、叶证书和证书链导入到 AWS ACM 中。

   ```
   aws acm import-certificate \
       --certificate fileb://leaf_cert.pem \
       --private-key fileb://private.key \
       --certificate-chain fileb://cert_chain.pem \
       --region us-east-1 \
       --query 'CertificateArn' \
       --output text
   ```

#### 从第三方 CA 导入证书
<a name="import-certificate-third-party-ca"></a>

先决条件：
+ 来自证书颁发机构的采用 PEM 格式的证书、未加密的私有密钥和证书链
+ 证书必须包含用于 TLS 客户端身份验证的扩展密钥用法
+ 将证书导入到 ACM 中的权限

##### 将证书导入到 ACM 中（AWS CLI）
<a name="import-certificate-cli"></a>

```
aws acm import-certificate \
  --certificate fileb://certificate.pem \
  --private-key fileb://private-key.pem \
  --certificate-chain fileb://certificate-chain.pem \
  --region us-east-1 \
  --query 'CertificateArn' \
  --output text
```

#### 后续步骤
<a name="certificate-next-steps"></a>

在 ACM 中获取或导入客户端证书后，您可以将原始服务器配置为要求双向 TLS 身份验证，并在 CloudFront 分配上启用源 mTLS。有关在 CloudFront 中启用源 mTLS 的说明，请参阅下一节“为 CloudFront 分配启用源双向 TLS”。

# 为 CloudFront 分配启用源双向 TLS
<a name="origin-enable-mtls-distributions"></a>

通过 AWS Certificate Manager 获取客户端证书并将原始服务器配置为要求双向 TLS 后，您可以在 CloudFront 分配上启用源 mTLS。

## 先决条件和要求
<a name="origin-mtls-prerequisites-requirements"></a>

在 CloudFront 分配上启用源 mTLS 之前，请确保您具有：
+ 存储在美国东部（弗吉尼亚州北部）区域（us-east-1）的 AWS Certificate Manager 中的客户端证书
+ 配置为要求双向 TLS 身份验证和验证客户端证书的原始服务器
+ 原始服务器，提供来自公开信任的证书颁发机构的证书
+ 修改 CloudFront 分配的权限
+ 源 mTLS 仅适用于商务版、高级版方案或按实际使用量付费定价方案。

**注意**  
可以为自定义源（包括在 AWS 外部托管的源）和支持双向 TLS 的 AWS 源（例如应用程序负载均衡器和 API Gateway）配置源 mTLS。

**重要**  
源 mTLS 不支持以下 CloudFront 功能：  
**gRPC 流量：**启用了源 mTLS 的源不支持 gRPC 协议
**WebSocket 连接**：启用了源 mTLS 的源不支持 WebSocket 协议
**VPC 源：**源 mTLS 不能与 VPC 源一起使用
**使用 Lambda@Edge 的源请求和源响应触发器**：源 mTLS 不支持源请求和源响应位置中的 Lambda@Edge 函数
**嵌入式 POP：**嵌入式 POP 不支持源 mTLS

## 启用源 mTLS
<a name="origin-enable-mtls-per-origin"></a>

每源配置支持您为同一分配中的不同源指定不同的客户端证书。当源有不同的身份验证要求时，这种方法可以提供最大的灵活性。

### 对于新分配（控制台）
<a name="origin-enable-mtls-new-distributions"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择**创建分配**

1. 选择定价方案：选择**商业版**或**高级版**或**按实际使用量付费** [免费方案不提供源 mTLS]

1. 在“源设置”部分，选择源类型为“其它”

1. 在**源设置**部分，选择**自定义源设置**

1. 配置第一个源（域名、协议等）

1. 在源配置中，找到 **mTLS**

1. 将 **mTLS** 切换为开启

1. 对于**客户端证书**，从 AWS Certificate Manager 中选择您的证书

1. （可选）添加具有其自己的源 mTLS 配置的其它源

1. 填写其余分配设置，然后选择**创建分配**

### 对于现有分配（控制台）
<a name="origin-enable-mtls-existing-distributions"></a>

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 从分配列表中，选择要修改的分配。[注意：请确保您的分配采用**专业版、高级版或按实际使用量付费**定价方案。否则，您必须先升级您的定价方案，然后才能启用源 mTLS]

1. 选择**源**选项卡

1. 选择要配置的源，然后选择**编辑**

1. 在源设置中，找到 **mTLS**

1. 将 **mTLS** 切换为开启

1. 对于**客户端证书**，从 AWS Certificate Manager 中选择您的证书。[注意：只会列出 EKU（扩展密钥用法）属性设置为“TLS 客户端身份验证”的客户端证书]

1. 选择**保存更改**

1. 根据需要对其它源重复此操作

## 使用 AWS CLI
<a name="origin-enable-mtls-cli"></a>

对于每源配置，请在每个源的配置中指定源 mTLS 设置：

```
{
  "Origins": {
    "Quantity": 2,
    "Items": [
      {
        "Id": "origin-1",
        "DomainName": "api.example.com",
        "CustomOriginConfig": {
          "HTTPSPort": 443,
          "OriginProtocolPolicy": "https-only"
        },
        "OriginMtlsConfig": {
          "ClientCertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/cert-1"
        }
      },
      {
        "Id": "origin-2",
        "DomainName": "backend.example.com",
        "CustomOriginConfig": {
          "HTTPSPort": 443,
          "OriginProtocolPolicy": "https-only"
        },
        "OriginMtlsConfig": {
          "CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/cert-2"
        }
      }
    ]
  }
}
```

**注意**  
如果服务器不请求客户端证书，CloudFront 将不会提供该证书，从而支持正常进行连接。

## 后续步骤
<a name="origin-enable-mtls-next-steps"></a>

在 CloudFront 分配上启用源 mTLS 后，可以使用 CloudFront 访问日志监控身份验证事件。

# 将 CloudFront Functions 与源双向 TLS 结合使用
<a name="origin-mtls-cloudfront-functions"></a>

CloudFront Functions 在边缘提供轻量级的无服务器计算，用于自定义内容交付。在 CloudFront Functions 中使用源双向 TLS 时，需要注意有关源选择和操作的特定行为和限制。

## 支持的 CloudFront Functions 操作
<a name="supported-cloudfront-functions-operations"></a>

CloudFront Functions 可以通过以下方式与启用了 mTLS 的源进行交互：

### updateRequestOrigin()
<a name="update-request-origin-function"></a>

updateRequestOrigin() 函数在使用启用了 mTLS 的源时支持进行有限的修改：
+ **在源 mTLS 源之间切换：**您可以更新请求以路由到使用源 mTLS 的不同源，前提是这两个源都使用**相同的客户端证书**。这支持您在保持双向 TLS 身份验证的同时实现自定义路由逻辑。
+ **禁用源 mTLS：**您可以通过在函数中设置 `mTLSConfig: 'off'`，从启用了 mTLS 的源切换到非 mTLS 源。这为根据请求特征有条件地禁用双向 TLS 身份验证提供了灵活性。

#### 示例：在具有相同证书的源 mTLS 源之间切换
<a name="example-switching-mtls-origins"></a>

```
function handler(event) {
    var request = event.request;

    // Route to different origin based on request path
    if (request.uri.startsWith('/api/v2')) {
        request.origin = {
            domainName: 'api-v2.example.com',
            customHeaders: {},
            // Both origins must use the same certificate
        };
    }

    return request;
}
```

#### 示例：有条件地禁用源 mTLS
<a name="example-disabling-mtls"></a>

```
function handler(event) {
    var request = event.request;

    // Disable mTLS for specific paths
    if (request.uri.startsWith('/public')) {
        request.origin = {
            domainName: 'public-origin.example.com',
            customHeaders: {},
            mTLSConfig: 'off'
        };
    }

    return request;
}
```

## 不支持的 CloudFront Functions 操作
<a name="unsupported-cloudfront-functions-operations"></a>

在正式上市时，以下 CloudFront Functions 操作不支持启用了源 mTLS 的源：

### selectRequestOriginById()
<a name="select-request-origin-by-id-function"></a>

`selectRequestOriginById()` 函数无法选择已启用源 mTLS 的源。尝试使用此函数选择已启用源 mTLS 的源将导致验证错误。

如果使用案例要求使用源 mTLS 进行动态源选择，请改用 `updateRequestOrigin()`，确保所有目标源都使用相同的客户端证书。

### createRequestOriginGroup()
<a name="create-request-origin-group-function"></a>

`createRequestOriginGroup()` 函数不支持创建包含已启用 mTLS 的源的源组。无法通过 CloudFront Functions 动态创建具有源 mTLS 源的源组。

如果您需要具有源 mTLS 的源失效转移功能，请直接在 CloudFront 分配设置中配置源组，而不是在函数中动态创建源组。

# 使用签名 URL 和签名 Cookie 提供私有内容
<a name="PrivateContent"></a>

许多通过互联网分发内容的公司都希望限制对文档、业务数据、流媒体或面向选定用户（例如付费用户）的内容的访问。要使用 CloudFront 安全地提供这种私有内容，可执行以下操作：
+ 要求用户使用特殊的 CloudFront 签名 URL 或签名 Cookie 访问私有内容。
+ 要求您的用户使用 CloudFront URL 访问内容，而不是直接用源服务器（例如 Amazon S3 或私有 HTTP 服务器）上的 URL 访问内容。CloudFront URL 不是必需的，但建议使用，以防止用户绕过在已签名的 URL 或已签名的 Cookie 中指定的限制。

有关更多信息，请参阅 [限制对文件的访问](private-content-overview.md)。

## 如何提供私有内容
<a name="private-content-task-list"></a>

要配置 CloudFront 以提供私有内容，请执行以下任务。

1. （可选但建议使用）要求用户仅通过 CloudFront 访问您的内容。使用的方法取决于您使用的是 Amazon S3 还是自定义源：
   + **Amazon S3** – 请参阅[限制对 Amazon S3 源的访问](private-content-restricting-access-to-s3.md)。
   + **自定义源** – 请参阅[在自定义源上限制对文件的访问](private-content-overview.md#forward-custom-headers-restrict-access)。

   自定义源包括 Amazon EC2、配置为网站端点的 Amazon S3 存储桶、Elastic Load Balancing 以及您自己的 HTTP Web 服务器。

1. 指定您希望用于创建签名 URL 或签名 Cookie 的*可信密钥组* 或*可信签署人*。建议您使用可信密钥组。有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

1. 将应用程序编写为响应来自授权用户的请求，这些用户使用签名 URL 或使用设置签名 Cookie 的 `Set-Cookie` 标头。请按照以下主题之一中的步骤操作：
   + [使用签名 URL](private-content-signed-urls.md)
   + [使用签名 Cookie](private-content-signed-cookies.md)

   如果您不确定要使用哪种方法，请参阅[决定使用签名 URL 还是签名 Cookie](private-content-choosing-signed-urls-cookies.md)。

**Topics**
+ [如何提供私有内容](#private-content-task-list)
+ [限制对文件的访问](private-content-overview.md)
+ [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)
+ [决定使用签名 URL 还是签名 Cookie](private-content-choosing-signed-urls-cookies.md)
+ [使用签名 URL](private-content-signed-urls.md)
+ [使用签名 Cookie](private-content-signed-cookies.md)
+ [用于 Base64 编码和加密的 Linux 命令和 OpenSSL](private-content-linux-openssl.md)
+ [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)

# 限制对文件的访问
<a name="private-content-overview"></a>

您可通过两种方式控制用户对私有内容的访问：
+ [限制对 CloudFront 缓存中的文件的访问](#private-content-overview-edge-caches)。
+ 通过执行下列操作之一，限制对您源中文件的访问：
  + [为 Amazon S3 存储桶设置源访问控制 (OAI](private-content-restricting-access-to-s3.md))。
  + [为私有 HTTP 服务器（自定义源）配置自定义标头。](#forward-custom-headers-restrict-access)

## 限制对 CloudFront 缓存中的文件的访问
<a name="private-content-overview-edge-caches"></a>

您可以将 CloudFront 配置为要求用户使用*签名 URL* 或*签名 Cookie* 访问您的文件。然后开发应用程序，以创建签名 URL 并将其分发给经身份验证的用户，或者为经身份验证的用户发送用于设置签名 Cookie 的 `Set-Cookie` 标头。（要为一些用户提供长期访问少量文件的权限，还可以手动创建签名 URL。） 

创建签名 URL 或签名 Cookie 以控制对您的文件的访问时，可以指定以下限制：
+ 结束日期和时间，在此之后，URL 不再有效。
+ （可选）URL 生效的日期和时间。
+ （可选）可用于访问您的内容的 IP 地址或计算机的地址范围。

签名 URL 或签名 Cookie 的其中一部分使用公有/私有密钥对中的私有密钥进行哈希处理和签名。当某人使用签名 URL 或签名 Cookie 访问文件时，CloudFront 将比较 URL 或 Cookie 的已签名部分和未签名部分。如果它们不匹配，则 CloudFront 将不提供该文件。

您必须使用 RSA 2048 或 ECDSA 256 私有密钥来对 URL 或 Cookie 进行签名。

## 限制对 Amazon S3 存储桶中文件的访问
<a name="private-content-overview-s3"></a>

您可以选择保护 Amazon S3 存储桶中的内容，以便用户可以通过指定的 CloudFront 分配访问内容，但不能使用 Amazon S3 URL 直接访问内容。这可防止其他人绕过 CloudFront 并使用 Amazon S3 URL 访问您希望限制访问的内容。虽然此步骤未要求使用签名 URL，但我们建议使用。

如需要求用户通过 CloudFront URL 访问内容，请执行以下任务：
+ 为 CloudFront *源访问控制*授予读取 S3 存储桶中的文件的权限。
+ 创建源访问控制，并将其与您的 CloudFront 分配相关联。
+ 删除其他任何人使用 Amazon S3 URL 读取这些文件的权限。

有关更多信息，请参阅 [限制对 Amazon S3 源的访问](private-content-restricting-access-to-s3.md)。

## 在自定义源上限制对文件的访问
<a name="forward-custom-headers-restrict-access"></a>

如果您使用自定义源，则可以选择设置自定义标头来限制访问。要使 CloudFront 从自定义源获取文件，CloudFront 必须使用标准 HTTP（或 HTTPS）请求访问这些文件。但是，使用自定义标头，您可以进一步限制对内容的访问，使得用户只能通过 CloudFront 进行访问而无法直接访问。虽然此步骤未要求使用签名 URL，但我们建议使用。

如需要求用户通过 CloudFront 访问内容，请在 CloudFront 分配中更改以下设置：

**源自定义标头**  
将 CloudFront 配置为将自定义标头转发到源。请参阅 [配置 CloudFront 以便向源请求添加自定义标头](add-origin-custom-headers.md#add-origin-custom-headers-configure)。

**查看器协议策略**  
将分配配置为要求查看器使用 HTTPS 访问 CloudFront。请参阅 [查看器协议策略](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy)。

**源协议策略**  
将分配配置为要求 CloudFront 与查看器使用相同协议来向源转发请求。请参阅 [协议（仅自定义源）](DownloadDistValuesOrigin.md#DownloadDistValuesOriginProtocolPolicy)。

进行了这些更改之后，在自定义源上更新应用程序，以仅接受满足如下条件的请求：其中包含您已将 CloudFront 配置为发送的自定义标头。

**查看器协议策略**和**源协议策略**的组合可确保在传输过程中对自定义标头进行加密。但是，建议您定期执行以下任务来轮换 CloudFront 转发到源的自定义标头：

1. 更新 CloudFront 分配，开始将新标头转发至自定义源。

1. 更新应用程序以接受新标头，从而确认请求来自 CloudFront。

1. 当请求不再包含您要替换的标头时，请更新应用程序以便不再接受旧标头，从而确认请求来自 CloudFront。

# 指定可以创建签名 URL 和签名 Cookie 的签署人
<a name="private-content-trusted-signers"></a>

**Topics**
+ [在可信密钥组（推荐）和AWS 账户之间进行选择](#choosing-key-groups-or-AWS-accounts)
+ [为签署人创建密钥对](#private-content-creating-cloudfront-key-pairs)
+ [重新设置私有密钥的格式（仅限 .NET 和 Java）](#private-content-reformatting-private-key)
+ [将签署人添加到分配](#private-content-adding-trusted-signers)
+ [轮换密钥对](#private-content-rotating-key-pairs)

要创建签名 URL 或签名 Cookie，您需要一个*签署人*。签署人可以是您在 CloudFront 中创建的受信密钥组，也可以是包含 CloudFront 密钥对的 AWS 账户。建议您使用带有签名 URL 和签名 Cookie 的可信密钥组。有关更多信息，请参阅 [在可信密钥组（推荐）和AWS 账户之间进行选择](#choosing-key-groups-or-AWS-accounts)。

签署人有两个目的：
+ 只要将可信签署人添加到分配中，CloudFront 就会开始要求查看器使用签名 URL 或签名 Cookie 访问文件。
+ 创建签名 URL 或签名 Cookie 时，使用来自签署人的密钥对中的私有密钥来签署 URL 或 Cookie 的一部分。当有人请求受限文件时，CloudFront 会将 URL 或 Cookie 中的签名与未签名 URL 或 Cookie 进行比较，以确认其未被篡改。CloudFront 还会验证 URL 或 Cookie 是否有效，即，未超过过期日期和时间。

指定签署人时，还可以通过将签署人添加到缓存行为，间接指定需要签名 URL 或签名 Cookie 的文件。如果分配只有一个缓存行为，则查看器必须使用签名 URL 或签名 Cookie 访问分配中的任何文件。如果创建了多个缓存行为，并将签署人添加到某些缓存行为而没有添加到其他缓存行为，则可要求查看器使用签名 URL 或签名 Cookie 访问某些文件而不是其他文件。

要指定允许创建签名 URL 或签名 Cookie 的签署人（私有密钥）并将签署人添加到 CloudFront 分配中，请执行以下任务：

1. 决定是使用可信密钥组还是AWS 账户作为签署人。我们建议使用可信密钥组。有关更多信息，请参阅 [在可信密钥组（推荐）和AWS 账户之间进行选择](#choosing-key-groups-or-AWS-accounts)。

1. 对于您在步骤 1 中选择的签署人，创建一个公有/私有密钥对。有关更多信息，请参阅 [为签署人创建密钥对](#private-content-creating-cloudfront-key-pairs)。

1. 如果使用 .NET 或 Java 创建签名 URL 或签名 Cookie，请重新设置私有密钥的格式。有关更多信息，请参阅 [重新设置私有密钥的格式（仅限 .NET 和 Java）](#private-content-reformatting-private-key)。

1. 在要为其创建签名 URL 或签名 Cookie 的分配中，指定签署人。有关更多信息，请参阅 [将签署人添加到分配](#private-content-adding-trusted-signers)。

## 在可信密钥组（推荐）和AWS 账户之间进行选择
<a name="choosing-key-groups-or-AWS-accounts"></a>

要使用签名 URL 或签名 Cookie，您需要一个*签署人*。签署人可以是您在 CloudFront 中创建的可信密钥组，也可以是包含 CloudFront 密钥对的AWS 账户。建议您使用可信密钥组，原因如下：
+ 对于 CloudFront 密钥组，您无需使用 AWS 账户 root 用户来管理 CloudFront 签名 URL 和签名 Cookie 的公有密钥。[AWS最佳实践](https://docs.aws.amazon.com/general/latest/gr/root-vs-iam.html#aws_tasks-that-require-root)建议您在不必使用 root 用户时就不要使用。
+ 借助 CloudFront 密钥组，您可以使用 CloudFront API 管理公有密钥、密钥组和可信签署人。您可以使用 API 自动执行密钥创建和密钥轮换。当您使用 AWS root 用户时，您必须使用 AWS 管理控制台 管理 CloudFront 密钥对，因此您无法自动执行此过程。
+ 由于您可以使用 CloudFront API 管理密钥组，因此还可以使用 AWS Identity and Access Management (IAM) 权限策略来限制允许不同用户执行的操作。例如，您可以允许用户上传公有密钥，但不能删除它们。或者，您可以允许用户删除公有密钥，但只有在满足某些条件时才能删除公有密钥，例如使用多重验证、从特定网络发送请求或在特定日期和时间范围内发送请求。
+ 通过 CloudFront 密钥组，您可以将更多的公有密钥与您的 CloudFront 分配关联，从而在如何使用和管理公有密钥方面提供更大的灵活性。默认情况下，您最多可以将四个密钥组与单一分配关联，并且一个密钥组中最多可以有五个公有密钥。

  当您使用 AWS 账户根用户管理 CloudFront 密钥对时，每个 AWS 账户最多只能拥有两个有效的 CloudFront 密钥对。

## 为签署人创建密钥对
<a name="private-content-creating-cloudfront-key-pairs"></a>

您用于创建 CloudFront 签名 URL 或签名 Cookie 的每个签署人都必须具有公有密钥/私有密钥对。签署人使用其私有密钥对 URL 或 Cookie 进行签名，而 CloudFront 使用公有密钥验证签名。

创建密钥对的方式取决于您是使用可信密钥组作为签署人（推荐），还是使用 CloudFront 密钥对。有关更多信息，请参阅以下部分。您创建的密钥对必须满足以下要求：
+ 它必须是 SSH-2 RSA 2048 或 ECDSA 256 密钥对。
+ 它必须采用 base64 编码的 PEM 格式。

为了帮助保护您的应用程序，建议您定期轮换密钥对。有关更多信息，请参阅 [轮换密钥对](#private-content-rotating-key-pairs)。

### 为可信密钥组创建密钥对（推荐）
<a name="create-key-pair-and-key-group"></a>

要为可信密钥组创建密钥对，请执行以下步骤：

1. 创建公有/私有密钥对。

1. 将公有密钥上传到 CloudFront。

1. 将公有密钥添加到 CloudFront 密钥组。

有关更多信息，请参阅以下流程。<a name="private-content-uploading-cloudfront-public-key-procedure"></a>

**创建密钥对**
**注意**  
以下步骤使用 OpenSSL 作为一种密钥对创建方法的示例。还有许多其它方法可以创建 RSA 或 ECDSA 密钥对。

1. 运行以下示例命令之一：
   + 以下示例命令使用 OpenSSL 生成长度为 2048 位的 RSA 密钥对，并将其保存到名为 `private_key.pem` 的文件中。

     ```
     openssl genrsa -out private_key.pem 2048
     ```
   + 下面的示例命令使用 OpenSSL 通过 `prime256v1` 曲线生成一个 ECDSA 密钥对，并保存到名为 `private_key.pem` 的文件中。

     ```
     openssl ecparam -name prime256v1 -genkey -noout -out privatekey.pem
     ```

1. 生成的文件同时包含公有密钥和私有密钥。以下示例命令从名为 `private_key.pem` 的文件中提取公有密钥。

   ```
   openssl rsa -pubout -in private_key.pem -out public_key.pem
   ```

   您稍后在以下过程中上传公有密钥（在 `public_key.pem` 文件中）。

**将公有密钥上传到 CloudFront**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航菜单中，选择**公有密钥**。

1. 选择**创建公有密钥**。

1. 在**创建公有密钥**窗口中，执行以下操作：

   1. 对于**密钥名称**，键入一个名称以标识公有密钥。

   1. 对于**密钥值**，粘贴公有密钥。如果按照上述过程中的步骤操作，则公有密钥位于名为 `public_key.pem` 的文件中。要复制和粘贴公有密钥的内容，您可以：
      + 在 macOS 或 Linux 命令行上使用 **cat** 命令，如下所示：

        ```
        cat public_key.pem
        ```

        复制该命令的输出，然后将其粘贴到**密钥值**字段中。
      + 使用记事本（在 Windows 上）或 TextEdit（在 macOS 上）等明文编辑器打开 `public_key.pem` 文件。复制文件的内容，然后将其粘贴到**密钥值**字段中。

   1. （可选）对于**注释**，请添加注释以描述公有密钥。

   完成后，选择**添加**。

1. 记录公有密钥 ID。稍后在创建签名 URL 或签名 Cookie 时使用它作为 `Key-Pair-Id` 字段的值。

**将公有密钥添加到密钥组**

1. 通过 打开 CloudFront 控制台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)

1. 在导航菜单中，选择**密钥组**。

1. 选择**添加密钥组**。

1. 在**创建密钥组**页上，执行以下操作：

   1. 对于**密钥组名称**，键入一个名称以标识密钥组。

   1. （可选）对于**注释**，键入注释以描述密钥组。

   1. 对于**公有密钥**，选择要添加到密钥组的公有密钥，然后选择**添加**。对要添加到密钥组的每个公有密钥重复此步骤。

1. 选择**创建密钥组**。

1. 记录密钥组名称。稍后可以使用它将密钥组与 CloudFront 分配中的缓存行为相关联。（在 CloudFront API 中，您使用密钥组 ID 将密钥组与缓存行为相关联。）

### 创建 CloudFront 密钥对（不建议这样做，需要AWS 账户根用户权限）
<a name="create-key-pair-aws-account"></a>

**重要**  
建议您为可信密钥组创建公有密钥，而不是执行以下步骤。有关为签名 URL 和签名 Cookie 创建公有密钥的推荐方法，请参阅[为可信密钥组创建密钥对（推荐）](#create-key-pair-and-key-group)。

您可以通过以下方式创建 CloudFront 密钥对：
+ 在 AWS 管理控制台 中创建密钥对并下载私有密钥。请参见以下过程。
+ 通过使用诸如 OpenSSL 等应用程序创建 RSA 密钥对，然后将公有密钥上传到 AWS 管理控制台。有关创建 RSA 密钥对的更多信息，请参阅[为可信密钥组创建密钥对（推荐）](#create-key-pair-and-key-group)。<a name="private-content-creating-cloudfront-key-pairs-procedure"></a>

**要在 中创建 CloudFront 密钥对AWS 管理控制台**

1. 使用 AWS 账户 root 用户登录 AWS 管理控制台。
**重要**  
IAM 用户无法创建 CloudFront 密钥对。必须使用 root 用户凭证登录才能创建密钥对。

1. 选择您的账户名称，然后选择**我的安全凭证**。

1. 选择 **CloudFront 密钥对**。

1. 确认没有或仅拥有一个有效的密钥对。如果已有两个有效的密钥对，则无法创建密钥对。

1. 选择**创建新的密钥对**。
**注意**  
您还可以选择创建自己的密钥对并上传公钥。CloudFront 密钥对支持 1024、2048 或 4096 位密钥。

1. 在**创建密钥对**对话框中，选择**下载私有密钥文件**，然后将该文件保存在您的计算机上。
**重要**  
将 CloudFront 密钥对的私有密钥保存在安全的位置，并设置对文件的权限，以便只有所需的管理员可读取它。如果某人获取您的私有密钥，则他们可以生成有效的签名 URL 和签名 Cookie，并下载您的内容。您不能再次获得私有密钥，因此，如果您丢失或删除了它，则必须创建新的 CloudFront 密钥对。

1. 记录密钥对的密钥对 ID。(在 AWS 管理控制台 中，这称为**访问密钥 ID**。) 创建签名 URL 或签名 Cookie 时会用到它。

## 重新设置私有密钥的格式（仅限 .NET 和 Java）
<a name="private-content-reformatting-private-key"></a>

如果使用 .NET 或 Java 创建签名 URL 或签名 Cookie，则不能以默认 PEM 格式使用密钥对中的私有密钥来创建签名。而是执行以下操作：
+ **.NET 框架** – 将私有密钥转换成 .NET 框架使用的 XML 格式。可使用多种工具。
+ **Java** – 将私有密钥转换成 DER 格式。执行此操作的一种方法是使用以下 OpenSSL 命令。在以下命令中，`private_key.pem` 是包含 PEM 格式的私有密钥的文件的名称，`private_key.der` 是运行该命令后包含 DER 格式的私有密钥的文件的名称。

  ```
  openssl pkcs8 -topk8 -nocrypt -in private_key.pem -inform PEM -out private_key.der -outform DER
  ```

  要确保编码器正常工作，将 Bouncy Castle Java 加密术 API 的 JAR 添加到您的项目中，然后添加 Bouncy Castle 提供商。

## 将签署人添加到分配
<a name="private-content-adding-trusted-signers"></a>

签署人是可以为分配创建签名 URL 和签名 Cookie 的可信密钥组（推荐）或 CloudFront 密钥对。要在 CloudFront 分配中使用签名 URL 或签名 Cookie，您必须指定签署人。

签署人与缓存行为相关联。这样，您就可以要求对同一分配中的某些文件使用签名 URL 或签名 Cookie，对另一些文件则不使用。仅对于与相应缓存行为关联的文件，分配才需要签名 URL 或 Cookie。

同样，签署人只能为与相应缓存行为相关联的文件签署 URL 或 Cookie。例如，如果您有两个签署人，他们分别针对两个不同的缓存行为，那么这两个签署人均不能为与另一个缓存行为相关联的文件创建签名 URL 或 Cookie。

**重要**  
在将签署人添加到分配之前，请执行以下操作：  
仔细定义缓存行为中的路径模式和缓存行为的顺序，以便不会授予用户意外访问您的内容的权限或阻止他们访问您希望每个人都可用的内容。  
例如，假设请求匹配两个缓存行为的路径模式。第一个缓存行为不要求签名 URL 或签名 Cookie，而第二个缓存行为有此要求。这种情况下，用户不需使用签名 URL 或签名 Cookie 即可访问文件，因为 CloudFront 处理的缓存行为与第一个相符的条件相关联。  
更多有关路径模式的信息，请参阅 [路径模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)。
对于您已经用于分发内容的分配，请确保您已准备好开始生成签名 URL 和签名 Cookie，然后再添加签署人。添加签署人时，CloudFront 拒绝不包含有效签名 URL 或签名 Cookie 的请求。

您可以使用 CloudFront 控制台或 CloudFront API 将签名者添加到您的分配中。

------
#### [ Console ]

以下步骤显示如何将可信密钥组添加为签署人。您也可以将AWS 账户添加为可信签署人，但不建议这样做。<a name="private-content-adding-trusted-signers-console-procedure"></a>

**使用控制台将签署人添加到分配**

1. 记录要用作可信签署人的密钥组的密钥组 ID。有关更多信息，请参阅[为可信密钥组创建密钥对（推荐）](#create-key-pair-and-key-group)。

1. 通过 打开 CloudFront 控制台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)

1. 选择要使用签名 URL 或签名 Cookie 保护其文件的分配。
**注意**  
要将签署人添加到新分配，请在创建分配时指定在步骤 6 中描述的相同设置。

1. 选择 **Behaviors** 选项卡。

1. 选择其路径模式与要使用签名 URL 或签名 Cookie 保护的文件相匹配的缓存行为，然后选择**编辑**。

1. 在**编辑行为**页上，执行以下操作：

   1. 对于**限制查看器访问(使用签名 URL 或签名 Cookie)**，选择**是**。

   1. 对于**可信密钥组或可信签署人**，选择**可信密钥组**。

   1. 对于**可信密钥组**，选择要添加的密钥组，然后选择**添加**。如果要添加多个密钥组，请重复此操作。

1. 选择**是，编辑**以更新缓存行为。

------
#### [ API ]

您可以使用 CloudFront API 将可信密钥组添加为签署人。您可以将签署人添加到现有分配或新分配。在这两种情况下，在 `TrustedKeyGroups` 元素中指定适用的值。

您也可以将AWS 账户添加为可信签署人，但不建议这样做。

请参阅《Amazon CloudFront API 参考》**中的以下主题：
+ **更新现有的分配** – [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)
+ **创建新的分配** – [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html)

------

## 轮换密钥对
<a name="private-content-rotating-key-pairs"></a>

建议您定期轮换（更改）签名 URL 和签名 Cookie 的密钥对。要轮换用于创建签名 URL 或签名 Cookie 的密钥对，而不使尚未过期的 URL 或 Cookie 失效，请执行以下任务：

1. 创建新的密钥对，然后将公有密钥添加到密钥组。有关更多信息，请参阅 [为可信密钥组创建密钥对（推荐）](#create-key-pair-and-key-group)。

1. 如果您在上一步骤中创建了新密钥组，请[将密钥组作为签署人添加到分配中](#private-content-adding-trusted-signers)。
**重要**  
不要从密钥组中删除任何现有公有密钥，也不要从分配中删除任何密钥组。只添加新的密钥组。

1. 更新您的应用程序，以使用新密钥对中的私有密钥创建签名。确认使用新私有密钥签署的签名 URL 或 Cookie 有效。

1. 等待使用之前的私有密钥对签名的 URL 或 Cookie 中的过期日期已过。然后，从密钥组中删除旧公有密钥。如果您在步骤 2 中创建了新的密钥组，请从分配中删除旧密钥组。

# 决定使用签名 URL 还是签名 Cookie
<a name="private-content-choosing-signed-urls-cookies"></a>

CloudFront 签名 URL 和签名 Cookie 提供相同的基本功能：它们允许您控制哪些用户可访问您的内容。如果您希望通过 CloudFront 提供私有内容，但尚未决定是使用签名 URL 还是签名 Cookie，请考虑以下因素。

在以下情况下使用签名 URL：
+ 您希望限制对单个文件的访问，例如应用程序的安装程序下载。
+ 用户使用不支持 Cookie 的客户端 (例如，自定义 HTTP 客户端)。

在以下情况下使用签名 Cookie：
+ 您希望提供对多个限制文件的访问，例如，HLS 格式视频的所有文件或者网站订户区域中的所有文件。
+ 您不希望更改当前 URL。

如果当前未使用签名 URL，并且未签名 URL 包含以下任一查询字符串参数，则不能使用签名 URL 或签名 Cookie：
+ `Expires`
+ `Policy`
+ `Signature`
+ `Key-Pair-Id`

CloudFront 假定包含任何这些查询字符串参数的 URL 是签名 URL，因此不会再查看签名 Cookie。

## 同时使用签名 URL 和签名 Cookie
<a name="private-content-using-signed-urls-and-cookies"></a>

签名 URL 优先于签名 Cookie。如果同时使用签名 URL 和签名 Cookie 来控制对相同文件的访问，并且查看器使用签名 URL 来请求文件，则 CloudFront 将仅基于签名 URL 确定是否向查看器返回文件。

# 使用签名 URL
<a name="private-content-signed-urls"></a>

签名 URL 包括额外的信息，例如，过期日期和时间，为您提供内容访问方面的更多控制权。该额外信息出现在策略声明中，且是基于标准策略或自定义策略。标准策略和自定义策略之间的差别将在接下来的两节中予以说明。

**注意**  
针对相同分配，您可以使用标准策略创建一些签名 URL 以及使用自定义策略创建一些签名 URL。

**Topics**
+ [决定为签名 URL 使用标准策略还是自定义策略](#private-content-choosing-canned-custom-policy)
+ [签名 URL 的工作方式](#private-content-how-signed-urls-work)
+ [决定签名 URL 的有效时间长度](#private-content-overview-choosing-duration)
+ [CloudFront 何时检查签名 URL 中的过期日期和时间](#private-content-check-expiration)
+ [代码示例和第三方工具](#private-content-overview-sample-code)
+ [使用标准策略创建签名 URL](private-content-creating-signed-url-canned-policy.md)
+ [使用自定义策略创建签名 URL](private-content-creating-signed-url-custom-policy.md)

## 决定为签名 URL 使用标准策略还是自定义策略
<a name="private-content-choosing-canned-custom-policy"></a>

创建签名 URL 时，需要编写 JSON 格式的策略声明，以指定对签名 URL 的限制，例如，URL 的有效期。可以使用标准策略或自定义策略。以下是标准策略和自定义策略的比较：


****  

| 说明 | 标准策略 | 自定义策略 | 
| --- | --- | --- | 
| 可对多个文件重复使用策略声明。要重复使用策略声明，您必须在 `Resource` 对象中使用通配符。有关更多信息，请参阅[在使用自定义策略的签名 URL 的策略声明中指定的值](private-content-creating-signed-url-custom-policy.md#private-content-custom-policy-statement-values)。 | 否 | 是 | 
| 可指定用户开始访问内容的日期和时间。 | 否 | 是（可选） | 
| 可指定用户无法再访问内容的日期和时间。 | 是 | 是 | 
| 可指定能够访问内容的用户的 IP 地址或 IP 地址范围。 | 否 | 是（可选） | 
| 签名 URL 包括策略的 Base64 编码版本，这会导致更长的 URL。 | 否 | 是 | 

有关使用*标准*策略创建签名 URL 的信息，请参阅[使用标准策略创建签名 URL](private-content-creating-signed-url-canned-policy.md)。

有关使用*自定义*策略创建签名 URL 的信息，请参阅[使用自定义策略创建签名 URL](private-content-creating-signed-url-custom-policy.md)。

## 签名 URL 的工作方式
<a name="private-content-how-signed-urls-work"></a>

以下概述了您如何为签名 URL 配置 CloudFront 和 Amazon S3，以及在用户使用签名 URL 请求文件时 CloudFront 如何响应。

1. 在 CloudFront 分配中，指定一个或多个可信密钥组，这些密钥组包含 CloudFront 可用来验证 URL 签名的公有密钥。您可以使用相应的私有密钥对 URL 进行签名。

   CloudFront 支持带有 RSA 2048 和 ECDSA 256 密钥签名的已签名 URL。

   有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

1. 开发应用程序，以确定用户是否应拥有对您的内容的访问权，以及为您希望限制访问的文件或应用程序的某些部分创建签名 URL。有关更多信息，请参阅以下主题：
   + [使用标准策略创建签名 URL](private-content-creating-signed-url-canned-policy.md)
   + [使用自定义策略创建签名 URL](private-content-creating-signed-url-custom-policy.md)

1. 用户请求您要求对其使用签名 URL 的文件。

1. 应用程序验证用户是否有权访问文件：他们已登录、已付费访问内容或已满足一些其他访问要求。

1. 您的应用程序创建并返回签名 URL 给用户。

1. 签名 URL 允许用户下载或流式传输内容。

   此步骤是自动的；用户通常不必做任何额外的事情以访问内容。例如，如果用户是在 Web 浏览器中访问您的内容，那么您的应用程序会将签名 URL 发回浏览器。浏览器立即在无用户干预的情况下使用签名 URL 访问 CloudFront 边缘缓存中的文件。

1. CloudFront 使用公有密钥验证签名并确认该 URL 未被篡改。如果签名无效，则请求将被拒绝。

   如果签名有效，CloudFront 将查看 URL 中的策略声明（如果使用标准策略，则构造一个），以确认该请求仍然有效。例如，如果您为该 URL 指定了开始和结束日期及时间，CloudFront 会确认用户是否是在您希望允许访问的时间段尝试访问您的内容。

   如果请求满足策略声明中的要求，CloudFront 将执行标准操作：确定文件是否已位于边缘缓存中，必要时将请求转发到源，然后向用户返回文件。

**注意**  
如果未签名的 URL 包含查询字符串参数，请确保将它们包含在 URL 中您签名的部分内。如果在签署后将查询字符串添加到签名 URL 中，该 URL 将返回 HTTP 403 状态。

## 决定签名 URL 的有效时间长度
<a name="private-content-overview-choosing-duration"></a>

您可使用只在很短时间内有效（可能只有几分钟）的签名 URL 来分配私有内容。有效时间如此短的签名 URL 适用于出于特定的目的即时向用户分发内容，如按需向客户分发租赁的电影或下载的音乐。如果您的签名 URL 有效期较短，您将可能希望使用您开发的应用程序自动生成它们。当用户开始下载文件或开始播放媒体文件时，CloudFront 将比较 URL 中的过期时间和当前时间，以确定 URL 是否仍然有效。

您也可使用有效时间较长 (可能数年) 的签名 URL 来分配私有内容。有效时间较长的签名 URL 适用于向已知用户分发私有内容，如向投资者分发业务计划或向员工分发培训材料等。您可以开发一个应用程序来为您生成这些长期签名 URL。

## CloudFront 何时检查签名 URL 中的过期日期和时间
<a name="private-content-check-expiration"></a>

在发出 HTTP 请求时，CloudFront 检查签名 URL 中的过期日期和时间。如果客户端刚好在过期时间之前开始下载大型文件，即使在下载过程中到了过期时间，该下载也应该完成。如果 TCP 连接断开，并且客户端试图在过期时间到期后重新开始下载，则下载将会失败。

如果客户端使用 Range GET 来获取较小的文件，在过期时间到期后发生的任何 GET 请求将会失败。有关 Range GET 的更多信息，请参阅 [CloudFront 如何处理对象的部分请求 (Range GET)。](RangeGETs.md)。

## 代码示例和第三方工具
<a name="private-content-overview-sample-code"></a>

有关创建签名 URL 的散列和签署部分的代码示例，请参阅以下主题：
+ [使用 Perl 创建 URL 签名](CreateURLPerl.md)
+ [使用 PHP 创建 URL 签名](CreateURL_PHP.md)
+ [使用 C\$1 和 .NET Framework 创建 URL 签名](CreateSignatureInCSharp.md)
+ [使用 Java 创建 URL 签名](CFPrivateDistJavaDevelopment.md)

# 使用标准策略创建签名 URL
<a name="private-content-creating-signed-url-canned-policy"></a>

要使用标准策略创建签名 URL，请完成以下步骤。<a name="private-content-creating-signed-url-canned-policy-procedure"></a>

**要使用标准策略创建签名 URL**

1. 如果您使用 .NET 和 Java 创建签名 URL，而且，如果您尚未将密钥对私有密钥的格式从默认 .pem 格式重新设置为与 .NET 和 Java 兼容的格式，那么现在就开始设置吧。有关更多信息，请参阅 [重新设置私有密钥的格式（仅限 .NET 和 Java）](private-content-trusted-signers.md#private-content-reformatting-private-key)。

1. 连接以下值。您可以使用此示例签名 URL 中的格式。

   ```
   https://d111111abcdef8.cloudfront.net/image.jpg?color=red&size=medium&Expires=1767290400&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~-ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmatEXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6&Key-Pair-Id=K2JCJMDEHXQW5F
   ```

   删除所有空格（包括制表符和换行符）。您可能需要在应用程序代码的字符串中包括换码符。所有值的类型均为 `String`。  
**1. *文件的基本 URL***  
基本 URL 是您将用于访问文件的 CloudFront URL，如果您不使用签名 URL，包括您自己的查询字符串参数（如果有）。在上一示例中，基本 URL 为 `https://d111111abcdef8.cloudfront.net/image.jpg`。有关适用于分配的 URL 格式的更多信息，请参阅[在 CloudFront 中自定义文件的 URL 格式](LinkFormat.md)。  
   + 以下 CloudFront URL 适用于分配中的图像文件（使用 CloudFront 域名）。请注意，`image.jpg` 是在 `images` 目录中。URL 中文件的路径必须与您的 HTTP 服务器或 Amazon S3 存储桶中文件的路径匹配。

     `https://d111111abcdef8.cloudfront.net/images/image.jpg`
   + 以下 CloudFront URL 包含查询字符串：

     `https://d111111abcdef8.cloudfront.net/images/image.jpg?size=large`
   + 以下 CloudFront URL 适用于分配中的映像文件。均使用备用域名。第二个包括查询字符串：

     `https://www.example.com/images/image.jpg`

     `https://www.example.com/images/image.jpg?color=red`
   + 以下 CloudFront URL 用于使用备用域名和 HTTPS 协议的分配中的映像文件：

     `https://www.example.com/images/image.jpg`  
** 2.`?`**  
`?` 表示查询参数位于基本 URL 后面。即使您未指定任何查询参数，也应包括 `?`。  
您可以按任意顺序指定以下查询参数。  
**3. *您的查询字符串参数（如果有*`&`**  
（可选）您可以输入自己的查询字符串参数。为此，请在每个参数之间添加一个 `&` 字符，例如 `color=red&size=medium`。您可以在 URL 中按任意顺序指定查询字符串参数。  
您的查询字符串参数不能命名为 `Expires`、`Signature` 或 `Key-Pair-Id`。  
** 4. `Expires=`*Unix 时间格式（按秒计）和协调世界时（UTC）格式的日期和时间***  
您希望 URL 不再允许访问文件的日期和时间。  
指定 Unix 时间格式（以秒为单位） 和协调通用时间 (UTC) 格式的过期日期和时间。例如，UTC 时间 2026 年 1 月 1 日上午 10 点转换为 Unix 时间格式就是 `1767290400`，如本主题开头的示例所示。  
要使用纪元时间，请指定 64 位整数表示日期，该日期不得晚于 `9223372036854775807`（2262 年 4 月 11 日，星期五，23:47:16.854 UTC）。  
  
有关 UTC 的信息，请参阅 [RFC 3339，Internet 上的日期和时间：时间戳](https://tools.ietf.org/html/rfc3339)。  
** 5. `&Signature=`*策略声明经过哈希处理和签署后的版本***  
JSON 策略声明经过哈希处理、签署和 Base64 编码的版本。有关更多信息，请参阅 [为使用标准策略的签名 URL 创建签名](#private-content-canned-policy-creating-signature)。  
** 6. `&Key-Pair-Id=`*CloudFront 公有密钥的公有密钥 ID，您使用该公有密钥的对应私有密钥来生成签名***  
CloudFront 公有密钥的 ID，例如，`K2JCJMDEHXQW5F`。公有密钥 ID 告诉 CloudFront 要使用哪个公有密钥来验证签名的 URL。CloudFront 将比较签名中的信息与策略声明中的信息，以确认该 URL 没有被篡改。  
此公有密钥必须属于作为分配中可信签署人的密钥组。有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

## 为使用标准策略的签名 URL 创建签名
<a name="private-content-canned-policy-creating-signature"></a>

要为使用标准策略的签名 URL 创建签名，请完成以下步骤：

**Topics**
+ [为使用标准策略的签名 URL 创建策略声明](#private-content-canned-policy-creating-policy-statement)
+ [为使用标准策略的签名 URL 创建签名](#private-content-canned-policy-signing-policy-statement)

### 为使用标准策略的签名 URL 创建策略声明
<a name="private-content-canned-policy-creating-policy-statement"></a>

使用标准策略创建签名 URL 时，`Signature` 参数是策略声明经过哈希处理和签署的版本。对于使用标准策略的签名 URL，您没有像对待使用自定义策略的签名 URL 那样将策略声明包含在 URL 内。要创建策略声明，请执行以下过程。<a name="private-content-canned-policy-creating-policy-statement-procedure"></a>

**为使用标准策略的签名 URL 创建策略声明**

1. 使用以下 JSON 格式以及 UTF-8 字符编码构建策略声明。根据指定，准确包括所有标点符号和其他文本值。有关 `Resource` 和 `DateLessThan` 参数的信息，请参阅 [在使用标准策略的签名 URL 的策略声明中指定的值](#private-content-canned-policy-statement-values)。

   ```
   {
       "Statement": [
           {
               "Resource": "base URL or stream name",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime": ending date and time in Unix time format and UTC
                   }
               }
           }
       ]
   }
   ```

1. 删除策略声明中的所有空格（包括制表符和换行符）。您可能需要在应用程序代码的字符串中包括换码符。

#### 在使用标准策略的签名 URL 的策略声明中指定的值
<a name="private-content-canned-policy-statement-values"></a>

为标准策略创建策略声明时，请指定以下值。

**资源**  
只能为 `Resource` 指定一个值。
包含查询字符串（如果有）的基本 URL，但不包括 CloudFront `Expires`、`Signature` 和 `Key-Pair-Id` 参数，例如：  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
请注意以下几点：  
+ **协议** – 该值必须以 `http://` 或 `https://` 开头。
+ **查询字符串参数** – 如果没有查询字符串参数，请省略问号。
+ **备用域名** – 如果在 URL 中指定备用域名 (CNAME)，则必须在引用网页或应用程序中的文件时指定备用域名。切勿为对象指定 Amazon S3 URL。

**DateLessThan**  
Unix 时间格式（以秒为单位）和协调通用时间 (UTC) 格式的 URL 过期日期和时间。例如，2026 年 1 月 1 日上午 10 点 UTC 转换为 Unix 时间格式就是 1767290400。  
该值必须与签名 URL 中的 `Expires` 查询字符串参数相匹配。切勿用引号将该值括起来。  
有关更多信息，请参阅 [CloudFront 何时检查签名 URL 中的过期日期和时间](private-content-signed-urls.md#private-content-check-expiration)。

#### 使用标准策略的签名 URL 的示例策略声明
<a name="private-content-canned-policy-creating-policy-statement-example"></a>

当您在签名 URL 中使用以下示例策略语句时，用户将可以访问文件 `https://d111111abcdef8.cloudfront.net/horizon.jpg`，直至 UTC 时间 2026 年 1 月 1 日上午 10 点：

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/horizon.jpg?size=large&license=yes",
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

### 为使用标准策略的签名 URL 创建签名
<a name="private-content-canned-policy-signing-policy-statement"></a>

要为签名 URL 中的 `Signature` 参数创建值，请对在[为使用标准策略的签名 URL 创建策略声明](#private-content-canned-policy-creating-policy-statement)中创建的策略声明进行哈希处理并签署。

有关额外信息以及如何哈希、签署及编码策略声明的示例，请参阅：
+ [用于 Base64 编码和加密的 Linux 命令和 OpenSSL](private-content-linux-openssl.md)
+ [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-canned-policy-creating-signature-download-procedure"></a>

**选项 1：使用标准策略创建签名**

1. 使用 SHA-1 哈希函数和生成的 RSA 或 ECDSA 私有密钥，对在[为使用标准策略的签名 URL 创建策略声明](#private-content-canned-policy-creating-policy-statement-procedure)过程中创建的策略声明进行哈希处理并签名。使用不再包含空格的策略声明版本。

   对于哈希函数所需的私有密钥，请使用其公有密钥位于分配的活动可信密钥组中的私有密钥。
**注意**  
您用于哈希及签署策略声明的方法取决于您的编程语言和平台。有关代码示例，请参阅 [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)。

1. 删除经过哈希处理并签署的字符串中的空格（包括制表符和换行符）。

1. 使用 MIME Base64 编码对字符串进行 Base64 编码。有关更多信息，请参阅 *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies* 中的 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)。

1. 用有效的字符替换 URL 查询字符串中的无效字符。下表列出了无效和有效字符。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html)

1. 将结果值附在签名 URL 的 `&Signature=` 之后，然后返回 [要使用标准策略创建签名 URL](#private-content-creating-signed-url-canned-policy-procedure)，以完成签名 URL 的各部分的串连。

# 使用自定义策略创建签名 URL
<a name="private-content-creating-signed-url-custom-policy"></a>

要使用自定义策略创建签名 URL，请完成以下步骤。<a name="private-content-creating-signed-url-custom-policy-procedure"></a>

**要使用自定义策略创建签名 URL**

1. 如果您使用 .NET 和 Java 创建签名 URL，而且，如果您尚未将密钥对私有密钥的格式从默认 .pem 格式重新设置为与 .NET 和 Java 兼容的格式，那么现在就开始设置吧。有关更多信息，请参阅 [重新设置私有密钥的格式（仅限 .NET 和 Java）](private-content-trusted-signers.md#private-content-reformatting-private-key)。

1. 连接以下值。您可以使用此示例签名 URL 中的格式。

   

   ```
   https://d111111abcdef8.cloudfront.net/image.jpg?color=red&size=medium&Policy=eyANCiAgICEXAMPLEW1lbnQiOiBbeyANCiAgICAgICJSZXNvdXJjZSI6Imh0dHA6Ly9kemJlc3FtN3VuMW0wLmNsb3VkZnJvbnQubmV0L2RlbW8ucGhwIiwgDQogICAgICAiQ29uZGl0aW9uIjp7IA0KICAgICAgICAgIklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIyMDcuMTcxLjE4MC4xMDEvMzIifSwNCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI5Njg2MDE3Nn0sDQogICAgICAgICAiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjEyOTY4NjAyMjZ9DQogICAgICB9IA0KICAgfV0gDQp9DQo&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~-ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmatEXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6&Key-Pair-Id=K2JCJMDEHXQW5F
   ```

   删除所有空格（包括制表符和换行符）。您可能需要在应用程序代码的字符串中包括换码符。所有值的类型均为 `String`。  
**1. *文件的基本 URL***  
基本 URL 是您将用于访问文件的 CloudFront URL，如果您不使用签名 URL，包括您自己的查询字符串参数（如果有）。在上一示例中，基本 URL 为 `https://d111111abcdef8.cloudfront.net/image.jpg`。有关适用于分配的 URL 格式的更多信息，请参阅[在 CloudFront 中自定义文件的 URL 格式](LinkFormat.md)。  
以下示例显示了您为分配指定的值。  
   + 以下 CloudFront URL 适用于分配中的图像文件（使用 CloudFront 域名）。请注意，`image.jpg` 是在 `images` 目录中。URL 中文件的路径必须与您的 HTTP 服务器或 Amazon S3 存储桶中文件的路径匹配。

     `https://d111111abcdef8.cloudfront.net/images/image.jpg`
   + 以下 CloudFront URL 包含查询字符串：

     `https://d111111abcdef8.cloudfront.net/images/image.jpg?size=large`
   + 以下 CloudFront URL 适用于分配中的映像文件。两者都使用备用域名，第二个包括查询字符串：

     `https://www.example.com/images/image.jpg`

     `https://www.example.com/images/image.jpg?color=red`
   + 以下 CloudFront URL 用于使用备用域名和 HTTPS 协议的分配中的映像文件：

     `https://www.example.com/images/image.jpg`  
**2.`?`**  
`?` 表示查询字符串参数位于基本 URL 后面。即使您未指定任何查询参数，也应包括 `?`。  
您可以按任意顺序指定以下查询参数。  
**3. *您的查询字符串参数（如果有*`&`**  
（可选）您可以输入自己的查询字符串参数。为此，请在每个参数之间添加一个 & 字符，例如 `color=red&size=medium`。您可以在 URL 中按任意顺序指定查询字符串参数。  
您的查询字符串参数不能命名为 `Policy`、`Signature` 或 `Key-Pair-Id`。
如果您添加自己的参数，请在每个参数后附加 `&`，包括最后一个参数。  
**4. `Policy=`*策略声明的 Base64 编码版本***  
您的策略声明采用 JSON 格式，删除了空格，然后进行 Base64 编码。有关更多信息，请参阅 [为使用自定义策略的签名 URL 创建策略声明](#private-content-custom-policy-statement)。  
策略语句控制签名 URL 授予用户的访问权限。它包括文件的 URL、过期日期和时间、URL 生效的可选日期和时间、允许访问文件的可选 IP 地址或 IP 地址范围。  
**5. `&Signature=`*策略声明经过哈希处理和签署后的版本***  
JSON 策略声明经过哈希处理、签署和 Base64 编码的版本。有关更多信息，请参阅 [为使用自定义策略的签名 URL 创建签名](#private-content-custom-policy-creating-signature)。  
**6. `&Key-Pair-Id=`*CloudFront 公有密钥的公有密钥 ID，您使用该公有密钥的对应私有密钥来生成签名***  
CloudFront 公有密钥的 ID，例如，`K2JCJMDEHXQW5F`。公有密钥 ID 告诉 CloudFront 要使用哪个公有密钥来验证签名的 URL。CloudFront 将比较签名中的信息与策略声明中的信息，以确认该 URL 没有被篡改。  
此公有密钥必须属于作为分配中可信签署人的密钥组。有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

## 为使用自定义策略的签名 URL 创建策略声明
<a name="private-content-custom-policy-statement"></a>

要为使用自定义策略的签名 URL 创建策略声明，请完成以下步骤。

有关以各种方式控制访问文件的示例策略声明，请参阅[使用自定义策略的签名 URL 的示例策略声明](#private-content-custom-policy-statement-examples)。<a name="private-content-custom-policy-creating-policy-procedure"></a>

**为使用自定义策略的签名 URL 创建策略声明**

1. 使用以下 JSON 格式构建策略声明。用自己的值替换小于 (`<`) 和大于 (`>`) 符号及其中的描述。有关更多信息，请参阅 [在使用自定义策略的签名 URL 的策略声明中指定的值](#private-content-custom-policy-statement-values)。

   ```
   {
       "Statement": [
           {
               "Resource": "<Optional but recommended: URL of the file>",
               "Condition": {
                   "DateLessThan": {
   	                "AWS:EpochTime": <Required: ending date and time in Unix time format and UTC>
                   },
                   "DateGreaterThan": {
   	                "AWS:EpochTime": <Optional: beginning date and time in Unix time format and UTC>
                   },
                   "IpAddress": {
   	                "AWS:SourceIp": "<Optional: IP address>"
                   }
               }
           }
       ]
   }
   ```

   请注意以下几点：
   + 您只能在策略中包含一个声明。
   + 使用 UTF-8 字符编码。
   + 根据指定，准确包括所有标点符号和参数名称。不接受参数名称的缩写。
   + `Condition` 部分中参数的顺序无关紧要。
   + 有关 `Resource`、`DateLessThan`、`DateGreaterThan` 和 `IpAddress` 值的信息，请参阅[在使用自定义策略的签名 URL 的策略声明中指定的值](#private-content-custom-policy-statement-values)。

1. 删除策略声明中的所有空格（包括制表符和换行符）。您可能需要在应用程序代码的字符串中包括换码符。

1. 使用 MIME Base64 编码对策略声明进行 Base64 编码。有关更多信息，请参阅 *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies* 中的 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)。

1. 用有效的字符替换 URL 查询字符串中的无效字符。下表列出了无效和有效字符。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html)

1. 将结果值附在签名 URL `Policy=` 之后。

1. 通过对策略声明进行哈希、签署及 Base64 编码处理，创建签名 URL 的签名。有关更多信息，请参阅 [为使用自定义策略的签名 URL 创建签名](#private-content-custom-policy-creating-signature)。

### 在使用自定义策略的签名 URL 的策略声明中指定的值
<a name="private-content-custom-policy-statement-values"></a>

针对自定义策略创建策略声明时，请指定以下值。

**资源**  
包含任何查询字符串的 URL，但不包括 CloudFront `Policy`、`Signature` 和 `Key-Pair-Id` 参数。例如：  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg\?size=large&license=yes`  
只能为 `Resource` 指定一个 URL 值。  
您可以忽略策略中的 `Resource` 参数，但这样做意味着拥有签名 URL 任何人都可以访问与您用于创建签名 URL 的密钥对相关联的任何**分配中的所有**文件。
请注意以下几点：  
+ **协议** – 该值必须以 `http://`、`https://` 或 `*://` 开头。
+ **查询字符串参数**：如果 URL 有查询字符串参数，请不要使用反斜杠字符（`\`）来转义查询字符串开头的问号字符（`?`）。例如：

  `https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`
+ **通配符** – 您可以在策略的 URL 中使用通配符。支持以下通配符：
  + 星号 (`*`)，匹配零个或多个字符
  + 问号 (`?`)，正好匹配一个字符

  当 CloudFront 将策略中的 URL 与 HTTP 请求中的 URL 进行匹配时，策略中的 URL 分为四个部分：协议、域、路径和查询字符串，如下所示：

  `[protocol]://[domain]/[path]\?[query string]`

  当您在策略的 URL 中使用通配符时，通配符匹配仅在包含该通配符的部分的界限内适用。例如，在策略中考虑此 URL:

  `https://www.example.com/hello*world`

  在此示例中，星号通配符 (`*`) 仅适用于路径部分，因此它与 URL `https://www.example.com/helloworld` 和 `https://www.example.com/hello-world` 匹配，但与 URL `https://www.example.net/hello?world` 不匹配。

  以下例外适用于通配符匹配的部分界限：
  + 路径部分中的尾部星号表示查询字符串部分中的星号。例如，`http://example.com/hello*` 等同于 `http://example.com/hello*\?*`。
  + 域部分中的尾部星号表示路径部分和查询字符串部分都有星号。例如，`http://example.com*` 等同于 `http://example.com*/*\?*`。
  + 策略中的 URL 可以忽略协议部分并在域部分中以星号开头。在这种情况下，协议部分被隐式设置为星号。例如，策略中的 URL `*example.com` 等同于 `*://*example.com/`。
  + 星号本身 (`"Resource": "*"`) 与任何 URL 都匹配。

  例如，策略中的值 `https://d111111abcdef8.cloudfront.net/*game_download.zip*` 与以下所有 URL 都匹配：
  + `https://d111111abcdef8.cloudfront.net/game_download.zip`
  + `https://d111111abcdef8.cloudfront.net/example_game_download.zip?license=yes`
  + `https://d111111abcdef8.cloudfront.net/test_game_download.zip?license=temp`
+ **备用域名** – 如果在策略的 URL 中指定备用域名 (CNAME)，则 HTTP 请求必须在网页或应用程序中使用该备用域名。请勿为策略中的文件指定 Amazon S3 URL。

**DateLessThan**  
Unix 时间格式（以秒为单位）和协调通用时间 (UTC) 格式的 URL 过期日期和时间。在策略中，切勿用引号将该值括起来。有关 UTC 的信息，请参阅 [Internet 上的日期和时间：时间戳](https://tools.ietf.org/html/rfc3339)。  
例如，2023 年 1 月 31 日上午 10 点 UTC 转换为 Unix 时间格式就是 1675159200。  
这是 `Condition` 部分唯一需要的参数。CloudFront 需要此值，以防止用户拥有对象的永久访问权。  
有关更多信息，请参阅[CloudFront 何时检查签名 URL 中的过期日期和时间](private-content-signed-urls.md#private-content-check-expiration)

**DateGreaterThan（可选）**  
Unix 时间格式（以秒为单位）和协调通用时间 (UTC) 格式的 URL 可选开始日期和时间。不允许用户在指定日期和时间或之前访问该文件。切勿用引号将该值括起来。

**IpAddress（可选）**  
发出 HTTP 请求的客户端的 IP 地址。请注意以下几点：  
+ 要允许任何 IP 地址访问文件，请省略 `IpAddress` 参数。
+ 可以指定一个 IP 地址或一个 IP 地址范围。如果客户端的 IP 地址在两个独立范围之一的范围内，您不能使用策略来允许访问。
+ 要允许从单个 IP 地址访问，可指定：

  `"`*IPv4 IP 地址*`/32"`
+ 必须采用标准 IPv4 CIDR 格式指定 IP 地址范围（例如，`192.0.2.0/24`）。有关更多信息，请参阅 [Classless Inter-domain Routing (CIDR): The Internet Address Assignment and Aggregation Plan](https://tools.ietf.org/html/rfc4632)。
**重要**  
不支持 IPv6 格式的 IP 地址，如 2001:0db8:85a3::8a2e:0370:7334。

  如果使用包含 `IpAddress` 的自定义策略，请勿为分配启用 IPv6。如果希望通过 IP 地址限制对某些内容的访问并支持其他内容的 IPv6 请求，可以创建两个分配。有关更多信息，请参阅 [启用 IPv6（查看器请求）](DownloadDistValuesGeneral.md#DownloadDistValuesEnableIPv6)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。

## 使用自定义策略的签名 URL 的示例策略声明
<a name="private-content-custom-policy-statement-examples"></a>

以下示例策略声明显示了如何控制对特定文件、目录中的所有文件或与密钥对 ID 有关的所有文件的访问。这些示例也显示了如何控制来自单个 IP 地址或 IP 地址范围的访问，以及如何防止用户在指定日期和时间后使用签名 URL。

如果复制并粘贴其中的任何示例，请删除任何空格（包括制表符和换行符），将值替换为自己的值，并在右大括号（`}`）后面包含一个换行符。

有关更多信息，请参阅 [在使用自定义策略的签名 URL 的策略声明中指定的值](#private-content-custom-policy-statement-values)。

**Topics**
+ [示例策略声明：从 IP 地址范围访问一个文件](#private-content-custom-policy-statement-example-one-object)
+ [示例策略声明：从 IP 地址范围访问一个目录中的所有文件](#private-content-custom-policy-statement-example-all-objects)
+ [示例策略声明：从一个 IP 地址访问与一个密钥对 ID 关联的所有文件](#private-content-custom-policy-statement-example-one-ip)

### 示例策略声明：从 IP 地址范围访问一个文件
<a name="private-content-custom-policy-statement-example-one-object"></a>

签名 URL 中的以下示例自定义策略指定用户可从范围 `192.0.2.0/24` 内的 IP 地址访问文件 `https://d111111abcdef8.cloudfront.net/game_download.zip`，直至 UTC 时间 2023 年 1 月 31 日上午 10 点：

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/game_download.zip",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675159200
                }
            }
        }
    ]
}
```

### 示例策略声明：从 IP 地址范围访问一个目录中的所有文件
<a name="private-content-custom-policy-statement-example-all-objects"></a>

以下示例自定义策略允许您为 `training` 目录中的任何文件创建签名 URL，如 `Resource` 参数中的星号通配符 (`*`) 所指示。用户可从范围 `192.0.2.0/24` 内的 IP 地址访问文件，直至 UTC 时间 2023 年 1 月 31 日上午 10 点：

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/training/*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675159200
                }
            }
        }
    ]
}
```

您在其中使用此策略的每个签名 URL 包括确定特定文件的 URL，例如：

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

### 示例策略声明：从一个 IP 地址访问与一个密钥对 ID 关联的所有文件
<a name="private-content-custom-policy-statement-example-one-ip"></a>

以下示例自定义策略允许您为与任何分配有关的任何文件创建签名 URL，如 `Resource` 参数中的星号通配符 (`*`) 所指示。签名 URL 必须使用 `https://` 协议，而不是 `http://`。用户必须使用 IP 地址 `192.0.2.10/32`。（CIDR 表示法中的值 `192.0.2.10/32` 指代单个 IP 地址 `192.0.2.10`。） 这些文件仅从 UTC 时间 2023 年 1 月 31 日上午 10 点到 UTC 时间 2023 年 2 月 2 日上午 10 点期间可用：

```
{
    "Statement": [
       {
            "Resource": "https://*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.10/32"
                },
                "DateGreaterThan": {
                    "AWS:EpochTime": 1675159200
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1675332000
                }
            }
        }
    ]
}
```

您在其中使用此策略的每个签名 URL 包括确定特定 CloudFront 分配中特定文件的 URL，例如：

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

签名 URL 还包括密钥对 ID，它必须与您在 URL 中指定的分配 (d111111abcdef8.cloudfront.net) 中的可信密钥组关联。

## 为使用自定义策略的签名 URL 创建签名
<a name="private-content-custom-policy-creating-signature"></a>

使用自定义策略的签名 URL 的签名是策略声明的哈希、签署及 Base64 编码版本。要为自定义策略创建签名，请完成以下步骤。

有关额外信息以及如何哈希、签署及编码策略声明的示例，请参阅：
+ [用于 Base64 编码和加密的 Linux 命令和 OpenSSL](private-content-linux-openssl.md)
+ [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-custom-policy-creating-signature-download-procedure"></a>

**选项 1：使用自定义策略创建签名**

1. 使用 SHA-1 哈希函数和生成的 RSA 或 ECDSA 私有密钥，对在[为使用自定义策略的签名 URL 创建策略声明](#private-content-custom-policy-creating-policy-procedure)过程中创建的 JSON 策略声明进行哈希处理并签名。使用不再包含空格但尚未进行 Base64 编码的策略声明版本。

   对于哈希函数所需的私有密钥，请使用其公有密钥位于分配的活动可信密钥组中的私有密钥。
**注意**  
您用于哈希及签署策略声明的方法取决于您的编程语言和平台。有关代码示例，请参阅 [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)。

1. 删除经过哈希处理并签署的字符串中的空格（包括制表符和换行符）。

1. 使用 MIME Base64 编码对字符串进行 Base64 编码。有关更多信息，请参阅 *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies* 中的 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)。

1. 用有效的字符替换 URL 查询字符串中的无效字符。下表列出了无效和有效字符。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html)

1. 将结果值附在签名 URL 的 `&Signature=` 之后，然后返回 [要使用自定义策略创建签名 URL](#private-content-creating-signed-url-custom-policy-procedure)，以完成签名 URL 的各部分的串连。

# 使用签名 Cookie
<a name="private-content-signed-cookies"></a>

当您不希望更改当前 URL，或者希望提供对多个限制文件（例如，网站订户区域中的所有文件）的访问权时，可使用 CloudFront 签名 Cookie 控制能够访问内容的人员。本主题介绍了使用签名 Cookie 时的注意事项，并介绍了如何使用标准策略和自定义策略设置签名 Cookie。

**Topics**
+ [决定为签名 Cookie 使用标准策略或自定义策略](#private-content-choosing-canned-custom-cookies)
+ [签名 Cookie 的工作方式](#private-content-how-signed-cookies-work)
+ [防止滥用签名 Cookie](#private-content-signed-cookie-misuse)
+ [CloudFront 何时检查签名 Cookie 中的过期日期和时间](#private-content-check-expiration-cookie)
+ [代码示例和第三方工具](#private-content-overview-sample-code-cookies)
+ [使用标准策略设置签名 Cookie](private-content-setting-signed-cookie-canned-policy.md)
+ [使用自定义策略设置签名 Cookie](private-content-setting-signed-cookie-custom-policy.md)
+ [使用 PHP 创建签名 Cookie](signed-cookies-PHP.md)

## 决定为签名 Cookie 使用标准策略或自定义策略
<a name="private-content-choosing-canned-custom-cookies"></a>

创建签名 Cookie 时，以 JSON 格式编写指定签名 Cookie 限制的策略声明，例如，URL 的有效期。可以使用标准策略或自定义策略。下表比较了标准策略和自定义策略：


****  

| 说明 | 标准策略 | 自定义策略 | 
| --- | --- | --- | 
| 可对多个文件重复使用策略声明。要重复使用策略声明，您必须在 `Resource` 对象中使用通配符。有关更多信息，请参阅[在使用自定义策略的签名 Cookie 的策略声明中指定的值](private-content-setting-signed-cookie-custom-policy.md#private-content-custom-policy-statement-cookies-values)。 | 否 | 是 | 
| 可指定用户开始访问内容的日期和时间 | 否 | 是（可选） | 
| 可指定用户无法再访问内容的日期和时间 | 是 | 是 | 
| 可指定能够访问内容的用户的 IP 地址或 IP 地址范围 | 否 | 是（可选） | 

有关使用标准策略创建签名 Cookie 的信息，请参阅[使用标准策略设置签名 Cookie](private-content-setting-signed-cookie-canned-policy.md)。

有关使用自定义策略创建签名 Cookie 的信息，请参阅[使用自定义策略设置签名 Cookie](private-content-setting-signed-cookie-custom-policy.md)。

## 签名 Cookie 的工作方式
<a name="private-content-how-signed-cookies-work"></a>

下面概述了如何为签名 Cookie 配置 CloudFront，并概述了当用户提交包含签名 Cookie 的请求时 CloudFront 如何响应。

1. 在 CloudFront 分配中，指定一个或多个可信密钥组，这些密钥组包含 CloudFront 可用来验证 URL 签名的公有密钥。您可以使用相应的私有密钥对 URL 进行签名。

   有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

1. 您开发应用程序，以确定用户是否应对您的内容拥有访问权限，如果是，则向查看器发送三个 `Set-Cookie` 标头。（每个 `Set-Cookie` 标头只能包含一个名称-值对，CloudFront 签名 Cookie 需要三个名称-值对。） 必须先向查看器发送 `Set-Cookie` 标头，查看器才能请求私有内容。如果对 Cookie 设置一个较短的过期时间，您可能还需要发送另外三个 `Set-Cookie` 标头以响应后续请求，从而确保用户继续拥有访问权限。

   通常情况下，CloudFront 分配至少拥有两个缓存行为，一个不需要身份验证，另一个则需要。站点安全部分的错误页面包含一个指向登录页面的重定向器或链接。

   如果将分配配置为根据 Cookie 缓存文件，CloudFront 则不会基于签名 Cookie 中的属性缓存单独的文件。

1. 用户登录您的网站，为内容付费或满足一些其他访问要求。

1. 应用程序在响应中返回 `Set-Cookie` 标头，查看器存储名称-值对。

1. 用户请求文件。

   用户的浏览器或其他查看器获取第 4 步中的名称-值对，并将它们添加到请求的 `Cookie` 标头中。这就是签名 Cookie。

1. CloudFront 使用公有密钥验证签名 Cookie 中的签名并确认该 Cookie 未被篡改。如果签名无效，则请求将被拒绝。

   如果 Cookie 中的签名有效，CloudFront 将查看 Cookie 中的策略声明（如果使用标准策略，则构造一个），以确认该请求仍然有效。例如，如果为 Cookie 指定了开始和结束日期及时间，CloudFront 会确认用户是否是在您希望允许访问的时间段尝试访问内容。

   如果请求满足策略声明中的要求，CloudFront 将像提供不受限制的内容那样提供内容：确定文件是否已在边缘缓存中，必要时将请求转发到源，然后向用户返回文件。

## 防止滥用签名 Cookie
<a name="private-content-signed-cookie-misuse"></a>

如果您在 `Domain` 标头中指定 `Set-Cookie` 参数，请尽可能指定最精确的值，以减少具有相同根域名的某些人的潜在访问。例如，app.example.com 优于 example.com，尤其是在 example.com 不由您控制时。这有助于防止他人从 www.example.com 访问内容。

为帮助防止此类攻击，请执行以下操作：
+ 排除 `Expires` 和 `Max-Age` Cookie 属性，以便 `Set-Cookie` 标头创建会话 Cookie。当用户关闭浏览器时，系统会自动删除会话 Cookie，这降低了其他人非法访问内容的可能性。
+ 包括 `Secure` 属性，以便在查看器将 Cookie 包含在请求中时对 Cookie 进行加密。
+ 如果可能，请使用自定义策略并包含查看器的 IP 地址。
+ 在 `CloudFront-Expires` 属性中，根据您希望用户对内容拥有访问权限的时间长度指定最短合理过期时间。

## CloudFront 何时检查签名 Cookie 中的过期日期和时间
<a name="private-content-check-expiration-cookie"></a>

为了确定签名 Cookie 是否仍然有效，CloudFront 会在发出 HTTP 请求时检查 Cookie 中的过期日期和时间。如果客户端刚好在过期时间之前开始下载大型文件，即使在下载过程中到了过期时间，该下载也应该完成。如果 TCP 连接断开，并且客户端试图在过期时间到期后重新开始下载，则下载将会失败。

如果客户端使用 Range GET 来获取较小的文件，在过期时间到期后发生的任何 GET 请求将会失败。更多有关 Range GET 的信息，请参阅 [CloudFront 如何处理对象的部分请求 (Range GET)。](RangeGETs.md)。

## 代码示例和第三方工具
<a name="private-content-overview-sample-code-cookies"></a>

私有内容的代码示例仅显示如何为签名 URL 创建签名。不过，为签名 Cookie 创建签名的过程非常相似，因此代码示例中的很多内容仍然有用。有关更多信息，请参阅以下主题：
+ [使用 Perl 创建 URL 签名](CreateURLPerl.md)
+ [使用 PHP 创建 URL 签名](CreateURL_PHP.md)
+ [使用 C\$1 和 .NET Framework 创建 URL 签名](CreateSignatureInCSharp.md)
+ [使用 Java 创建 URL 签名](CFPrivateDistJavaDevelopment.md)

# 使用标准策略设置签名 Cookie
<a name="private-content-setting-signed-cookie-canned-policy"></a>

要使用标准策略设置签名 Cookie，请完成以下步骤。要创建签名，请参阅[为使用标准策略的签名 Cookie 创建签名](#private-content-canned-policy-signature-cookies)。<a name="private-content-setting-signed-cookie-canned-policy-procedure"></a>

**使用标准策略设置签名 Cookie**

1. 如果您要使用 .NET 和 Java 创建签名 Cookie，但尚未将密钥对私有密钥的格式从默认 .pem 格式重新设置为与 .NET 和 Java 兼容的格式，请立即执行该操作。有关更多信息，请参阅 [重新设置私有密钥的格式（仅限 .NET 和 Java）](private-content-trusted-signers.md#private-content-reformatting-private-key)。

1. 将应用程序编程为向批准的查看器发送三个 `Set-Cookie` 标头。您之所以需要三个 `Set-Cookie` 标头，是因为每个 `Set-Cookie` 标头只能包含一个名称-值对，而 CloudFront 签名 Cookie 需要三个名称-值对。名称值对为：`CloudFront-Expires`、`CloudFront-Signature` 和 `CloudFront-Key-Pair-Id`。在用户首次请求您希望控制访问权的文件时，查看器中必须包含这些值。
**注意**  
一般情况下，建议排除 `Expires` 和 `Max-Age` 属性。如果排除这些属性，那么当用户关闭浏览器时，浏览器会删除 Cookie，这降低了其他人非法访问内容的可能性。有关更多信息，请参阅 [防止滥用签名 Cookie](private-content-signed-cookies.md#private-content-signed-cookie-misuse)。

   **Cookie 属性的名称区分大小写**。

   包含换行符的目的只是为了让属性的可读性更佳。

   ```
   Set-Cookie: 
   CloudFront-Expires=date and time in Unix time format (in seconds) and Coordinated Universal Time (UTC); 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   Set-Cookie: 
   CloudFront-Signature=hashed and signed version of the policy statement; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   Set-Cookie: 
   CloudFront-Key-Pair-Id=public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   ```  
**（可选）`Domain`**  
所请求文件的域名。如果不指定 `Domain` 属性，则默认值是 URL 中的域名，它仅适用于指定的域名，而不适用于子域。如果指定 `Domain` 属性，它还将适用于子域。可以选择在域名前加一个句点（例如 `Domain=.example.com`）。此外，如果指定 `Domain` 属性，那么 URL 中的域名则必须与 `Domain` 属性的值匹配。  
可以指定 CloudFront 为您的分配指定的域名（例如 d111111abcdef8.cloudfront.net），但不能为域名指定 \$1.cloudfront.net。  
如果希望在 URL 中使用备用域名（如 example.com），则必须向分配添加备用域名，而无论是否指定 `Domain` 属性。有关更多信息，请参阅 [备用域名 (CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。  
**（可选）`Path`**  
所请求文件的路径。如果未指定 `Path` 属性，则默认值是 URL 中的路径。  
**`Secure`**  
要求查看器先加密 Cookie，然后再发送请求。建议通过 HTTPS 连接发送 `Set-Cookie` 标头，以确保 Cookie 受到保护，而不会受到中间人攻击。  
**`HttpOnly`**  
定义浏览器（如果受支持）如何与 Cookie 值进行交互。如果选择 `HttpOnly`，则 JavaScript 无法访问 Cookie 值。这种预防措施有助于缓解跨站点脚本（XSS）攻击。有关更多信息，请参阅[使用 HTTP Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)。  
**`CloudFront-Expires`**  
指定 Unix 时间格式（以秒为单位） 和协调通用时间 (UTC) 格式的过期日期和时间。例如，2026 年 1 月 1 日上午 10 点 UTC 转换为 Unix 时间格式就是 1767290400。  
要使用纪元时间，请指定 64 位整数表示日期，该日期不得晚于 `9223372036854775807`（2262 年 4 月 11 日，星期五，23:47:16.854 UTC）。  
有关 UTC 的信息，请参阅 *RFC 3339，Internet 上的日期和时间：时间戳*，网址为 [https://tools.ietf.org/html/rfc3339](https://tools.ietf.org/html/rfc3339)。  
**`CloudFront-Signature`**  
JSON 策略声明经过哈希处理、签署和 Base64 编码的版本。有关更多信息，请参阅 [为使用标准策略的签名 Cookie 创建签名](#private-content-canned-policy-signature-cookies)。  
**`CloudFront-Key-Pair-Id`**  
CloudFront 公有密钥的 ID，例如，`K2JCJMDEHXQW5F`。公有密钥 ID 告诉 CloudFront 要使用哪个公有密钥来验证签名的 URL。CloudFront 将比较签名中的信息与策略声明中的信息，以确认该 URL 没有被篡改。  
此公有密钥必须属于作为分配中可信签署人的密钥组。有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

以下示例显示了在文件的 URL 中使用与分配关联的域名时一个签名 Cookie 的 `Set-Cookie` 标头：

```
Set-Cookie: CloudFront-Expires=1426500000; Domain=d111111abcdef8.cloudfront.net; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=yXrSIgyQoeE4FBI4eMKF6ho~CA8_; Domain=d111111abcdef8.cloudfront.net; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=d111111abcdef8.cloudfront.net; Path=/images/*; Secure; HttpOnly
```

以下示例显示了在文件的 URL 中使用备用域名 example.org 时一个签名 Cookie 的 `Set-Cookie` 标头：

```
Set-Cookie: CloudFront-Expires=1426500000; Domain=example.org; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=yXrSIgyQoeE4FBI4eMKF6ho~CA8_; Domain=example.org; Path=/images/*; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=example.org; Path=/images/*; Secure; HttpOnly
```

如果希望在 URL 中使用备用域名 (如 example.com)，则必须向分配添加备用域名，而无论是否指定 `Domain` 属性。有关更多信息，请参阅 [备用域名 (CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。

## 为使用标准策略的签名 Cookie 创建签名
<a name="private-content-canned-policy-signature-cookies"></a>

要为使用标准策略的签名 Cookie 创建签名，请完成以下步骤。

**Topics**
+ [为使用标准策略的签名 Cookie 创建策略声明](#private-content-canned-policy-statement-cookies)
+ [签署策略声明，以便为使用标准策略的签名 Cookie 创建签名](#private-content-canned-policy-cookies-signing-policy-statement)

### 为使用标准策略的签名 Cookie 创建策略声明
<a name="private-content-canned-policy-statement-cookies"></a>

设置使用标准策略的签名 Cookie 时，`CloudFront-Signature` 属性是策略声明的经过哈希处理和签署的版本。对于使用标准策略的签名 Cookie，请不要像在使用自定义策略的签名 Cookie 一样在 `Set-Cookie` 标头中包含策略声明。要创建策略语句，请完成以下步骤。<a name="private-content-canned-policy-statement-cookies-procedure"></a>

**为使用标准策略的签名 Cookie 创建策略声明**

1. 使用以下 JSON 格式以及 UTF-8 字符编码构建策略声明。根据指定，准确包括所有标点符号和其他文本值。有关 `Resource` 和 `DateLessThan` 参数的信息，请参阅 [在策略声明中为使用标准策略的签名 Cookie 指定的值](#private-content-canned-policy-statement-cookies-values)。

   ```
   {
       "Statement": [
           {
               "Resource": "base URL or stream name",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime": ending date and time in Unix time format and UTC
                   }
               }
           }
       ]
   }
   ```

1. 删除策略声明中的所有空格（包括制表符和换行符）。您可能需要在应用程序代码的字符串中包括换码符。

#### 在策略声明中为使用标准策略的签名 Cookie 指定的值
<a name="private-content-canned-policy-statement-cookies-values"></a>

为标准策略创建策略声明时，请指定以下值：

**资源**  
包含查询字符串 (如果有) 的基本 URL，例如：  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
只能为 `Resource` 指定一个值。  
请注意以下几点：  
+ **协议** – 该值必须以 `http://` 或 `https://` 开头。
+ **查询字符串参数** – 如果没有查询字符串参数，请省略问号。
+ **备用域名** – 如果在 URL 中指定备用域名 (CNAME)，则必须在引用网页或应用程序中的文件时指定备用域名。请勿为文件指定 Amazon S3 URL。

**DateLessThan**  
Unix 时间格式（以秒为单位）和协调通用时间 (UTC) 格式的 URL 过期日期和时间。切勿用引号将该值括起来。  
例如，UTC 时间 2015 年 3 月 16 日上午 10 点转换为 Unix 时间格式就是 1426500000。  
该值必须与 `CloudFront-Expires` 标头中的 `Set-Cookie` 属性的值匹配。切勿用引号将该值括起来。  
有关更多信息，请参阅 [CloudFront 何时检查签名 Cookie 中的过期日期和时间](private-content-signed-cookies.md#private-content-check-expiration-cookie)。

#### 标准策略的示例策略声明
<a name="private-content-canned-policy-cookies-sample-policy-statement"></a>

当在签名 Cookie 中使用以下示例策略声明时，用户可访问文件 `https://d111111abcdef8.cloudfront.net/horizon.jpg`，直至 UTC 时间 2015 年 3 月 16 日上午 10 点：

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/horizon.jpg?size=large&license=yes",
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": 1426500000
                }
            }
        }
    ]
}
```

### 签署策略声明，以便为使用标准策略的签名 Cookie 创建签名
<a name="private-content-canned-policy-cookies-signing-policy-statement"></a>

要为 `CloudFront-Signature` 标头中的 `Set-Cookie` 属性创建值，请对在 [为使用标准策略的签名 Cookie 创建策略声明](#private-content-canned-policy-statement-cookies-procedure) 中创建的策略声明进行哈希处理并签署。

有关如何对策略声明进行哈希、签署及编码的更多信息和示例，请参阅以下主题：
+ [用于 Base64 编码和加密的 Linux 命令和 OpenSSL](private-content-linux-openssl.md)
+ [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-canned-policy-cookie-creating-signature-procedure"></a>

**为使用标准策略的签名 Cookie 创建签名**

1. 使用 SHA-1 哈希函数和 RSA 对在[为使用标准策略的签名 Cookie 创建策略声明](#private-content-canned-policy-statement-cookies-procedure)过程中创建的策略声明进行哈希处理并签署。使用不再包含空格的策略声明版本。

   对于哈希函数所需的私有密钥，请使用其公有密钥位于分配的活动可信密钥组中的私有密钥。
**注意**  
您用于哈希及签署策略声明的方法取决于您的编程语言和平台。有关代码示例，请参阅 [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)。

1. 删除经过哈希处理并签署的字符串中的空格（包括制表符和换行符）。

1. 使用 MIME Base64 编码对字符串进行 Base64 编码。有关更多信息，请参阅 *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies* 中的 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)。

1. 用有效的字符替换 URL 查询字符串中的无效字符。下表列出了无效和有效字符。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-canned-policy.html)

1. 在 `Set-Cookie` 名称-值对的 `CloudFront-Signature` 标头中包含结果值。然后返回到[使用标准策略设置签名 Cookie](#private-content-setting-signed-cookie-canned-policy-procedure)为 `CloudFront-Key-Pair-Id` 添加 `Set-Cookie` 标头。

# 使用自定义策略设置签名 Cookie
<a name="private-content-setting-signed-cookie-custom-policy"></a>

要使用自定义策略设置签名 Cookie，请完成以下步骤。<a name="private-content-setting-signed-cookie-custom-policy-procedure"></a>

**使用自定义策略设置签名 Cookie**

1. 如果您使用 .NET 和 Java 创建签名 URL，而且，如果您尚未将密钥对私有密钥的格式从默认 .pem 格式重新设置为与 .NET 和 Java 兼容的格式，那么现在就开始设置吧。有关更多信息，请参阅 [重新设置私有密钥的格式（仅限 .NET 和 Java）](private-content-trusted-signers.md#private-content-reformatting-private-key)。

1. 将应用程序编程为向批准的查看器发送三个 `Set-Cookie` 标头。您之所以需要三个 `Set-Cookie` 标头，是因为每个 `Set-Cookie` 标头只能包含一个名称-值对，而 CloudFront 签名 Cookie 需要三个名称-值对。名称值对为：`CloudFront-Policy`、`CloudFront-Signature` 和 `CloudFront-Key-Pair-Id`。在用户首次请求您希望控制访问权的文件时，查看器中必须包含这些值。
**注意**  
一般情况下，建议排除 `Expires` 和 `Max-Age` 属性。如果排除这些属性，那么当用户关闭浏览器时，浏览器则会删除 Cookie，这降低了其他人非法访问内容的可能性。有关更多信息，请参阅 [防止滥用签名 Cookie](private-content-signed-cookies.md#private-content-signed-cookie-misuse)。

   **Cookie 属性的名称区分大小写**。

   包含换行符的目的只是为了让属性的可读性更佳。

   ```
   Set-Cookie: 
   CloudFront-Policy=base64 encoded version of the policy statement; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   
   Set-Cookie: 
   CloudFront-Signature=hashed and signed version of the policy statement; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   
   Set-Cookie: 
   CloudFront-Key-Pair-Id=public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature; 
   Domain=optional domain name; 
   Path=/optional directory path; 
   Secure; 
   HttpOnly
   ```  
**（可选）`Domain`**  
所请求文件的域名。如果不指定 `Domain` 属性，则默认值是 URL 中的域名，它仅适用于指定的域名，而不适用于子域。如果指定 `Domain` 属性，它还将适用于子域。可以选择在域名前加一个句点（例如 `Domain=.example.com`）。此外，如果指定 `Domain` 属性，那么 URL 中的域名则必须与 `Domain` 属性的值匹配。  
可以指定 CloudFront 为您的分配指定的域名（例如 d111111abcdef8.cloudfront.net），但不能为域名指定 \$1.cloudfront.net。  
如果希望在 URL 中使用备用域名（如 example.com），则必须向分配添加备用域名，而无论是否指定 `Domain` 属性。有关更多信息，请参阅 [备用域名 (CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。  
**（可选）`Path`**  
所请求文件的路径。如果未指定 `Path` 属性，则默认值是 URL 中的路径。  
**`Secure`**  
要求查看器先加密 Cookie，然后再发送请求。建议通过 HTTPS 连接发送 `Set-Cookie` 标头，以确保 Cookie 受到保护，而不会受到中间人攻击。  
**`HttpOnly`**  
要求查看器仅在 HTTP 或 HTTPS 请求中发送 Cookie。  
**`CloudFront-Policy`**  
您的策略声明采用 JSON 格式，删除了空格，然后进行 Base64 编码。有关更多信息，请参阅 [为使用自定义策略的签名 Cookie 创建签名](#private-content-custom-policy-signature-cookies)。  
策略语句控制签名 Cookie 授予用户的访问权限。它包括用户可以访问的文件、过期日期和时间、URL 生效的可选日期和时间、允许访问文件的可选 IP 地址或 IP 地址范围。  
**`CloudFront-Signature`**  
JSON 策略声明经过哈希处理、签署和 Base64 编码的版本。有关更多信息，请参阅 [为使用自定义策略的签名 Cookie 创建签名](#private-content-custom-policy-signature-cookies)。  
**`CloudFront-Key-Pair-Id`**  
CloudFront 公有密钥的 ID，例如，`K2JCJMDEHXQW5F`。公有密钥 ID 告诉 CloudFront 要使用哪个公有密钥来验证签名的 URL。CloudFront 将比较签名中的信息与策略声明中的信息，以确认该 URL 没有被篡改。  
此公有密钥必须属于作为分配中可信签署人的密钥组。有关更多信息，请参阅 [指定可以创建签名 URL 和签名 Cookie 的签署人](private-content-trusted-signers.md)。

## 自定义策略的 `Set-Cookie` 标头示例
<a name="example-set-cookie-headers-custom-policy"></a>

请参阅以下 `Set-Cookie` 标头对示例。

如果您希望在 URL 中使用备用域名（如 example.com），则无论您是否指定 `Domain` 属性，都必须向分配中添加备用域名。有关更多信息，请参阅 [备用域名 (CNAME)](DownloadDistValuesGeneral.md#DownloadDistValuesCNAME)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。

**Example 示例 1**  
在文件的 URL 中使用与分配关联的域名时，您可以将 `Set-Cookie` 标头用于一个已签名的 Cookie。  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
```

**Example 示例 2**  
在文件的 URL 中使用备用域名（example.org）时，您可以将 `Set-Cookie` 标头用于一个已签名的 Cookie。  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=example.org; Path=/; Secure; HttpOnly
```

**Example 示例 3**  
在文件的 URL 中使用与分配关联的域名时，您可以将 `Set-Cookie` 标头对用于一个已签名的请求。  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=d111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=dd111111abcdef8.cloudfront.net; Path=/; Secure; HttpOnly
```

**Example 示例 4**  
在文件的 URL 中使用与分配关联的备用域名（example.org）时，您可以将 `Set-Cookie` 标头对用于一个已签名的请求。  

```
Set-Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QxMTExMTFhYmNkZWY4LmNsb3VkZnJvbnQubmV0L2dhbWVfZG93bmxvYWQuemlwIiwiQ29uZGl0aW9uIjp7IklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiIxOTIuMC4yLjAvMjQifSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0MjY1MDAwMDB9fX1dfQ__; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=dtKhpJ3aUYxqDIwepczPiDb9NXQ_; Domain=example.org; Path=/; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=example.org; Path=/; Secure; HttpOnly
```

## 为使用自定义策略的签名 Cookie 创建策略声明
<a name="private-content-custom-policy-statement-cookies"></a>

要创建自定义策略的策略声明，请完成以下步骤。有关以各种方式控制访问文件的一些示例策略声明，请参阅[使用自定义策略的签名 Cookie 的示例策略声明](#private-content-custom-policy-statement-signed-cookies-examples)。<a name="private-content-custom-policy-statement-cookies-procedure"></a>

**为使用自定义策略的签名 Cookie 创建策略声明**

1. 使用以下 JSON 格式构建策略声明。

   ```
   {
       "Statement": [
           {
               "Resource": "URL of the file",
               "Condition": {
                   "DateLessThan": {
                       "AWS:EpochTime":required ending date and time in Unix time format and UTC
                   },
                   "DateGreaterThan": {
                       "AWS:EpochTime":optional beginning date and time in Unix time format and UTC
                   },
                   "IpAddress": {
                       "AWS:SourceIp": "optional IP address"
                   }
               }
           }
       ]
   }
   ```

   请注意以下几点：
   + 您只能包含一个语句。
   + 使用 UTF-8 字符编码。
   + 根据指定，准确包括所有标点符号和参数名称。不接受参数名称的缩写。
   + `Condition` 部分中参数的顺序无关紧要。
   + 有关 `Resource`、`DateLessThan`、`DateGreaterThan` 和 `IpAddress` 值的信息，请参阅[在使用自定义策略的签名 Cookie 的策略声明中指定的值](#private-content-custom-policy-statement-cookies-values)。

1. 删除策略声明中的所有空格（包括制表符和换行符）。您可能需要在应用程序代码的字符串中包括换码符。

1. 使用 MIME Base64 编码对策略声明进行 Base64 编码。有关更多信息，请参阅 *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies* 中的 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)。

1. 用有效的字符替换 URL 查询字符串中的无效字符。下表列出了无效和有效字符。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html)

1. 将结果值包含在 `Set-Cookie` 标头的 `CloudFront-Policy=` 之后。

1. 通过对策略声明进行哈希、签署及 Base64 编码处理，为 `CloudFront-Signature` 的 `Set-Cookie` 标头创建签名。有关更多信息，请参阅 [为使用自定义策略的签名 Cookie 创建签名](#private-content-custom-policy-signature-cookies)。

### 在使用自定义策略的签名 Cookie 的策略声明中指定的值
<a name="private-content-custom-policy-statement-cookies-values"></a>

针对自定义策略创建策略声明时，请指定以下值。

**资源**  
包含查询字符串 (如果有) 的基本 URL：  
`https://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes`  
如果省略 `Resource` 参数，用户将可以访问与用于创建签名 URL 的密钥对相关联的任何分配的所有关联文件。
只能为 `Resource` 指定一个值。  
请注意以下几点：  
+ **协议** – 该值必须以 `http://` 或 `https://` 开头。
+ **查询字符串参数** – 如果没有查询字符串参数，请省略问号。
+ **通配符** – 可以使用与零个或更多个字符匹配的通配符 (\$1)，或者与字符串中的任何位置的一个字符完全匹配的通配符 (?)。例如，值：

  `https://d111111abcdef8.cloudfront.net/*game_download.zip*`

  将包括（例如）以下文件：
  + `https://d111111abcdef8.cloudfront.net/game_download.zip`
  + `https://d111111abcdef8.cloudfront.net/example_game_download.zip?license=yes`
  + `https://d111111abcdef8.cloudfront.net/test_game_download.zip?license=temp`
+ **备用域名** – 如果在 URL 中指定备用域名 (CNAME)，则必须在引用网页或应用程序中的文件时指定备用域名。请勿为文件指定 Amazon S3 URL。

**DateLessThan**  
Unix 时间格式（以秒为单位）和协调通用时间 (UTC) 格式的 URL 过期日期和时间。切勿用引号将该值括起来。  
例如，UTC 时间 2015 年 3 月 16 日上午 10 点转换为 Unix 时间格式就是 1426500000。  
有关更多信息，请参阅 [CloudFront 何时检查签名 Cookie 中的过期日期和时间](private-content-signed-cookies.md#private-content-check-expiration-cookie)。

**DateGreaterThan（可选）**  
Unix 时间格式（以秒为单位）和协调通用时间 (UTC) 格式的 URL 可选开始日期和时间。不允许用户在指定日期和时间或之前访问该文件。切勿用引号将该值括起来。

**IpAddress（可选）**  
发出 GET 请求的客户端的 IP 地址。请注意以下几点：  
+ 要允许任何 IP 地址访问文件，请省略 `IpAddress` 参数。
+ 可以指定一个 IP 地址或一个 IP 地址范围。例如，如果客户端的 IP 地址在两个独立范围之一的范围内，您不能设置策略以允许访问。
+ 要允许从单个 IP 地址访问，可指定：

  `"`*IPv4 IP 地址*`/32"`
+ 必须采用标准 IPv4 CIDR 格式指定 IP 地址范围 (例如，`192.0.2.0/24`)。有关更多信息，请转到《RFC 4632，无类别域间路由 (CIDR)：互联网地址分配和聚合计划》**，网址为 [https://tools.ietf.org/html/rfc4632](https://tools.ietf.org/html/rfc4632)。
**重要**  
不支持 IPv6 格式的 IP 地址，如 2001:0db8:85a3::8a2e:0370:7334。

  如果使用包含 `IpAddress` 的自定义策略，请勿为分配启用 IPv6。如果希望通过 IP 地址限制对某些内容的访问并支持其他内容的 IPv6 请求，可以创建两个分配。有关更多信息，请参阅 [启用 IPv6（查看器请求）](DownloadDistValuesGeneral.md#DownloadDistValuesEnableIPv6)主题中的 [所有分配设置参考](distribution-web-values-specify.md)。

## 使用自定义策略的签名 Cookie 的示例策略声明
<a name="private-content-custom-policy-statement-signed-cookies-examples"></a>

以下示例策略声明显示了如何控制对特定文件、目录中的所有文件或与密钥对 ID 有关的所有文件的访问。这些示例也显示了如何控制来自单个 IP 地址或 IP 地址范围的访问，以及如何防止用户在指定日期和时间后使用签名 Cookie。

如果复制并粘贴其中的任何示例，请删除任何空格（包括制表符和换行符），将值替换为自己的值，并在右大括号（\$1）后面包含一个换行符。

有关更多信息，请参阅 [在使用自定义策略的签名 Cookie 的策略声明中指定的值](#private-content-custom-policy-statement-cookies-values)。

**Topics**
+ [示例策略声明：从 IP 地址范围访问一个文件](#private-content-custom-policy-statement-signed-cookies-example-one-object)
+ [示例策略声明：从 IP 地址范围访问一个目录中的所有文件](#private-content-custom-policy-statement-signed-cookies-example-all-objects)
+ [示例策略声明：从一个 IP 地址访问与一个密钥对 ID 关联的所有文件](#private-content-custom-policy-statement-signed-cookies-example-one-ip)

### 示例策略声明：从 IP 地址范围访问一个文件
<a name="private-content-custom-policy-statement-signed-cookies-example-one-object"></a>

签名 Cookie 中的以下示例自定义策略指定用户可从范围 `192.0.2.0/24` 内的 IP 地址访问文件 `https://d111111abcdef8.cloudfront.net/game_download.zip`，直至 UTC 时间 2023 年 1 月 1 日上午 10 点：

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/game_download.zip",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

### 示例策略声明：从 IP 地址范围访问一个目录中的所有文件
<a name="private-content-custom-policy-statement-signed-cookies-example-all-objects"></a>

以下示例自定义策略允许为 `training` 目录中的任何文件创建签名 Cookie，如 `Resource` 参数中的 \$1 通配符所指示。用户可从范围 `192.0.2.0/24` 内的 IP 地址访问文件，直至 UTC 时间 2013 年 1 月 1 日上午 10 点：

```
{
    "Statement": [
        {
            "Resource": "https://d111111abcdef8.cloudfront.net/training/*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.0/24"
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1767290400
                }
            }
        }
    ]
}
```

在其中使用此策略的每个签名 Cookie 包括用于标识特定文件的基本 URL，例如：

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

### 示例策略声明：从一个 IP 地址访问与一个密钥对 ID 关联的所有文件
<a name="private-content-custom-policy-statement-signed-cookies-example-one-ip"></a>

以下示例自定义策略允许为与任何分配相关联的任何文件设置签名 Cookie，如 `Resource` 参数中的 \$1 通配符所指示。用户必须使用 IP 地址 `192.0.2.10/32`。（CIDR 表示法中的值 `192.0.2.10/32` 指代单个 IP 地址 `192.0.2.10`。） 这些文件仅从 UTC 时间 2013 年 1 月 1 日上午 10 点到 UTC 时间 2013 年 1 月 2 日上午 10 点期间可用：

```
{
    "Statement": [
        {
            "Resource": "https://*",
            "Condition": {
                "IpAddress": {
                    "AWS:SourceIp": "192.0.2.10/32"
                },
                "DateGreaterThan": {
                    "AWS:EpochTime": 1767290400
                },
                "DateLessThan": {
                    "AWS:EpochTime": 1767376800
                }
            }
        }
    ]
}
```

在其中使用此策略的每个签名 Cookie 包括用于标识特定 CloudFront 分配中特定文件的基本 URL，例如：

`https://d111111abcdef8.cloudfront.net/training/orientation.pdf`

签名 Cookie 还包括密钥对 ID，它必须与您在基本 URL 中指定的分配 (d111111abcdef8.cloudfront.net) 中的可信密钥组关联。

## 为使用自定义策略的签名 Cookie 创建签名
<a name="private-content-custom-policy-signature-cookies"></a>

使用自定义策略的签名 Cookie 的签名是策略声明的哈希、签署及 Base64 编码版本。

有关额外信息以及如何哈希、签署及编码策略声明的示例，请参阅：
+ [用于 Base64 编码和加密的 Linux 命令和 OpenSSL](private-content-linux-openssl.md)
+ [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)<a name="private-content-custom-policy-signature-cookies-procedure"></a>

**为使用自定义策略的签名 Cookie 创建签名**

1. 使用 SHA-1 哈希函数和 RSA 对在[为使用自定义策略的签名 URL 创建策略声明](private-content-creating-signed-url-custom-policy.md#private-content-custom-policy-creating-policy-procedure)过程中创建的 JSON 策略声明进行哈希处理并签署。使用不再包含空格但尚未进行 Base64 编码的策略声明版本。

   对于哈希函数所需的私有密钥，请使用其公有密钥位于分配的活动可信密钥组中的私有密钥。
**注意**  
您用于哈希及签署策略声明的方法取决于您的编程语言和平台。有关代码示例，请参阅 [为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)。

1. 删除经过哈希处理并签署的字符串中的空格（包括制表符和换行符）。

1. 使用 MIME Base64 编码对字符串进行 Base64 编码。有关更多信息，请参阅 *RFC 2045, MIME (Multipurpose Internet Mail Extensions) Part One: Format of Internet Message Bodies* 中的 [Section 6.8, Base64 Content-Transfer-Encoding](https://tools.ietf.org/html/rfc2045#section-6.8)。

1. 用有效的字符替换 URL 查询字符串中的无效字符。下表列出了无效和有效字符。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html)

1. 将结果值包含到 `Set-Cookie` 名称值对的 `CloudFront-Signature=` 标头中，并返回[使用自定义策略设置签名 Cookie](#private-content-setting-signed-cookie-custom-policy-procedure)以添加 `Set-Cookie` 的 `CloudFront-Key-Pair-Id` 标头。

# 使用 PHP 创建签名 Cookie
<a name="signed-cookies-PHP"></a>

以下代码示例与[使用 PHP 创建 URL 签名](CreateURL_PHP.md)中的示例类似，创建指向视频的链接。但是，此示例没有对代码中的 URL 进行签名，而是使用 `create_signed_cookies()` 功能对 Cookie 进行签名。客户端播放器使用 Cookie 对向 CloudFront 分配发出的每个请求进行身份验证。

这种方法对于流式传输内容非常有用，例如 HTTP 直播（HLS，HTTP Live Streaming）或基于 HTTP 的动态自适应流式传输（DASH，Dynamic adaptive streaming over HTTP），在这种情况下，客户端需要发出多个请求来检索清单、片段和相关的播放资产。通过使用签名 Cookie，客户端可以对每个请求进行身份验证，而无需为每个分段生成新的签名 URL。

**注意**  
在使用签名 URL 提供私有内容的过程中，创建 URL 签名只是其中的一部分。有关更多信息，请参阅 [使用签名 Cookie](private-content-signed-cookies.md)。



**Topics**
+ [创建 RSA SHA-1 签名](#create-rsa-sha-1signature-cookies)
+ [创建签名 Cookie](#create-the-signed-cookie)
+ [完整代码](#full-code-signed-cookies)

以下部分将代码示例细分到各个单独的部分。您可以在下面找到完整的[代码示例](#full-code-signed-cookies)。

## 创建 RSA SHA-1 签名
<a name="create-rsa-sha-1signature-cookies"></a>

本代码示例执行以下操作：

1. 函数 `rsa_sha1_sign` 执行哈希操作并签署策略声明。所需的参数是策略语句和私有密钥，该私有密钥对应于分配的可信密钥组中的公有密钥。

1. 接下来，`url_safe_base64_encode` 函数创建签名 URL 安全版本。

   ```
   function rsa_sha1_sign($policy, $private_key_filename) {
       $signature = "";
       $fp = fopen($private_key_filename, "r");
       $priv_key = fread($fp, 8192);
       fclose($fp);
       $pkeyid = openssl_get_privatekey($priv_key);
       openssl_sign($policy, $signature, $pkeyid);
       openssl_free_key($pkeyid);
       return $signature;
   }
   
   function url_safe_base64_encode($value) {
       $encoded = base64_encode($value);
       return str_replace(
           array('+', '=', '/'),
           array('-', '_', '~'),
           $encoded);
   }
   ```

## 创建签名 Cookie
<a name="create-the-signed-cookie"></a>

下面的代码构造了创建签名 Cookie 的过程，使用的 Cookie 属性包括：`CloudFront-Expires`、`CloudFront-Signature` 和 `CloudFront-Key-Pair-Id`。代码使用自定义策略。

```
function create_signed_cookies($resource, $private_key_filename, $key_pair_id, $expires, $client_ip = null) {
    $policy = array(
        'Statement' => array(
            array(
                'Resource' => $resource,
                'Condition' => array(
                    'DateLessThan' => array('AWS:EpochTime' => $expires)
                )
            )
        )
    );

    if ($client_ip) {
        $policy['Statement'][0]['Condition']['IpAddress'] = array('AWS:SourceIp' => $client_ip . '/32');
    }

    $policy = json_encode($policy);
    $encoded_policy = url_safe_base64_encode($policy);
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    $encoded_signature = url_safe_base64_encode($signature);

    return array(
        'CloudFront-Policy' => $encoded_policy,
        'CloudFront-Signature' => $encoded_signature,
        'CloudFront-Key-Pair-Id' => $key_pair_id
    );
}
```

有关更多信息，请参阅 [使用自定义策略设置签名 Cookie](private-content-setting-signed-cookie-custom-policy.md)。

## 完整代码
<a name="full-code-signed-cookies"></a>

以下代码示例完整演示使用 PHP 创建 CloudFront 签名 Cookie 的过程。您可以从 [demo-php.zip](samples/demo-php.zip) 文件下载完整示例。

在下面的示例中，您可以修改 `$policy Condition` 元素，以同时允许 IPv4 和 IPv6 地址范围。有关示例，请参阅《Amazon Simple Storage Service 用户指南》**中的[在 IAM 策略中使用 IPv6 地址](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ipv6-access.html#ipv6-access-iam)。

```
<?php

function rsa_sha1_sign($policy, $private_key_filename) {
    $signature = "";
    $fp = fopen($private_key_filename, "r");
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key);
    openssl_sign($policy, $signature, $pkeyid);
    openssl_free_key($pkeyid);
    return $signature;
}

function url_safe_base64_encode($value) {
    $encoded = base64_encode($value);
    return str_replace(
        array('+', '=', '/'),
        array('-', '_', '~'),
        $encoded);
}

function create_signed_cookies($resource, $private_key_filename, $key_pair_id, $expires, $client_ip = null) {
    $policy = array(
        'Statement' => array(
            array(
                'Resource' => $resource,
                'Condition' => array(
                    'DateLessThan' => array('AWS:EpochTime' => $expires)
                )
            )
        )
    );

    if ($client_ip) {
        $policy['Statement'][0]['Condition']['IpAddress'] = array('AWS:SourceIp' => $client_ip . '/32');
    }

    $policy = json_encode($policy);
    $encoded_policy = url_safe_base64_encode($policy);
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    $encoded_signature = url_safe_base64_encode($signature);

    return array(
        'CloudFront-Policy' => $encoded_policy,
        'CloudFront-Signature' => $encoded_signature,
        'CloudFront-Key-Pair-Id' => $key_pair_id
    );
}



$private_key_filename = '/home/test/secure/example-priv-key.pem';
$key_pair_id = 'K2JCJMDEHXQW5F';
$base_url = 'https://d1234.cloudfront.net';

$expires = time() + 3600; // 1 hour from now

// Get the viewer real IP from the x-forward-for header as $_SERVER['REMOTE_ADDR'] will return viewer facing IP. An alternative option is to use CloudFront-Viewer-Address header. Note that this header is a trusted CloudFront immutable header. Example format: IP:PORT ("CloudFront-Viewer-Address": "1.2.3.4:12345")
$client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];


// For HLS manifest and segments (using wildcard)
$hls_resource = $base_url . '/sign/*';
$signed_cookies = create_signed_cookies($hls_resource, $private_key_filename, $key_pair_id, $expires, $client_ip);

// Set the cookies
$cookie_domain = parse_url($base_url, PHP_URL_HOST);
foreach ($signed_cookies as $name => $value) {
    setcookie($name, $value, $expires, '/', $cookie_domain, true, true);
}

?>

<!DOCTYPE html>
<html>
<head>
    <title>CloudFront Signed HLS Stream with Cookies</title>
</head>
<body>
    <h1>Amazon CloudFront Signed HLS Stream with Cookies</h1>
    <h2>Expires at <?php echo gmdate('Y-m-d H:i:s T', $expires); ?> only viewable by IP <?php echo $client_ip; ?></h2>
    
    <div id='hls-video'>
        <video id="video" width="640" height="360" controls></video>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
    <script>
        var video = document.getElementById('video');
        var manifestUrl = '<?php echo $base_url; ?>/sign/manifest.m3u8';
        
        if (Hls.isSupported()) {
            var hls = new Hls();
            hls.loadSource(manifestUrl);
            hls.attachMedia(video);
        }
        else if (video.canPlayType('application/vnd.apple.mpegurl')) {
            video.src = manifestUrl;
        }
    </script>
</body>
</html>
```

您可以不使用签名 Cookie，而是使用签名 URL。有关更多信息，请参阅 [使用 PHP 创建 URL 签名](CreateURL_PHP.md)。

# 用于 Base64 编码和加密的 Linux 命令和 OpenSSL
<a name="private-content-linux-openssl"></a>

可使用以下 Linux 命令行命令和 OpenSSL 哈希和签署策略声明，对签名进行 Base64 编码，将 URL 查询字符串中无效的字符替换为有效的字符。

有关 OpenSSL 的信息，请转到 [https://www.openssl.org](https://www.openssl.org)。

```
cat policy | tr -d "\n" | tr -d " \t\n\r" | openssl sha1 -sign private_key.pem | openssl base64 -A | tr -- '+=/' '-_~'
```

在上述命令中：
+ `cat` 读取 `policy` 文件。
+ `tr -d "\n" | tr -d " \t\n\r"` 移除由 `cat` 添加的空格和换行符。
+ OpenSSL 使用 SHA-1 对文件进行哈希处理，并使用私有密钥文件 `private_key.pem` 对其进行签名。私有密钥签名可以是 RSA 2048 或 ECDSA 256。
+ OpenSSL 对经过哈希处理和签名的策略声明进行 Base64 编码。
+ `tr` 使用有效的字符替换 URL 查询字符串参数中的无效字符。

有关演示如何创建签名的更多代码示例，请参阅[为签名 URL 创建签名的代码示例](PrivateCFSignatureCodeAndExamples.md)。

# 为签名 URL 创建签名的代码示例
<a name="PrivateCFSignatureCodeAndExamples"></a>

本部分包括演示如何为签名 URL 创建签名的可下载应用程序示例。示例可以 Perl、PHP、C\$1 和 Java 语言提供。可以使用任意示例来创建签名 URL。Perl 脚本在 Linux 和 macOS 平台上运行。PHP 示例将在运行 PHP 的任何服务器上工作。C\$1 示例使用 .NET Framework。

有关 JavaScript (Node.js) 中的代码示例，请参阅 AWS 开发人员博客上的[以 Node.js 创建 Amazon CloudFront 已签名 URL](https://aws.amazon.com/blogs/developer/creating-amazon-cloudfront-signed-urls-in-node-js/)。

有关 Python 中的代码示例，请参阅《AWS SDK for Python (Boto3) API Reference》**中的 [Generate a signed URL for Amazon CloudFront](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html#examples)，以及 Boto3 GitHub 存储库中的[此代码示例](https://github.com/boto/boto3/blob/develop/boto3/examples/cloudfront.rst)。

**Topics**
+ [使用 Perl 创建 URL 签名](CreateURLPerl.md)
+ [使用 PHP 创建 URL 签名](CreateURL_PHP.md)
+ [使用 C\$1 和 .NET Framework 创建 URL 签名](CreateSignatureInCSharp.md)
+ [使用 Java 创建 URL 签名](CFPrivateDistJavaDevelopment.md)

# 使用 Perl 创建 URL 签名
<a name="CreateURLPerl"></a>

本部分包括一个用于 Linux/Mac 平台的 Perl 脚本，您可以使用它为私有内容创建签名。要创建签名，请使用指定 CloudFront URL、签署人私有密钥的路径、密钥 ID 以及 URL 的到期日期的命令行参数来运行该脚本。工具还可对签名 URL 进行解码。

**注意**  
创建 URL 签名只是使用签名 URL 提供私有内容过程的一部分。有关端到端流程的更多信息，请参阅[使用签名 URL](private-content-signed-urls.md)。

**Topics**
+ [用于创建签名 URL 的 Perl 脚本的源](#CreateURLPerlScriptSource)

## 用于创建签名 URL 的 Perl 脚本的源
<a name="CreateURLPerlScriptSource"></a>

以下 Perl 源代码可用于为 CloudFront 创建签名 URL。代码中的注释包括有关该工具的命令行开关和功能的信息。

```
#!/usr/bin/perl -w

# Copyright 2008 Amazon Technologies, Inc.  Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. You may obtain a copy of the License at:
#
# https://aws.amazon.com/apache2.0
#
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.

=head1 cfsign.pl

cfsign.pl - A tool to generate and verify Amazon CloudFront signed URLs

=head1 SYNOPSIS

This script uses an existing RSA key pair to sign and verify Amazon CloudFront signed URLs

View the script source for details as to which CPAN packages are required beforehand. 

For help, try:

cfsign.pl --help

URL signing examples:

cfsign.pl --action encode --url https://images.my-website.com/gallery1.zip --policy sample_policy.json --private-key privkey.pem --key-pair-id mykey

cfsign.pl --action encode --url https://images.my-website.com/gallery1.zip --expires 1257439868 --private-key privkey.pem --key-pair-id mykey

URL decode example:

cfsign.pl --action decode --url "http//mydist.cloudfront.net/?Signature=AGO-PgxkYo99MkJFHvjfGXjG1QDEXeaDb4Qtzmy85wqyJjK7eKojQWa4BCRcow__&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovLypicmFkbS5qcGciLCJDb25kaXRpb24iOnsiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjEwLjUyLjE3LjkvMCJ9LCJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI1MjUyMDgzMH19fV19Cg__&Key-Pair-Id=mykey"


To generate an RSA key pair, you can use openssl and the following commands:

# Generate a 2048 bit key pair
openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout -out public-key.pem


=head1 OPTIONS

=over 8

=item B<--help>

Print a help message and exits.

=item B<--action> [action]

The action to execute.  action can be one of:

  encode - Generate a signed URL (using a canned policy or a user policy)
  decode - Decode a signed URL

=item B<--url>

The URL to en/decode

=item B<--stream>

The stream to en/decode

=item B<--private-key>

The path to your private key.

=item B<--key-pair-id>

The key pair identifier.

=item B<--policy>

The CloudFront policy document.

=item B<--expires>

The Unix epoch time when the URL is to expire. If both this option and
the --policy option are specified, --policy will be used. Otherwise, this 
option alone will use a canned policy.

=back

=cut

use strict;
use warnings;

# you might need to use CPAN to get these modules.
# run perl -MCPAN -e "install <module>" to get them.
# The openssl command line will also need to be in your $PATH.
use File::Temp qw/tempfile/;
use File::Slurp;
use Getopt::Long;
use IPC::Open2;
use MIME::Base64 qw(encode_base64 decode_base64);
use Pod::Usage;
use URI;

my $CANNED_POLICY 
    = '{"Statement":[{"Resource":"<RESOURCE>","Condition":{"DateLessThan":{"AWS:EpochTime":<EXPIRES>}}}]}';

my $POLICY_PARAM      = "Policy";
my $EXPIRES_PARAM     = "Expires";
my $SIGNATURE_PARAM   = "Signature";
my $KEY_PAIR_ID_PARAM = "Key-Pair-Id";

my $verbose = 0;
my $policy_filename = "";
my $expires_epoch = 0;
my $action = "";
my $help = 0;
my $key_pair_id = "";
my $url = "";
my $stream = "";
my $private_key_filename = "";

my $result = GetOptions("action=s"      => \$action,
                        "policy=s"      => \$policy_filename,
                        "expires=i"     => \$expires_epoch,
                        "private-key=s" => \$private_key_filename,
                        "key-pair-id=s" => \$key_pair_id,
                        "verbose"       => \$verbose,
                        "help"          => \$help,
                        "url=s"         => \$url,
                        "stream=s"      => \$stream,
                    );

if ($help or !$result) {
    pod2usage(1);
    exit;
}

if ($url eq "" and $stream eq "") {
    print STDERR "Must include a stream or a URL to encode or decode with the --stream or --url option\n";
    exit;
}

if ($url ne "" and $stream ne "") {
    print STDERR "Only one of --url and --stream may be specified\n";
    exit;
}

if ($url ne "" and !is_url_valid($url)) {
    exit;
}

if ($stream ne "") {
    exit unless is_stream_valid($stream);

    # The signing mechanism is identical, so from here on just pretend we're
    # dealing with a URL
    $url = $stream;
} 

if ($action eq "encode") {
    # The encode action will generate a private content URL given a base URL, 
    # a policy file (or an expires timestamp) and a key pair id parameter
    my $private_key;
    my $public_key;
    my $public_key_file;
    
    my $policy;
    if ($policy_filename eq "") {
        if ($expires_epoch == 0) {
            print STDERR "Must include policy filename with --policy argument or an expires" . 
                          "time using --expires\n";            
        }
        
        $policy = $CANNED_POLICY;
        $policy =~ s/<EXPIRES>/$expires_epoch/g;
        $policy =~ s/<RESOURCE>/$url/g;
    } else {
        if (! -e $policy_filename) {
            print STDERR "Policy file $policy_filename does not exist\n";
            exit;
        }
        $expires_epoch = 0; # ignore if set
        $policy = read_file($policy_filename);
    }

    if ($private_key_filename eq "") {
        print STDERR "You must specific the path to your private key file with --private-key\n";
        exit;
    }

    if (! -e $private_key_filename) {
        print STDERR "Private key file $private_key_filename does not exist\n";
        exit;
    }

    if ($key_pair_id eq "") {
        print STDERR "You must specify a key pair id with --key-pair-id\n";
        exit;
    }

    my $encoded_policy = url_safe_base64_encode($policy);
    my $signature = rsa_sha1_sign($policy, $private_key_filename);
    my $encoded_signature = url_safe_base64_encode($signature);

    my $generated_url = create_url($url, $encoded_policy, $encoded_signature, $key_pair_id, $expires_epoch);


    if ($stream ne "") {
        print "Encoded stream (for use within a swf):\n" . $generated_url . "\n";
        print "Encoded and escaped stream (for use on a webpage):\n" .  escape_url_for_webpage($generated_url) . "\n"; 
    } else {
        print "Encoded URL:\n" . $generated_url . "\n";
    }
} elsif ($action eq "decode") {
    my $decoded = decode_url($url);
    if (!$decoded) {
        print STDERR "Improperly formed URL\n";
        exit;
    }

    print_decoded_url($decoded);
} else {
    # No action specified, print help.  But only if this is run as a program (caller will be empty)
    pod2usage(1) unless caller();
}

# Decode a private content URL into its component parts
sub decode_url {
    my $url = shift;

    if ($url =~ /(.*)\?(.*)/) {
        my $base_url = $1;
        my $params = $2;

        my @unparsed_params = split(/&/, $params);
        my %params = ();
        foreach my $param (@unparsed_params) {
            my ($key, $val) = split(/=/, $param);
            $params{$key} = $val;
        }

        my $encoded_signature = "";
        if (exists $params{$SIGNATURE_PARAM}) {
            $encoded_signature = $params{"Signature"};
        } else {
            print STDERR "Missing Signature URL parameter\n";
            return 0;
        }

        my $encoded_policy = "";
        if (exists $params{$POLICY_PARAM}) {
            $encoded_policy = $params{$POLICY_PARAM};
        } else {
            if (!exists $params{$EXPIRES_PARAM}) {
                print STDERR "Either the Policy or Expires URL parameter needs to be specified\n";
                return 0;    
            }
            
            my $expires = $params{$EXPIRES_PARAM};
            
            my $policy = $CANNED_POLICY;
            $policy =~ s/<EXPIRES>/$expires/g;
            
            my $url_without_cf_params = $url;
            $url_without_cf_params =~ s/$SIGNATURE_PARAM=[^&]*&?//g;
            $url_without_cf_params =~ s/$POLICY_PARAM=[^&]*&?//g;
            $url_without_cf_params =~ s/$EXPIRES_PARAM=[^&]*&?//g;
            $url_without_cf_params =~ s/$KEY_PAIR_ID_PARAM=[^&]*&?//g;
            
            if ($url_without_cf_params =~ /(.*)\?$/) {
                $url_without_cf_params = $1;
            }
            
            $policy =~ s/<RESOURCE>/$url_without_cf_params/g;
            
            $encoded_policy = url_safe_base64_encode($policy);
        }

        my $key = "";
        if (exists $params{$KEY_PAIR_ID_PARAM}) {
            $key = $params{$KEY_PAIR_ID_PARAM};
        } else {
            print STDERR "Missing $KEY_PAIR_ID_PARAM parameter\n";
            return 0;
        }

        my $policy = url_safe_base64_decode($encoded_policy);

        my %ret = ();
        $ret{"base_url"} = $base_url;
        $ret{"policy"} = $policy;
        $ret{"key"} = $key;

        return \%ret;
    } else {
        return 0;
    }
}

# Print a decoded URL out
sub print_decoded_url {
    my $decoded = shift;

    print "Base URL: \n" . $decoded->{"base_url"} . "\n";
    print "Policy: \n" . $decoded->{"policy"} . "\n";
    print "Key: \n" . $decoded->{"key"} . "\n";
}

# Encode a string with base 64 encoding and replace some invalid URL characters
sub url_safe_base64_encode {
    my ($value) = @_;

    my $result = encode_base64($value);
    $result =~ tr|+=/|-_~|;

    return $result;
}

# Decode a string with base 64 encoding.  URL-decode the string first
# followed by reversing any special character ("+=/") translation.
sub url_safe_base64_decode {
    my ($value) = @_;

    $value =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
    $value =~ tr|-_~|+=/|;

    my $result = decode_base64($value);

    return $result;
}

# Create a private content URL
sub create_url {
    my ($path, $policy, $signature, $key_pair_id, $expires) = @_;
    
    my $result;
    my $separator = $path =~ /\?/ ? '&' : '?';
    if ($expires) {
        $result = "$path$separator$EXPIRES_PARAM=$expires&$SIGNATURE_PARAM=$signature&$KEY_PAIR_ID_PARAM=$key_pair_id";
    } else {
        $result = "$path$separator$POLICY_PARAM=$policy&$SIGNATURE_PARAM=$signature&$KEY_PAIR_ID_PARAM=$key_pair_id";
    }
    $result =~ s/\n//g;

    return $result;
}

# Sign a document with given private key file.
# The first argument is the document to sign
# The second argument is the name of the private key file
sub rsa_sha1_sign {
    my ($to_sign, $pvkFile) = @_;
    print "openssl sha1 -sign $pvkFile $to_sign\n";

    return write_to_program($pvkFile, $to_sign);
}

# Helper function to write data to a program
sub write_to_program {
my ($keyfile, $data) = @_;
unlink "temp_policy.dat" if (-e "temp_policy.dat");
unlink "temp_sign.dat" if (-e "temp_sign.dat");

write_file("temp_policy.dat", $data);

system("openssl dgst -sha1 -sign \"$keyfile\" -out temp_sign.dat temp_policy.dat");

my $output = read_file("temp_sign.dat");

    return $output;
}

# Read a file into a string and return the string
sub read_file {
    my ($file) = @_;

    open(INFILE, "<$file") or die("Failed to open $file: $!");
    my $str = join('', <INFILE>);
    close INFILE;

    return $str;
}

sub is_url_valid {
    my ($url) = @_;

    # HTTP distributions start with http[s]:// and are the correct thing to sign
    if ($url =~ /^https?:\/\//) {
        return 1;
    } else {
        print STDERR "CloudFront requires absolute URLs for HTTP distributions\n";
        return 0;
    }
}

sub is_stream_valid {
    my ($stream) = @_;

    if ($stream =~ /^rtmp:\/\// or $stream =~ /^\/?cfx\/st/) {
        print STDERR "Streaming distributions require that only the stream name is signed.\n";
        print STDERR "The stream name is everything after, but not including, cfx/st/\n";
        return 0;
    } else {
        return 1;
    }
}

# flash requires that the query parameters in the stream name are url
# encoded when passed in through javascript, etc.  This sub handles the minimal
# required url encoding.
sub escape_url_for_webpage {
    my ($url) = @_;

    $url =~ s/\?/%3F/g;
    $url =~ s/=/%3D/g;
    $url =~ s/&/%26/g;

    return $url;
}

1;
```

# 使用 PHP 创建 URL 签名
<a name="CreateURL_PHP"></a>

运行 PHP 的任何 Web 服务器可使用此 PHP 代码示例，来为私有的 CloudFront 分配创建策略声明和签名。该完整示例使用签名 URL 链接创建一个正常运作的网页，这些链接使用 CloudFront 流播放视频流。您可以从 [demo-php.zip](samples/demo-php.zip) 文件下载完整示例。

**备注**  
创建 URL 签名只是使用签名 URL 提供私有内容过程的一部分。有关整个过程的更多信息，请参阅[使用签名 URL](private-content-signed-urls.md)。
还可以通过使用 适用于 PHP 的 AWS SDK 中的 `UrlSigner` 类来创建签名 URL。有关更多信息，请参阅 *适用于 PHP 的 AWS SDKAPI 参考*中的 [Class UrlSigner](https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.CloudFront.UrlSigner.html)。

**Topics**
+ [创建 RSA SHA-1 签名](#sample-rsa-sign)
+ [创建标准策略](#sample-canned-policy)
+ [创建自定义策略](#sample-custom-policy)
+ [完整代码示例](#full-example)

以下部分将代码示例细分到各个单独的部分。您可在下方找到[完整代码示例](#full-example)。

## 创建 RSA SHA-1 签名
<a name="sample-rsa-sign"></a>

本代码示例执行以下操作：
+ 函数 `rsa_sha1_sign` 执行哈希操作并签署策略声明。所需的参数是策略语句和私有密钥，该私有密钥对应于分配的可信密钥组中的公有密钥。
+ 接下来，`url_safe_base64_encode` 函数创建签名 URL 安全版本。

```
function rsa_sha1_sign($policy, $private_key_filename) {
    $signature = "";

    // load the private key
    $fp = fopen($private_key_filename, "r");
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key);

    // compute signature
    openssl_sign($policy, $signature, $pkeyid);

    // free the key from memory
    openssl_free_key($pkeyid);

    return $signature;
}

function url_safe_base64_encode($value) {
    $encoded = base64_encode($value);
    // replace unsafe characters +, = and / with 
    // the safe characters -, _ and ~
    return str_replace(
        array('+', '=', '/'),
        array('-', '_', '~'),
        $encoded);
}
```

以下代码段使用函数 `get_canned_policy_stream_name()` 和 `get_custom_policy_stream_name()` 来创建标准策略和自定义策略。CloudFront 使用这些策略来创建用于流式传输视频的 URL，包括指定过期时间。

然后，您可以使用标准策略或自定义策略来确定如何管理对内容的访问。有关选择哪一项的更多信息，请参阅[决定为签名 URL 使用标准策略还是自定义策略](private-content-signed-urls.md#private-content-choosing-canned-custom-policy)部分。

## 创建标准策略
<a name="sample-canned-policy"></a>

以下代码示例构建了签名的*标准*策略声明。

**注意**  
`$expires` 变量是一个日期/时间戳，必须为整数，而不是字符串。

```
function get_canned_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $expires) {
    // this policy is well known by CloudFront, but you still need to sign it, since it contains your parameters
    $canned_policy = '{"Statement":[{"Resource":"' . $video_path . '","Condition":{"DateLessThan":{"AWS:EpochTime":'. $expires . '}}}]}';
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($canned_policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($canned_policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, null, $encoded_signature, $key_pair_id, $expires);
    // URL-encode the query string characters
    return $stream_name;
}
```

更多有关标准策略的信息，请参阅 [使用标准策略创建签名 URL](private-content-creating-signed-url-canned-policy.md)。

## 创建自定义策略
<a name="sample-custom-policy"></a>

以下代码示例构建了签名的*自定义* 策略声明。

```
function get_custom_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $policy) {
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, $encoded_policy, $encoded_signature, $key_pair_id, null);
    // URL-encode the query string characters
    return $stream_name;
}
```

更多有关自定义策略的信息，请参阅 [使用自定义策略创建签名 URL](private-content-creating-signed-url-custom-policy.md)。

## 完整代码示例
<a name="full-example"></a>

以下代码示例完整演示了使用 PHP 创建 CloudFront 签名 URL。您可以从 [demo-php.zip](samples/demo-php.zip) 文件下载完整示例。

在下面的示例中，您可以修改 `$policy` `Condition` 元素，以同时允许 IPv4 和 IPv6 地址范围。有关示例，请参阅《Amazon Simple Storage Service 用户指南》**中的[在 IAM 策略中使用 IPv6 地址](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ipv6-access.html#ipv6-access-iam)。

```
<?php

function rsa_sha1_sign($policy, $private_key_filename) {
    $signature = "";

    // load the private key
    $fp = fopen($private_key_filename, "r");
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key);

    // compute signature
    openssl_sign($policy, $signature, $pkeyid);

    // free the key from memory
    openssl_free_key($pkeyid);

    return $signature;
}

function url_safe_base64_encode($value) {
    $encoded = base64_encode($value);
    // replace unsafe characters +, = and / with the safe characters -, _ and ~
    return str_replace(
        array('+', '=', '/'),
        array('-', '_', '~'),
        $encoded);
}

function create_stream_name($stream, $policy, $signature, $key_pair_id, $expires) {
    $result = $stream;
    // if the stream already contains query parameters, attach the new query parameters to the end
    // otherwise, add the query parameters
    $separator = strpos($stream, '?') == FALSE ? '?' : '&';
    // the presence of an expires time means we're using a canned policy
    if($expires) {
        $result .= $separator . "Expires=" . $expires . "&Signature=" . $signature . "&Key-Pair-Id=" . $key_pair_id;
    }
    // not using a canned policy, include the policy itself in the stream name
    else {
        $result .= $separator . "Policy=" . $policy . "&Signature=" . $signature . "&Key-Pair-Id=" . $key_pair_id;
    }

    // new lines would break us, so remove them
    return str_replace('\n', '', $result);
}


function get_canned_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $expires) {
    // this policy is well known by CloudFront, but you still need to sign it, since it contains your parameters
    $canned_policy = '{"Statement":[{"Resource":"' . $video_path . '","Condition":{"DateLessThan":{"AWS:EpochTime":'. $expires . '}}}]}';
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($canned_policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($canned_policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, null, $encoded_signature, $key_pair_id, $expires);
    // URL-encode the query string characters
    return $stream_name;
}

function get_custom_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $policy) {
    // the policy contains characters that cannot be part of a URL, so we base64 encode it
    $encoded_policy = url_safe_base64_encode($policy);
    // sign the original policy, not the encoded version
    $signature = rsa_sha1_sign($policy, $private_key_filename);
    // make the signature safe to be included in a URL
    $encoded_signature = url_safe_base64_encode($signature);

    // combine the above into a stream name
    $stream_name = create_stream_name($video_path, $encoded_policy, $encoded_signature, $key_pair_id, null);
    // URL-encode the query string characters
    return $stream_name;
}


// Path to your private key.  Be very careful that this file is not accessible
// from the web!

$private_key_filename = '/home/test/secure/example-priv-key.pem';
$key_pair_id = 'K2JCJMDEHXQW5F';

// Make sure you have "Restrict viewer access" enabled on this path behaviour and using the above Trusted key groups (recommended).
$video_path = 'https://example.com/secure/example.mp4';

$expires = time() + 300; // 5 min from now
$canned_policy_stream_name = get_canned_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $expires);

// Get the viewer real IP from the x-forward-for header as $_SERVER['REMOTE_ADDR'] will return viewer facing IP. An alternative option is to use CloudFront-Viewer-Address header. Note that this header is a trusted CloudFront immutable header. Example format: IP:PORT ("CloudFront-Viewer-Address": "1.2.3.4:12345")
$client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$policy =
'{'.
    '"Statement":['.
        '{'.
            '"Resource":"'. $video_path . '",'.
            '"Condition":{'.
                '"IpAddress":{"AWS:SourceIp":"' . $client_ip . '/32"},'.
                '"DateLessThan":{"AWS:EpochTime":' . $expires . '}'.
            '}'.
        '}'.
    ']' .
    '}';
$custom_policy_stream_name = get_custom_policy_stream_name($video_path, $private_key_filename, $key_pair_id, $policy);

?>

<html>

<head>
    <title>CloudFront</title>
</head>

<body>
    <h1>Amazon CloudFront</h1>
    <h2>Canned Policy</h2>
    <h3>Expires at <?php echo gmdate('Y-m-d H:i:s T', $expires); ?></h3>
    <br />

    <div id='canned'>The canned policy video will be here: <br>
    
        <video width="640" height="360" autoplay muted controls>
        <source src="<?php echo $canned_policy_stream_name; ?>" type="video/mp4">
        Your browser does not support the video tag.
        </video>
    </div>

    <h2>Custom Policy</h2>
    <h3>Expires at <?php echo gmdate('Y-m-d H:i:s T', $expires); ?> only viewable by IP <?php echo $client_ip; ?></h3>
    <div id='custom'>The custom policy video will be here: <br>

         <video width="640" height="360" autoplay muted controls>
         <source src="<?php echo $custom_policy_stream_name; ?>" type="video/mp4">
         Your browser does not support the video tag.
        </video>
    </div> 

</body>

</html>
```

有关更多 URL 签名示例，请参阅以下主题：
+ [使用 Perl 创建 URL 签名](CreateURLPerl.md)
+ [使用 C\$1 和 .NET Framework 创建 URL 签名](CreateSignatureInCSharp.md)
+ [使用 Java 创建 URL 签名](CFPrivateDistJavaDevelopment.md)

您可以不使用签名 URL 来创建签名，而是使用签名 Cookie。有关更多信息，请参阅 [使用 PHP 创建签名 Cookie](signed-cookies-PHP.md)。

# 使用 C\$1 和 .NET Framework 创建 URL 签名
<a name="CreateSignatureInCSharp"></a>

本节中的 C\$1 示例实现了一个示例应用程序，该程序演示了如何使用标准和自定义策略声明为 CloudFront 私有分配创建签名。此示例包括基于 [适用于 .NET 的 AWS SDK](https://aws.amazon.com/sdkfornet) 的实用工具函数，这些函数在 .NET 应用程序中非常有用。

还可以通过使用 适用于 .NET 的 SDK 创建签名 URL 和签名 Cookie。在 *适用于 .NET 的 SDK API 参考*中，请参阅以下主题：
+ **签名 URL** – [AmazonCloudFrontUrlSigner](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFront/TCloudFrontUrlSigner.html) 
+ **签名 Cookie** – [AmazonCloudFrontCookieSigner](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFront/TCloudFrontCookieSigner.html) 

要下载代码，请转到 [C\$1 签名代码](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/AWS_PrivateCF_Distributions.zip)。

**备注**  
`AmazonCloudFrontUrlSigner` 和 `AmazonCloudFrontCookieSigner` 类已移至单独的包。有关使用它们的更多信息，请参阅《适用于 .NET 的 AWS SDK (V4) Developer Guide》**中的 [CookieSigner and UrlSigner](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html#net-dg-v4-CookieSigner-UrlSigner)。
创建 URL 签名只是使用签名 URL 提供私有内容过程的一部分。有关更多信息，请参阅 [使用签名 URL](private-content-signed-urls.md)。有关使用签名 Cookie 的更多信息，请参阅[使用签名 Cookie](private-content-signed-cookies.md)。

## 在 .NET 框架中使用 RSA 密钥
<a name="rsa-key-sdk-net"></a>

要在 .NET 框架中使用 RSA 密钥，您必须将 AWS 提供的 .pem 文件转换为 .NET 框架使用的 XML 格式。

转换之后，RSA 私有密钥文件是以下格式：

**Example ：XML .NET 框架格式的 RSA 私有密钥**  <a name="RSAPrivateKeyXML.NETFrameworkFormat"></a>

```
<RSAKeyValue>
  <Modulus>
    wO5IvYCP5UcoCKDo1dcspoMehWBZcyfs9QEzGi6Oe5y+ewGr1oW+vB2GPB
    ANBiVPcUHTFWhwaIBd3oglmF0lGQljP/jOfmXHUK2kUUnLnJp+oOBL2NiuFtqcW6h/L5lIpD8Yq+NRHg
    Ty4zDsyr2880MvXv88yEFURCkqEXAMPLE=
  </Modulus>
  <Exponent>AQAB</Exponent>
  <P>
    5bmKDaTz
    npENGVqz4Cea8XPH+sxt+2VaAwYnsarVUoSBeVt8WLloVuZGG9IZYmH5KteXEu7fZveYd9UEXAMPLE==
  </P>
  <Q>
    1v9l/WN1a1N3rOK4VGoCokx7kR2SyTMSbZgF9IWJNOugR/WZw7HTnjipO3c9dy1Ms9pUKwUF4
    6d7049EXAMPLE==
  </Q>
  <DP>
    RgrSKuLWXMyBH+/l1Dx/I4tXuAJIrlPyo+VmiOc7b5NzHptkSHEPfR9s1
    OK0VqjknclqCJ3Ig86OMEtEXAMPLE==
  </DP>
  <DQ>
    pjPjvSFw+RoaTu0pgCA/jwW/FGyfN6iim1RFbkT4
    z49DZb2IM885f3vf35eLTaEYRYUHQgZtChNEV0TEXAMPLE==
  </DQ>
  <InverseQ>
    nkvOJTg5QtGNgWb9i
    cVtzrL/1pFEOHbJXwEJdU99N+7sMK+1066DL/HSBUCD63qD4USpnf0myc24in0EXAMPLE==</InverseQ>
  <D>
      Bc7mp7XYHynuPZxChjWNJZIq+A73gm0ASDv6At7F8Vi9r0xUlQe/v0AQS3ycN8QlyR4XMbzMLYk
      3yjxFDXo4ZKQtOGzLGteCU2srANiLv26/imXA8FVidZftTAtLviWQZBVPTeYIA69ATUYPEq0a5u5wjGy
      UOij9OWyuEXAMPLE=
   </D>
</RSAKeyValue>
```

## C\$1 中的标准策略签名方法
<a name="canned-policy-signed-url-net"></a>

以下 C\$1 代码通过执行以下步骤创建使用标准策略的签名 URL：
+ 创建策略声明。
+ 使用 SHA1 对策略声明进行哈希处理，并使用 RSA 和其相应公有密钥位于可信密钥组中的私有密钥对结果进行签名。
+ 对经过哈希及签署的策略声明进行 Base64 编码，并替换使字符串能够安全地用作 URL 请求参数的特殊字符。
+ 联接值。

有关完整实现，请参阅 [C\$1 签名代码](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/AWS_PrivateCF_Distributions.zip)中的示例。

**注意**  
当您将公有密钥上传到 CloudFront 时，会返回 `keyId`。有关更多信息，请参阅 ![\[6\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/images/callouts/6.png)[ &Key-Pair-Id](private-content-creating-signed-url-canned-policy.md)。

**Example ：C\$1 中的标准策略签名方法**  <a name="ExampleCannedPolicySigningMethod-CSharp"></a>

```
public static string ToUrlSafeBase64String(byte[] bytes)
{
    return System.Convert.ToBase64String(bytes)
        .Replace('+', '-')
        .Replace('=', '_')
        .Replace('/', '~');
}

public static string CreateCannedPrivateURL(string urlString, 
    string durationUnits, string durationNumber, string pathToPolicyStmnt, 
    string pathToPrivateKey, string keyId)
{
    // args[] 0-thisMethod, 1-resourceUrl, 2-seconds-minutes-hours-days 
    // to expiration, 3-numberOfPreviousUnits, 4-pathToPolicyStmnt, 
    // 5-pathToPrivateKey, 6-keyId

    TimeSpan timeSpanInterval = GetDuration(durationUnits, durationNumber);

    // Create the policy statement.
    string strPolicy = CreatePolicyStatement(pathToPolicyStmnt,
        urlString, 
        DateTime.Now, 
        DateTime.Now.Add(timeSpanInterval), 
        "0.0.0.0/0");
    if ("Error!" == strPolicy) return "Invalid time frame." + 
        "Start time cannot be greater than end time.";

    // Copy the expiration time defined by policy statement.
    string strExpiration = CopyExpirationTimeFromPolicy(strPolicy);

    // Read the policy into a byte buffer.
    byte[] bufferPolicy = Encoding.ASCII.GetBytes(strPolicy);

    // Initialize the SHA1CryptoServiceProvider object and hash the policy data.
    using (SHA1CryptoServiceProvider 
        cryptoSHA1 = new SHA1CryptoServiceProvider())
    {
        bufferPolicy = cryptoSHA1.ComputeHash(bufferPolicy);

        // Initialize the RSACryptoServiceProvider object.
        RSACryptoServiceProvider providerRSA = new RSACryptoServiceProvider();
        XmlDocument xmlPrivateKey = new XmlDocument();

        // Load your private key, which you created by converting your 
        // .pem file to the XML format that the .NET framework uses.  
        // Several tools are available. 
        xmlPrivateKey.Load(pathToPrivateKey);

        // Format the RSACryptoServiceProvider providerRSA and 
        // create the signature.
        providerRSA.FromXmlString(xmlPrivateKey.InnerXml);
        RSAPKCS1SignatureFormatter rsaFormatter = 
            new RSAPKCS1SignatureFormatter(providerRSA);
        rsaFormatter.SetHashAlgorithm("SHA1");
        byte[] signedPolicyHash = rsaFormatter.CreateSignature(bufferPolicy);

        // Convert the signed policy to URL-safe base64 encoding and 
        // replace unsafe characters + = / with the safe characters - _ ~
        string strSignedPolicy = ToUrlSafeBase64String(signedPolicyHash);

        // Concatenate the URL, the timestamp, the signature, 
        // and the key pair ID to form the signed URL.
        return urlString + 
            "?Expires=" + 
            strExpiration + 
            "&Signature=" + 
            strSignedPolicy + 
            "&Key-Pair-Id=" + 
            keyId;
    }
}
```

## C\$1 中自定义策略签名方法
<a name="custom-policy-signed-url-net"></a>

以下 C\$1 代码通过执行以下步骤创建使用自定义策略的签名 URL：

1. 创建策略声明。

1. 对策略声明进行 Base64 编码，并替换使字符串能够安全地用作 URL 请求参数的特殊字符。

1. 使用 SHA1 对策略声明进行哈希处理，并使用 RSA 和其相应公有密钥位于可信密钥组中的私有密钥对结果进行加密。

1. 对经过哈希的策略声明进行 Base64 编码，并替换使字符串能够安全地用作 URL 请求参数的特殊字符。

1. 联接值。

有关完整实现，请参阅 [C\$1 签名代码](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/AWS_PrivateCF_Distributions.zip)中的示例。

**注意**  
当您将公有密钥上传到 CloudFront 时，会返回 `keyId`。有关更多信息，请参阅 ![\[6\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/images/callouts/6.png)[ &Key-Pair-Id](private-content-creating-signed-url-canned-policy.md)。

**Example ：C\$1 中的自定义策略签名方法**  <a name="ExampleCustomPolicySigningMethod-CSharp"></a>

```
public static string ToUrlSafeBase64String(byte[] bytes)
{
    return System.Convert.ToBase64String(bytes)
        .Replace('+', '-')
        .Replace('=', '_')
        .Replace('/', '~');
}

public static string CreateCustomPrivateURL(string urlString, 
    string durationUnits, string durationNumber, string startIntervalFromNow, 
    string ipaddress, string pathToPolicyStmnt, string pathToPrivateKey, 
    string keyId)
{
    // args[] 0-thisMethod, 1-resourceUrl, 2-seconds-minutes-hours-days 
    // to expiration, 3-numberOfPreviousUnits, 4-starttimeFromNow, 
    // 5-ip_address, 6-pathToPolicyStmt, 7-pathToPrivateKey, 8-keyId

    TimeSpan timeSpanInterval = GetDuration(durationUnits, durationNumber);
    TimeSpan timeSpanToStart = GetDurationByUnits(durationUnits, 
        startIntervalFromNow);
    if (null == timeSpanToStart) 
        return "Invalid duration units." + 
            "Valid options: seconds, minutes, hours, or days";
            
    string strPolicy = CreatePolicyStatement(
        pathToPolicyStmnt, urlString, DateTime.Now.Add(timeSpanToStart), 
        DateTime.Now.Add(timeSpanInterval), ipaddress);

    // Read the policy into a byte buffer.
    byte[] bufferPolicy = Encoding.ASCII.GetBytes(strPolicy);

    // Convert the policy statement to URL-safe base64 encoding and 
    // replace unsafe characters + = / with the safe characters - _ ~

    string urlSafePolicy = ToUrlSafeBase64String(bufferPolicy);

    // Initialize the SHA1CryptoServiceProvider object and hash the policy data.
    byte[] bufferPolicyHash;
    using (SHA1CryptoServiceProvider cryptoSHA1 = 
        new SHA1CryptoServiceProvider())
    {
        bufferPolicyHash = cryptoSHA1.ComputeHash(bufferPolicy);

        // Initialize the RSACryptoServiceProvider object.
        RSACryptoServiceProvider providerRSA = new RSACryptoServiceProvider();
        XmlDocument xmlPrivateKey = new XmlDocument();

        // Load your private key, which you created by converting your 
        // .pem file to the XML format that the .NET framework uses.  
        // Several tools are available. 
        xmlPrivateKey.Load(pathToPrivateKey);

        // Format the RSACryptoServiceProvider providerRSA 
        // and create the signature.
        providerRSA.FromXmlString(xmlPrivateKey.InnerXml);
        RSAPKCS1SignatureFormatter RSAFormatter = 
            new RSAPKCS1SignatureFormatter(providerRSA);
        RSAFormatter.SetHashAlgorithm("SHA1");
        byte[] signedHash = RSAFormatter.CreateSignature(bufferPolicyHash);

        // Convert the signed policy to URL-safe base64 encoding and 
        // replace unsafe characters + = / with the safe characters - _ ~
        string strSignedPolicy = ToUrlSafeBase64String(signedHash);

        return urlString + 
            "?Policy=" + 
            urlSafePolicy + 
            "&Signature=" + 
            strSignedPolicy + 
            "&Key-Pair-Id=" + 
            keyId;
    }
}
```

## 用于签名生成的实用工具方法
<a name="utility-methods-signed-url"></a>

以下方法从文件和签名生成解析时间间隔中获得策略声明。

**Example ：用于签名生成的实用工具方法**  <a name="UtilityMethodsForSignatureGeneration"></a>

```
public static string CreatePolicyStatement(string policyStmnt, 
   string resourceUrl, 
   DateTime startTime, 
   DateTime endTime, 
   string ipAddress)
   
{
   // Create the policy statement.
   FileStream streamPolicy = new FileStream(policyStmnt, FileMode.Open, FileAccess.Read);
   using (StreamReader reader = new StreamReader(streamPolicy))
   {
      string strPolicy = reader.ReadToEnd();

      TimeSpan startTimeSpanFromNow = (startTime - DateTime.Now);
      TimeSpan endTimeSpanFromNow = (endTime - DateTime.Now);
      TimeSpan intervalStart = 
         (DateTime.UtcNow.Add(startTimeSpanFromNow)) - 
         new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
      TimeSpan intervalEnd = 
         (DateTime.UtcNow.Add(endTimeSpanFromNow)) - 
         new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

      int startTimestamp = (int)intervalStart.TotalSeconds; // START_TIME
      int endTimestamp = (int)intervalEnd.TotalSeconds;  // END_TIME

      if (startTimestamp > endTimestamp)
         return "Error!";

      // Replace variables in the policy statement.
      strPolicy = strPolicy.Replace("RESOURCE", resourceUrl);
      strPolicy = strPolicy.Replace("START_TIME", startTimestamp.ToString());
      strPolicy = strPolicy.Replace("END_TIME", endTimestamp.ToString());
      strPolicy = strPolicy.Replace("IP_ADDRESS", ipAddress);
      strPolicy = strPolicy.Replace("EXPIRES", endTimestamp.ToString());
      return strPolicy;
   }   
}

public static TimeSpan GetDuration(string units, string numUnits)
{
   TimeSpan timeSpanInterval = new TimeSpan();
   switch (units)
   {
      case "seconds":
         timeSpanInterval = new TimeSpan(0, 0, 0, int.Parse(numUnits));
         break;
      case "minutes":
         timeSpanInterval = new TimeSpan(0, 0, int.Parse(numUnits), 0);
         break;
      case "hours":
         timeSpanInterval = new TimeSpan(0, int.Parse(numUnits), 0 ,0);
         break;
      case "days":
         timeSpanInterval = new TimeSpan(int.Parse(numUnits),0 ,0 ,0);
         break;
      default:
         Console.WriteLine("Invalid time units;" + 
            "use seconds, minutes, hours, or days");
         break;
   }
   return timeSpanInterval;
}

private static TimeSpan GetDurationByUnits(string durationUnits, 
   string startIntervalFromNow)
{
   switch (durationUnits)
   {
      case "seconds":
         return new TimeSpan(0, 0, int.Parse(startIntervalFromNow));
      case "minutes":
         return new TimeSpan(0, int.Parse(startIntervalFromNow), 0);
      case "hours":
         return new TimeSpan(int.Parse(startIntervalFromNow), 0, 0);
      case "days":
         return new TimeSpan(int.Parse(startIntervalFromNow), 0, 0, 0);
      default:
         return new TimeSpan(0, 0, 0, 0);
   }
}

public static string CopyExpirationTimeFromPolicy(string policyStatement)
{
   int startExpiration = policyStatement.IndexOf("EpochTime");
   string strExpirationRough = policyStatement.Substring(startExpiration + 
      "EpochTime".Length);
   char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
         
   List<char> listDigits = new List<char>(digits);
   StringBuilder buildExpiration = new StringBuilder(20);
         
   foreach (char c in strExpirationRough)
   {
      if (listDigits.Contains(c))
         buildExpiration.Append(c);
   }
   return buildExpiration.ToString();   
}
```

另请参阅
+ [使用 Perl 创建 URL 签名](CreateURLPerl.md)
+ [使用 PHP 创建 URL 签名](CreateURL_PHP.md)
+ [使用 Java 创建 URL 签名](CFPrivateDistJavaDevelopment.md)

# 使用 Java 创建 URL 签名
<a name="CFPrivateDistJavaDevelopment"></a>

除了以下代码示例外，您还可以使用 [适用于 Java 的 AWS SDK（版本 1）中的 `CloudFrontUrlSigner` 实用程序类](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/cloudfront/CloudFrontUrlSigner.html)来创建 [CloudFront 签名 URL](private-content-signed-urls.md)。

有关更多示例，请参阅《AWS SDK 代码示例代码库》**中的[使用 AWS SDK 创建签名 URL 和 Cookie](https://docs.aws.amazon.com/code-library/latest/ug/cloudfront_example_cloudfront_CloudFrontUtilities_section.html)。

**注意**  
创建签名 URL 只是使用[使用 CloudFront 提供私有内容](PrivateContent.md)过程的一部分。有关整个过程的更多信息，请参阅 [使用签名 URL](private-content-signed-urls.md)。

以下示例演示如何创建 CloudFront 签名 URL。

**Example Java 策略和签名加密方法**  <a name="ExampleJavaPolicyAndSignatureEncryptionMethods"></a>

```
package org.example;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import software.amazon.awssdk.services.cloudfront.CloudFrontUtilities;
import software.amazon.awssdk.services.cloudfront.model.CannedSignerRequest;
import software.amazon.awssdk.services.cloudfront.url.SignedUrl;

public class Main {

    public static void main(String[] args) throws Exception {
        CloudFrontUtilities cloudFrontUtilities = CloudFrontUtilities.create();
        Instant expirationDate = Instant.now().plus(7, ChronoUnit.DAYS);
        String resourceUrl = "https://a1b2c3d4e5f6g7.cloudfront.net";
        String keyPairId = "K1UA3WV15I7JSD";
        CannedSignerRequest cannedRequest = CannedSignerRequest.builder()
                .resourceUrl(resourceUrl)
                .privateKey(new java.io.File("/path/to/private_key.pem").toPath())
                .keyPairId(keyPairId)
                .expirationDate(expirationDate)
                .build();
        SignedUrl signedUrl = cloudFrontUtilities.getSignedUrlWithCannedPolicy(cannedRequest);
        String url = signedUrl.url();
        System.out.println(url);

    }
}
```

另请参阅：
+ [使用 Perl 创建 URL 签名](CreateURLPerl.md)
+ [使用 PHP 创建 URL 签名](CreateURL_PHP.md)
+ [使用 C\$1 和 .NET Framework 创建 URL 签名](CreateSignatureInCSharp.md)

# 限制对AWS源的访问
<a name="private-content-restricting-access-to-origin"></a>

您可以通过可提供以下好处的方式配置 CloudFront 和某些 AWS 源：
+ 限制对 AWS 源的访问，使其无法供公开访问。
+ 确保查看者（用户）只能通过指定的 CloudFront 分配访问 AWS 源中的内容。这可以防止查看者直接从源访问内容，或通过意外的 CloudFront 分配来访问内容。

为此，请将 CloudFront 配置为向 AWS 源发送经过身份验证的请求，并将 AWS 源配置为仅允许访问来自 CloudFront 的经过身份验证的请求。有关更多信息，请参阅以下主题，了解 AWS 源的兼容类型。

**Topics**
+ [限制对 AWS Elemental MediaPackage v2 源的访问](private-content-restricting-access-to-mediapackage.md)
+ [限制对AWS Elemental MediaStore源的访问](private-content-restricting-access-to-mediastore.md)
+ [限制对 AWS Lambda 函数 URL 源的访问](private-content-restricting-access-to-lambda.md)
+ [限制对 Amazon S3 源的访问](private-content-restricting-access-to-s3.md)
+ [通过 VPC 源限制访问](private-content-vpc-origins.md)

# 限制对 AWS Elemental MediaPackage v2 源的访问
<a name="private-content-restricting-access-to-mediapackage"></a>

CloudFront 提供*源访问控制*（OAC），用于限制对 MediaPackage v2 源的访问。

**注意**  
CloudFront OAC 仅支持 MediaPackage v2。不支持 MediaPackage v1。

**Topics**
+ [创建新的 OAC](#create-oac-overview-mediapackage)
+ [源访问控制的高级设置](#oac-advanced-settings-mediapackage)

## 创建新的 OAC
<a name="create-oac-overview-mediapackage"></a>

完成以下主题中描述的步骤，在 CloudFront 中设置新的 OAC。

**Topics**
+ [先决条件](#oac-prerequisites-mediapackage)
+ [向 CloudFront 授予访问 MediaPackage v2 源的权限](#oac-permission-to-access-mediapackage)
+ [创建 OAC](#create-oac-mediapackage)

### 先决条件
<a name="oac-prerequisites-mediapackage"></a>

在创建和设置 OAC 之前，您必须拥有带 MediaPackage v2 源的 CloudFront 分配。有关更多信息，请参阅 [使用 MediaStore 容器或 MediaPackage 通道](DownloadDistS3AndCustomOrigins.md#concept_AWS_Media)。

### 向 CloudFront 授予访问 MediaPackage v2 源的权限
<a name="oac-permission-to-access-mediapackage"></a>

在 CloudFront 分配中创建 OAC 或对其设置之前，请确保 CloudFront 具有访问 MediaPackage v2 源的权限。请在创建 CloudFront 分配后，但在分配配置中将 OAC 添加到 MediaPackage v2 源之前，执行此操作。

使用 IAM 策略来支持 CloudFront 服务主体 (`cloudfront.amazonaws.com`) 访问源。使用策略中的 `Condition` 元素，*仅* 在该请求代表包含 MediaPackage v2 源的 CloudFront 分配时，才允许 CloudFront 访问 MediaPackage v2 源。这是要向其添加 OAC 的具有 MediaPackage v2 源的分配。

**Example ：支持启用了 OAC 的 CloudFront 分配进行只读访问的 IAM 策略**  
以下策略允许 CloudFront 分配（`E1PDK09ESKHJWT`）访问 MediaPackage v2 源。该源是为 `Resource` 元素指定的 ARN。    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {"Service": "cloudfront.amazonaws.com"},
            "Action": "mediapackagev2:GetObject",
            "Resource": "arn:aws:mediapackagev2:us-east-1:123456789012:channelGroup/channel-group-name/channel/channel-name/originEndpoint/origin_endpoint_name",
            "Condition": {
                "StringEquals": {"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1PDK09ESKHJWT"}
            }
        }
    ]
}
```

**备注**  
如果您启用了 MQAR 功能和来源访问控制（OAC），请将 `mediapackagev2:GetHeadObject` 操作添加到 IAM 策略中。MQAR 需要此权限，才能向 MediaPackage v2 源发送 `HEAD` 请求。有关 MQAR 的更多信息，请参阅[媒体质量感知弹性](media-quality-score.md)。
如果您创建的分配无权访问您的 MediaPackage v2 源，则可以从 CloudFront 控制台中选择**复制策略**，然后选择**更新端点权限**。然后，您可以将复制的权限附加到端点。有关更多信息，请参阅《AWS Elemental MediaPackage 用户指南》**中的[端点策略字段](https://docs.aws.amazon.com/mediapackage/latest/userguide/endpoints-policy.html)。

### 创建 OAC
<a name="create-oac-mediapackage"></a>

要创建 OAC，可以使用 AWS 管理控制台、CloudFormation、AWS CLI 或 CloudFront API。

------
#### [ Console ]

**创建 OAC**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择 **Origin access**（源访问）。

1. 选择 **Create control setting**（创建控制设置）。

1. 在**创建新 OAC** 页面上，执行以下操作：

   1. 输入 OAC 的**名称**和（可选）**描述**。

   1. 在**签名行为**中，建议保留默认设置（**签署请求（推荐）**）。有关更多信息，请参阅 [源访问控制的高级设置](#oac-advanced-settings-mediapackage)。

1. 对于**源类型**，请选择 **MediaPackage V2**。

1. 选择**创建**。
**提示**  
创建 OAC 后，记下**名称**。在以下过程中，您需要此名称。

**向分配中的 MediaPackage v2 源添加 OAC**

1. 通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择具有要向其中添加 OAC 的 MediaPackage V2 源的分配，然后选择**源**选项卡。

1. 选择要向其中添加 OAC 的 MediaPackage v2 源，然后选择**编辑**。

1. 对于源的**协议**，请选择**仅 HTTPS**。

1. 从**源访问控制**下拉菜单中，选择要使用的 OAC。

1. 选择**保存更改**。

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署自己发送到 MediaPackage v2 源的所有请求。

------
#### [ CloudFormation ]

要使用 CloudFormation 创建 OAC，请使用 `AWS::CloudFront::OriginAccessControl` 资源类型。以下示例演示了 YAML 格式的 CloudFormation 模板语法，用于创建 OAC。

```
Type: AWS::CloudFront::OriginAccessControl
Properties: 
  OriginAccessControlConfig: 
      Description: An optional description for the origin access control
      Name: ExampleOAC
      OriginAccessControlOriginType: mediapackagev2
      SigningBehavior: always
      SigningProtocol: sigv4
```

有关更多信息，请参阅 *AWS CloudFormation 用户指南* 中的 [AWS::CloudFront::OriginAccessControl](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-originaccesscontrol.html)。

------
#### [ CLI ]

要使用 AWS Command Line Interface (AWS CLI) 创建源访问控制，请使用 **aws cloudfront create-origin-access-control** 命令。您可以使用输入文件来提供命令的输入参数，而不是将每个单独的参数指定为命令行输入。

**创建源访问控制（带输入文件的 CLI）**

1. 使用以下命令创建名为 `origin-access-control.yaml` 的文件。此文件包含 **create-origin-access-control** 命令的所有输入参数。

   ```
   aws cloudfront create-origin-access-control --generate-cli-skeleton yaml-input > origin-access-control.yaml
   ```

1. 打开刚创建的 `origin-access-control.yaml` 文件。编辑文件以添加 OAC 的名称、描述（可选），然后将 `SigningBehavior` 更改为 `always`。然后保存文件。

   有关其他 OAC 设置的信息，请参阅[源访问控制的高级设置](#oac-advanced-settings-mediapackage)。

1. 使用以下命令通过 `origin-access-control.yaml` 文件中的输入参数创建源访问控制。

   ```
   aws cloudfront create-origin-access-control --cli-input-yaml file://origin-access-control.yaml
   ```

   记下命令输出中的 `Id` 值。您需要使用它将 OAC 添加到 CloudFront 分配中的 MediaPackage v2 源。

**将 OAC 附加到现有分配中的 MediaPackage v2 源（带输入文件的 CLI）**

1. 使用以下命令保存要向其添加 OAC 的 CloudFront 分配的分配配置。该分配必须具有 MediaPackage v2 源。

   ```
   aws cloudfront get-distribution-config --id <CloudFront distribution ID> --output yaml > dist-config.yaml
   ```

1. 打开刚创建的名为 `dist-config.yaml` 的文件。编辑文件，进行以下更改：
   + 在 `Origins` 对象中，将 OAC 的 ID 添加到名为 `OriginAccessControlId` 的字段中。
   + 从名为 `OriginAccessIdentity` 的字段中移除值（如果存在）。
   + 将 `ETag` 字段重命名为 `IfMatch`，但不更改字段的值。

   完成后保存该文件。

1. 使用以下命令更新分配以使用源访问控制。

   ```
   aws cloudfront update-distribution --id <CloudFront distribution ID> --cli-input-yaml file://dist-config.yaml
   ```

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署自己发送到 MediaPackage v2 源的所有请求。

------
#### [ API ]

要使用 CloudFront API 创建 OAC，请使用 [CreateOriginAccessControl](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateOriginAccessControl.html)。有关您在此 API 调用中指定的字段的更多信息，请参阅有关 AWS SDK 或其他 API 客户端的 API 参考文档。

创建 OAC 后，可以使用下面的任何一个 API 调用将其附加到分配中的 MediaPackage v2 源：
+ 要将它附加到现有分配，请使用 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。
+ 要将它附加到新分配，请使用 [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html)。

对于这两个 API 调用，请在源内的 `OriginAccessControlId` 字段中提供 OAC ID。有关您在这些 API 调用中指定的其他字段的更多信息，请参阅 [所有分配设置参考](distribution-web-values-specify.md) 以及有关 AWS SDK 或其他 API 客户端的 API 参考文档。

------

## 源访问控制的高级设置
<a name="oac-advanced-settings-mediapackage"></a>

CloudFront OAC 功能包括仅适用于特定使用案例的高级设置。除非您特别需要高级设置，否则请使用推荐的设置。

OAC 包含一个名为**签名行为**（在控制台中）或 `SigningBehavior`（在 API、CLI 和 CloudFormation 中）的设置。此设置提供以下选项：

**始终签署源请求（推荐设置）**  
我们建议使用此设置，此设置在控制台中名为**签署请求（推荐）**，在 API、CLI 和 CloudFormation 中名为 `always`。使用此设置，CloudFront 将始终签署自己发送到 MediaPackage v2 源的所有请求。

**切勿签署源请求**  
此设置在控制台中名为 **Do not sign requests**（请勿签署请求），在 API、CLI 和 CloudFormation 中名为 `never`。使用此设置关闭所有使用此 OAC 的分配中所有源的 OAC。与从所有使用 OAC 的源和分配中逐一删除 OAC 相比，这可以节省时间和精力。使用此设置，CloudFront 不会签署自己发送到 MediaPackage v2 源的任何请求。  
要使用此设置，MediaPackage v2 源必须可供公开访问。如果您将此设置用于不可公开访问的 MediaPackage v2 源，则 CloudFront 无法访问该源。MediaPackage v2 源向 CloudFront 返回错误，而 CloudFront 将这些错误传递给查看器。有关更多信息，请参阅《AWS Elemental MediaPackage 用户指南》**中针对 [MediaPackage 中的策略和权限](https://docs.aws.amazon.com/mediapackage/latest/userguide/policies-permissions.html)的 MediaPackage v2 策略示例。

**不要改写查看器（客户端）`Authorization` 标头**  
此设置在控制台中名为 **Do not override authorization header**（请勿改写授权标头），在 API、CLI 和 CloudFormation 中名为 `no-override`。如果您希望 CloudFront 仅在相应的查看器请求不包含 `Authorization` 标头时签署源请求，请使用此设置。使用此设置，CloudFront 会在查看器请求中存在 `Authorization` 标头时传递该标头，但在查看器请求不包含 `Authorization` 标头时对源请求进行签名（添加自己的 `Authorization` 标头）。  
要传递查看器请求的 `Authorization` 标头，您*必须* 针对所有使用与此源访问控制关联的 MediaPackage v2 源的缓存行为将 `Authorization` 标头添加到[缓存策略](controlling-the-cache-key.md)中。

# 限制对AWS Elemental MediaStore源的访问
<a name="private-content-restricting-access-to-mediastore"></a>

CloudFront 提供*源访问控制*（OAC），用于限制对 AWS Elemental MediaStore 源的访问。

**Topics**
+ [创建新的源访问控制](#create-oac-overview-mediastore)
+ [源访问控制的高级设置](#oac-advanced-settings-mediastore)

## 创建新的源访问控制
<a name="create-oac-overview-mediastore"></a>

完成以下主题中描述的步骤，在 CloudFront 中设置新的源访问控制。

**Topics**
+ [先决条件](#oac-prerequisites-mediastore)
+ [向 CloudFront 授予访问 MediaStore 源的权限](#oac-permission-to-access-mediastore)
+ [创建源访问控制](#create-oac-mediastore)

### 先决条件
<a name="oac-prerequisites-mediastore"></a>

在创建和设置源访问控制之前，您必须拥有带有 MediaStore 源的 CloudFront 分配。

### 向 CloudFront 授予访问 MediaStore 源的权限
<a name="oac-permission-to-access-mediastore"></a>

在 CloudFront 分配中创建来源访问控制或对其设置之前，请确保 CloudFront 具有访问 MediaStore 源的权限。请在创建 CloudFront 分配后，但在分配配置中将 OAC 添加到 MediaStore 源之前，执行此操作。

使用 MediaStore 容器策略来支持 CloudFront 服务主体 (`cloudfront.amazonaws.com`) 访问源。使用策略中的 `Condition` 元素，仅在请求代表包含 MediaStore 源的 CloudFront 分配时，才允许 CloudFront 访问 MediaStore 容器。这是要向其添加 OAC 的具有 MediaStore 源的分配。

以下是支持 CloudFront 分配访问 MediaStore 源的 MediaStore 容器策略示例。

**Example 支持启用了 OAC 的 CloudFront 分配进行只读访问的 MediaStore 容器策略**    
****  

```
{
        "Version":"2012-10-17",		 	 	 
        "Statement": [
            {
                "Sid": "AllowCloudFrontServicePrincipalReadOnly",
                "Effect": "Allow",
                "Principal": {
                  "Service": "cloudfront.amazonaws.com"
                },
                "Action": [ 
                  "mediastore:GetObject"
                ],
                "Resource": "arn:aws:mediastore:us-east-1:111122223333:container/<container name>/*",
                "Condition": {
                    "StringEquals": {
                      "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/CloudFront-distribution-ID"
                    },
                    "Bool": {
                      "aws:SecureTransport": "true"
                    }
                }
            }
        ]
}
```

**Example 支持启用了 OAC 的 CloudFront 分配进行读写访问的 MediaStore 容器策略**    
****  

```
{
        "Version":"2012-10-17",		 	 	 
        "Statement": [
            {
                "Sid": "AllowCloudFrontServicePrincipalReadWrite",
                "Effect": "Allow",
                "Principal": {
                  "Service": "cloudfront.amazonaws.com"
                },
                "Action": [ 
                  "mediastore:GetObject",
                  "mediastore:PutObject"
                ],
                "Resource": "arn:aws:mediastore:us-east-1:111122223333:container/container-name/*",
                "Condition": {
                    "StringEquals": {
                      "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/CloudFront-distribution-ID"
                    },
                    "Bool": {
                      "aws:SecureTransport": "true"
                    }
                }
            }
        ]
}
```

**注意**  
要允许写访问，您必须在 CloudFront 分配的行为设置中将 **Allowed HTTP methods**（允许的 HTTP 方法）配置为包含 `PUT`。

### 创建源访问控制
<a name="create-oac-mediastore"></a>

要创建 OAC，可以使用 AWS 管理控制台、CloudFormation、AWS CLI 或 CloudFront API。

------
#### [ Console ]

**创建源访问控制**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择 **Origin access**（源访问）。

1. 选择 **Create control setting**（创建控制设置）。

1. 在 **Create control setting**（创建控制设置）表单上，执行以下操作：

   1. 在 **Details**（详细信息）窗格中，输入源访问控制的 **Name**（名称）和（可选）**Description**（描述）。

   1. 在 **Settings**（设置）中，建议保留默认设置 [**Sign requests (recommended)**（签署请求（推荐））]。有关更多信息，请参阅 [源访问控制的高级设置](#oac-advanced-settings-mediastore)。

1. 从 **Origin type**（源类型）下拉列表中选择 MediaStore。

1. 选择**创建**。

   创建 OAC 后，记下 **Name**（名称）。在以下过程中，您需要此名称。

**向分配中的 MediaStore 源添加源访问控制**

1. 通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择具有要向其添加 OAC 的 MediaStore 源的分配，然后选择 **Origins**（源）选项卡。

1. 选择要向其添加 OAC 的 MediaStore 源，然后选择 **Edit**（编辑）。

1. 对于源的 **Protocol**（协议），选择 **HTTPS only**（仅 HTTPS）。

1. 从 **Origin access control**（源访问控制）下拉菜单中，选择要使用的 OAC。

1. 选择**保存更改**。

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署其发送到 MediaStore 桶源的所有请求。

------
#### [ CloudFormation ]

要使用 CloudFormation 创建源访问控制（OAC），请使用 `AWS::CloudFront::OriginAccessControl` 资源类型。以下示例显示了 YAML 格式的 CloudFormation 模板语法，用于创建源访问控制。

```
Type: AWS::CloudFront::OriginAccessControl
Properties: 
  OriginAccessControlConfig: 
      Description: An optional description for the origin access control
      Name: ExampleOAC
      OriginAccessControlOriginType: mediastore
      SigningBehavior: always
      SigningProtocol: sigv4
```

有关更多信息，请参阅 *AWS CloudFormation 用户指南* 中的 [AWS::CloudFront::OriginAccessControl](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-originaccesscontrol.html)。

------
#### [ CLI ]

要使用 AWS Command Line Interface (AWS CLI) 创建源访问控制，请使用 **aws cloudfront create-origin-access-control** 命令。您可以使用输入文件来提供命令的输入参数，而不是将每个单独的参数指定为命令行输入。

**创建源访问控制（带输入文件的 CLI）**

1. 使用以下命令创建名为 `origin-access-control.yaml` 的文件。此文件包含 **create-origin-access-control** 命令的所有输入参数。

   ```
   aws cloudfront create-origin-access-control --generate-cli-skeleton yaml-input > origin-access-control.yaml
   ```

1. 打开刚创建的 `origin-access-control.yaml` 文件。编辑文件以添加 OAC 的名称、描述（可选），然后将 `SigningBehavior` 更改为 `always`。然后保存文件。

   有关其他 OAC 设置的信息，请参阅[源访问控制的高级设置](#oac-advanced-settings-mediastore)。

1. 使用以下命令通过 `origin-access-control.yaml` 文件中的输入参数创建源访问控制。

   ```
   aws cloudfront create-origin-access-control --cli-input-yaml file://origin-access-control.yaml
   ```

   记下命令输出中的 `Id` 值。您需要使用它将 OAC 添加到 CloudFront 分配中的 MediaStore 源。

**将 OAC 附加到现有分配中的 MediaStore 源（带输入文件的 CLI）**

1. 使用以下命令保存要向其添加 OAC 的 CloudFront 分配的分配配置。该分配必须有 MediaStore 源。

   ```
   aws cloudfront get-distribution-config --id <CloudFront distribution ID> --output yaml > dist-config.yaml
   ```

1. 打开刚创建的名为 `dist-config.yaml` 的文件。编辑文件，进行以下更改：
   + 在 `Origins` 对象中，将 OAC 的 ID 添加到名为 `OriginAccessControlId` 的字段中。
   + 从名为 `OriginAccessIdentity` 的字段中移除值（如果存在）。
   + 将 `ETag` 字段重命名为 `IfMatch`，但不更改字段的值。

   完成后保存该文件。

1. 使用以下命令更新分配以使用源访问控制。

   ```
   aws cloudfront update-distribution --id <CloudFront distribution ID> --cli-input-yaml file://dist-config.yaml
   ```

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署其发送到 MediaStore 源的所有请求。

------
#### [ API ]

要使用 CloudFront API 创建源访问控制，请使用 [CreateOriginAccessControl](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateOriginAccessControl.html)。有关您在此 API 调用中指定的字段的更多信息，请参阅 AWS 开发工具包或其他 API 客户端的 API 参考文档。

创建源访问控制后，可以使用下面的任何一个 API 调用将其附加到分配中的 MediaStore 源：
+ 要将它附加到现有分配，请使用 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。
+ 要将它附加到新分配，请使用 [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html)。

对于这两个 API 调用，请在源内的 `OriginAccessControlId` 字段中提供源访问控制 ID。有关您在这些 API 调用中指定的其他字段的更多信息，请参阅 [所有分配设置参考](distribution-web-values-specify.md) 以及有关 AWS SDK 或其他 API 客户端的 API 参考文档。

------

## 源访问控制的高级设置
<a name="oac-advanced-settings-mediastore"></a>

CloudFront 源访问控制特征包括仅适用于特定使用案例的高级设置。除非您特别需要高级设置，否则请使用推荐的设置。

源访问控制包含一个名为 **Signing behavior**（签名行为）（在控制台中）或 `SigningBehavior`（在 API、CLI 和 CloudFormation）的设置。此设置提供以下选项：

**始终签署源请求（推荐设置）**  
我们建议使用此设置，此设置在控制台中名为**签署请求（推荐）**，在 API、CLI 和 CloudFormation 中名为 `always`。使用此设置，CloudFront 将始终签署其发送到 MediaStore 源的所有请求。

**切勿签署源请求**  
此设置在控制台中名为 **Do not sign requests**（请勿签署请求），在 API、CLI 和 CloudFormation 中名为 `never`。使用此设置关闭所有使用此源访问控制的分配中所有源的源访问控制。与从所有使用源访问控制的源和分配中逐一删除源访问控制相比，这可以节省时间和精力。使用此设置，CloudFront 不会签署其发送到 MediaStore 源的任何请求。  
要使用此设置，MediaStore 源必须可供公开访问。如果您将此设置用于不可公开访问的 MediaStore 源，则 CloudFront 无法访问该源。MediaStore 源向 CloudFront 返回错误，而 CloudFront 将这些错误传递给查看器。有关更多信息，请参阅[通过 HTTPS 进行公有读取访问](https://docs.aws.amazon.com/mediastore/latest/ug/policies-examples-public-https.html)的 MediaStore 容器策略示例。

**不要改写查看器（客户端）`Authorization` 标头**  
此设置在控制台中名为 **Do not override authorization header**（请勿改写授权标头），在 API、CLI 和 CloudFormation 中名为 `no-override`。如果您希望 CloudFront 仅在相应的查看器请求不包含 `Authorization` 标头时签署源请求，请使用此设置。使用此设置，CloudFront 会在查看器请求中存在 `Authorization` 标头时传递该标头，但在查看器请求不包含 `Authorization` 标头时对源请求进行签名（添加自己的 `Authorization` 标头）。  
要传递查看器请求的 `Authorization` 标头，您*必须* 针对所有使用与此源访问控制关联的 MediaStore 源的缓存行为将 `Authorization` 标头添加到[缓存策略](controlling-the-cache-key.md)中。

# 限制对 AWS Lambda 函数 URL 源的访问
<a name="private-content-restricting-access-to-lambda"></a>

CloudFront 提供*源访问控制*（OAC），以限制对 Lambda 函数 URL 源的访问。

**Topics**
+ [创建新 OAC](#create-oac-overview-lambda)
+ [源访问控制的高级设置](#oac-advanced-settings-lambda)
+ [示例模板代码](#example-template-code-lambda-oac)

## 创建新 OAC
<a name="create-oac-overview-lambda"></a>

完成以下主题中描述的步骤，在 CloudFront 中设置新的 OAC。

**重要**  
如果您在 Lambda 函数 URL 中使用 `PUT` 或 `POST` 方法，则您的用户在向 CloudFront 发送请求时，必须计算正文的 SHA256，并在 `x-amz-content-sha256` 标头中包含请求正文的有效载荷哈希值。Lambda 不支持未签名的有效负载。

**Topics**
+ [先决条件](#oac-prerequisites-lambda)
+ [向 CloudFront 授予访问 Lambda 函数 URL 的权限](#oac-permission-to-access-lambda)
+ [创建 OAC](#create-oac-lambda)

### 先决条件
<a name="oac-prerequisites-lambda"></a>

在创建和设置 OAC 之前，您必须拥有将 Lambda 函数 URL 作为源的 CloudFront 分配。要使用 OAC，您必须将 `AWS_IAM` 指定作为 `AuthType` 参数的值。有关更多信息，请参阅 [使用 Lambda 函数 URL](DownloadDistS3AndCustomOrigins.md#concept_lambda_function_url)。

### 向 CloudFront 授予访问 Lambda 函数 URL 的权限
<a name="oac-permission-to-access-lambda"></a>

在 CloudFront 分配中创建 OAC 或对其设置之前，请确保 CloudFront 具有访问 Lambda 函数 URL 的权限。请在创建 CloudFront 分配后，但在分配配置中将 OAC 添加到 Lambda 函数 URL 之前，执行此操作。

**注意**  
要更新 Lambda 函数 URL 的 IAM 策略，您必须使用 AWS Command Line Interface（AWS CLI）。目前不支持在 Lambda 控制台中编辑 IAM 策略。

以下 AWS CLI 命令授予 CloudFront 服务主体（`cloudfront.amazonaws.com`）访问您的 Lambda 函数 URL 的权限。使用策略中的 `Condition` 元素，*仅* 在该请求代表包含 Lambda 函数 URL 的 CloudFront 分配时，才允许 CloudFront 访问 Lambda。这是您要向其添加 OAC 的具有 Lambda 函数 URL 源的分配。

**Example ：更新策略以支持启用了 OAC 的 CloudFront 分配进行只读访问的 AWS CLI 命令**  
以下 AWS CLI 命令允许 CloudFront 分配（`E1PDK09ESKHJWT`）访问您的 Lambda *`FUNCTION_URL_NAME`*。

```
aws lambda add-permission \
--statement-id "AllowCloudFrontServicePrincipal" \
--action "lambda:InvokeFunctionUrl" \
--principal "cloudfront.amazonaws.com" \
--source-arn "arn:aws:cloudfront::123456789012:distribution/E1PDK09ESKHJWT" \
--function-name FUNCTION_URL_NAME
```

```
aws lambda add-permission \
--statement-id "AllowCloudFrontServicePrincipalInvokeFunction" \
--action "lambda:InvokeFunction" \
--principal "cloudfront.amazonaws.com" \
--source-arn "arn:aws:cloudfront::123456789012:distribution/E1PDK09ESKHJWT" \
--function-name FUNCTION_URL_NAME
```

**注意**  
如果您创建了一个分配，但它没有访问您的 Lambda 函数 URL 的权限，则可以从 CloudFront 控制台中选择**复制 CLI 命令**，然后从命令行终端输入此命令。有关更多信息，请参阅《AWS Lambda 开发人员指南》**中的[向 AWS 服务 授予函数访问权限](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-serviceinvoke)。

### 创建 OAC
<a name="create-oac-lambda"></a>

要创建 OAC，可以使用 AWS 管理控制台、CloudFormation、AWS CLI 或 CloudFront API。

------
#### [ Console ]

**创建 OAC**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择 **Origin access**（源访问）。

1. 选择 **Create control setting**（创建控制设置）。

1. 在**创建新 OAC** 页面上，执行以下操作：

   1. 输入 OAC 的**名称**和（可选）**描述**。

   1. 在**签名行为**中，建议保留默认设置（**签署请求（推荐）**）。有关更多信息，请参阅 [源访问控制的高级设置](#oac-advanced-settings-lambda)。

1. 对于**源类型**，请选择 **Lambda**。

1. 选择**创建**。
**提示**  
创建 OAC 后，记下**名称**。在以下过程中，您需要此名称。

**向分配中的 Lambda 函数 URL 添加源访问控制**

1. 通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择具有要向其中添加 OAC 的 Lambda 函数 URL 的分配，然后选择**源**选项卡。

1. 选择要向其中添加 OAC 的 Lambda 函数 URL，然后选择**编辑**。

1. 对于源的**协议**，请选择**仅 HTTPS**。

1. 从**源访问控制**下拉菜单中，选择要使用的 OAC。

1. 选择**保存更改**。

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署自己发送到 Lambda 函数 URL 的所有请求。

------
#### [ CloudFormation ]

要使用 CloudFormation 创建 OAC，请使用 `AWS::CloudFront::OriginAccessControl` 资源类型。以下示例演示了 YAML 格式的 CloudFormation 模板语法，用于创建 OAC。

```
Type: AWS::CloudFront::OriginAccessControl
Properties: 
  OriginAccessControlConfig: 
      Description: An optional description for the origin access control
      Name: ExampleOAC
      OriginAccessControlOriginType: lambda
      SigningBehavior: always
      SigningProtocol: sigv4
```

有关更多信息，请参阅 *AWS CloudFormation 用户指南* 中的 [AWS::CloudFront::OriginAccessControl](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-originaccesscontrol.html)。

------
#### [ CLI ]

要使用 AWS Command Line Interface (AWS CLI) 创建源访问控制，请使用 **aws cloudfront create-origin-access-control** 命令。您可以使用输入文件来提供命令的输入参数，而不是将每个单独的参数指定为命令行输入。

**创建源访问控制（带输入文件的 CLI）**

1. 使用以下命令创建名为 `origin-access-control.yaml` 的文件。此文件包含 **create-origin-access-control** 命令的所有输入参数。

   ```
   aws cloudfront create-origin-access-control --generate-cli-skeleton yaml-input > origin-access-control.yaml
   ```

1. 打开刚创建的 `origin-access-control.yaml` 文件。编辑文件以添加 OAC 的名称、描述（可选），然后将 `SigningBehavior` 更改为 `always`。然后保存文件。

   有关其他 OAC 设置的信息，请参阅[源访问控制的高级设置](#oac-advanced-settings-lambda)。

1. 使用以下命令通过 `origin-access-control.yaml` 文件中的输入参数创建源访问控制。

   ```
   aws cloudfront create-origin-access-control --cli-input-yaml file://origin-access-control.yaml
   ```

   记下命令输出中的 `Id` 值。您需要使用它将 OAC 添加到 CloudFront 分配中的 Lambda 函数 URL。

**将 OAC 附加到现有分配中的 Lambda 函数 URL（带输入文件的 CLI）**

1. 使用以下命令保存要向其添加 OAC 的 CloudFront 分配的分配配置。该分配必须将 Lambda 函数 URL 作为源。

   ```
   aws cloudfront get-distribution-config --id <CloudFront distribution ID> --output yaml > dist-config.yaml
   ```

1. 打开刚创建的名为 `dist-config.yaml` 的文件。编辑文件，进行以下更改：
   + 在 `Origins` 对象中，将 OAC 的 ID 添加到名为 `OriginAccessControlId` 的字段中。
   + 从名为 `OriginAccessIdentity` 的字段中移除值（如果存在）。
   + 将 `ETag` 字段重命名为 `IfMatch`，但不更改字段的值。

   完成后保存该文件。

1. 使用以下命令更新分配以使用源访问控制。

   ```
   aws cloudfront update-distribution --id <CloudFront distribution ID> --cli-input-yaml file://dist-config.yaml
   ```

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署自己发送到 Lambda 函数 URL 的所有请求。

------
#### [ API ]

要使用 CloudFront API 创建 OAC，请使用 [CreateOriginAccessControl](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateOriginAccessControl.html)。有关您在此 API 调用中指定的字段的更多信息，请参阅有关 AWS SDK 或其他 API 客户端的 API 参考文档。

创建 OAC 后，可以使用下面的任何一个 API 调用将其附加到分配中的 Lambda 函数 URL：
+ 要将它附加到现有分配，请使用 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。
+ 要将它附加到新分配，请使用 [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html)。

对于这两个 API 调用，请在源内的 `OriginAccessControlId` 字段中提供 OAC ID。有关您在这些 API 调用中指定的其他字段的更多信息，请参阅有关 AWS SDK 或其他 API 客户端的 API 参考文档。

------

## 源访问控制的高级设置
<a name="oac-advanced-settings-lambda"></a>

CloudFront OAC 功能包括仅适用于特定使用案例的高级设置。除非您特别需要高级设置，否则请使用推荐的设置。

OAC 包含一个名为**签名行为**（在控制台中）或 `SigningBehavior`（在 API、CLI 和 CloudFormation 中）的设置。此设置提供以下选项：

**始终签署源请求（推荐设置）**  
我们建议使用此设置，此设置在控制台中名为**签署请求（推荐）**，在 API、CLI 和 CloudFormation 中名为 `always`。使用此设置，CloudFront 将始终签署自己发送到 Lambda 函数 URL 的所有请求。

**切勿签署源请求**  
此设置在控制台中名为 **Do not sign requests**（请勿签署请求），在 API、CLI 和 CloudFormation 中名为 `never`。使用此设置关闭所有使用此 OAC 的分配中所有源的 OAC。与从所有使用 OAC 的源和分配中逐一删除 OAC 相比，这可以节省时间和精力。使用此设置，CloudFront 不会签署自己发送到 Lambda 函数 URL 的任何请求。  
要使用此设置，Lambda 函数 URL 必须可供公开访问。如果您将此设置用于不可公开访问的 Lambda 函数 URL，则 CloudFront 无法访问该源。Lambda 函数 URL 源向 CloudFront 返回错误，而 CloudFront 会将这些错误传递给查看器。有关更多信息，请参阅**《AWS Lambda 用户指南》中的 [Lambda 函数 URL 的安全性和身份验证模型](https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html)。

**不要改写查看器（客户端）`Authorization` 标头**  
此设置在控制台中名为 **Do not override authorization header**（请勿改写授权标头），在 API、CLI 和 CloudFormation 中名为 `no-override`。如果您希望 CloudFront 仅在相应的查看器请求不包含 `Authorization` 标头时签署源请求，请使用此设置。使用此设置，CloudFront 会在查看器请求中存在 `Authorization` 标头时传递该标头，但在查看器请求不包含 `Authorization` 标头时对源请求进行签名（添加自己的 `Authorization` 标头）。  
+ 如果您使用此设置，则必须为 Lambda 函数 URL 指定签名版本 4 签名，而不是您的 CloudFront 分配的名称或 CNAME。当 CloudFront 将查看器请求中的 `Authorization` 标头转发到 Lambda 函数 URL 时，Lambda 会针对 Lambda URL 域的主机验证签名。如果签名不是基于 Lambda URL 域，签名中的主机将与 Lambda URL 源使用的主机不匹配。这意味着请求将失败，从而导致签名验证错误。
+ 要传递查看器请求的 `Authorization` 标头，您*必须* 针对所有使用与此源访问控制关联的 Lambda 函数 URL 的缓存行为，将 `Authorization` 标头添加到[缓存策略](controlling-the-cache-key.md)中。

## 示例模板代码
<a name="example-template-code-lambda-oac"></a>

如果您的 CloudFront 源是与 OAC 关联的 Lambda 函数 URL，则可以使用以下 Python 脚本，通过 `POST` 方法将文件上传到 Lambda 函数。

此代码假设您对 OAC 进行了配置，将默认签名行为设置为**始终签署源请求**，并且没有选择**请勿覆盖授权标头**设置。

通过此配置，OAC 可以使用 Lambda 主机名正确管理 Lambda 的 SigV4 授权。有效载荷使用来自 IAM 身份的 SigV4 进行签名，该身份已获得 Lambda 函数 URL 的授权，指定为 `IAM_AUTH` 类型。

模板针对来自客户端的 `POST` 请求，演示如何处理 x-amz-content-sha256 标头中已签名的有效载荷哈希值。具体而言，此模板设计用于管理表单数据有效载荷。该模板可以实现通过 CloudFront 将文件安全上传到 Lambda 函数 URL，并使用 AWS 身份验证机制确保只有经过授权的请求才能访问 Lambda 函数。

**代码包括以下功能：**  
满足在 x-amz-content-sha256 标头中包含有效载荷哈希的要求
使用 SigV4 身份验证安全地访问 AWS 服务
支持使用分段表单数据来上传文件
包括请求异常的错误处理

```
import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
import requests
import hashlib
import os


def calculate_body_hash(body):
    return hashlib.sha256(body).hexdigest()


def sign_request(request, credentials, region, service):
    sigv4 = SigV4Auth(credentials, service, region)
    sigv4.add_auth(request)


def upload_file_to_lambda(cloudfront_url, file_path, region):
    # AWS credentials
    session = boto3.Session()
    credentials = session.get_credentials()

    # Prepare the multipart form-data
    boundary = "------------------------boundary"

    # Read file content
    with open(file_path, 'rb') as file:
        file_content = file.read()

    # Get the filename from the path
    filename = os.path.basename(file_path)

    # Prepare the multipart body
    body = (
        f'--{boundary}\r\n'
        f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n'
        f'Content-Type: application/octet-stream\r\n\r\n'
    ).encode('utf-8')
    body += file_content
    body += f'\r\n--{boundary}--\r\n'.encode('utf-8')

    # Calculate SHA256 hash of the entire body
    body_hash = calculate_body_hash(body)

    # Prepare headers
    headers = {
        'Content-Type': f'multipart/form-data; boundary={boundary}',
        'x-amz-content-sha256': body_hash
    }

    # Create the request
    request = AWSRequest(
        method='POST',
        url=cloudfront_url,
        data=body,
        headers=headers
    )

    # Sign the request
    sign_request(request, credentials, region, 'lambda')

    # Get the signed headers
    signed_headers = dict(request.headers)

    # Print request headers before sending
    print("Request Headers:")
    for header, value in signed_headers.items():
        print(f"{header}: {value}")

    try:
        # Send POST request with signed headers
        response = requests.post(
            cloudfront_url,
            data=body,
            headers=signed_headers
        )

        # Print response status and content
        print(f"\nStatus code: {response.status_code}")
        print("Response:", response.text)

        # Print response headers
        print("\nResponse Headers:")
        for header, value in response.headers.items():
            print(f"{header}: {value}")

    except requests.exceptions.RequestException as e:
        print(f"An error occurred: {e}")


# Usage
cloudfront_url = "https://d111111abcdef8.cloudfront.net"
file_path = r"filepath"
region = "us-east-1"  # example: "us-west-2"

upload_file_to_lambda(cloudfront_url, file_path, region)
```

# 限制对 Amazon S3 源的访问
<a name="private-content-restricting-access-to-s3"></a>

CloudFront 提供两种向 Amazon S3 源发送经身份验证的请求的方式：*源访问控制（OAC）*和*源访问身份* (OAI)。OAC 有助于您保护源（如 Amazon S3）。

我们*建议*您改用 OAC，因为它支持以下功能：
+ AWS 区域 中的所有 Amazon S3 存储桶，包括 2022 年 12 月之后推出的选择加入区域
+ Amazon S3 [使用 AWS KMS 的服务器端加密](https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html) (SSE-KMS)
+ 对 Amazon S3 的动态请求（`PUT` 和 `DELETE`）

OAI 不支持这些功能，或者在这些情况下需要额外的解决方法。如果您已经在使用 OAI 并想要迁移，请参阅[从源访问身份 (OAI) 迁移到源访问控制（OAC）](#migrate-from-oai-to-oac)。

**注意**  
当您将 CloudFront OAC 与 Amazon S3 存储桶来源一起使用时，必须将 **Amazon S3 对象所有权**设置为**强制存储桶拥有者**，这是新 Amazon S3 存储桶的默认值。如果您需要 ACL，请使用**存储桶拥有者优先**设置来控制通过 CloudFront 上传的对象。
如果您的源是配置为[网站端点](https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteEndpoints.html)的 Amazon S3 存储桶，则必须使用 CloudFront 将其设置为自定义源。这意味着您无法使用 OAC（或 OAI）。OAC 不支持使用 Lambda@Edge 进行源重定向。

以下主题介绍了如何将 OAC 与 Amazon S3 源配合使用。

**主题**
+ [创建新的源访问控制](#create-oac-overview-s3)
+ [删除将其 OAC 附加到 S3 存储桶的分配](#delete-oac-distribution-s3)
+ [从源访问身份 (OAI) 迁移到源访问控制（OAC）](#migrate-from-oai-to-oac)
+ [源访问控制的高级设置](#oac-advanced-settings-s3)

## 创建新的源访问控制
<a name="create-oac-overview-s3"></a>

完成以下主题中描述的步骤，在 CloudFront 中设置新的源访问控制。

**Topics**
+ [先决条件](#oac-prerequisites-s3)
+ [向 CloudFront 授予访问 S3 存储桶的权限](#oac-permission-to-access-s3)
+ [创建源访问控制](#create-oac-s3)

### 先决条件
<a name="oac-prerequisites-s3"></a>

在创建和设置源访问控制（OAC）之前，您必须拥有带有 Amazon S3 存储桶源的 CloudFront 分配。此源必须是常规 S3 存储桶，而不是配置为[网站端点](https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteEndpoints.html)的存储桶。有关设置 CloudFront 分配与 S3 存储桶源配合使用的更多信息，请参阅[开始使用 CloudFront 标准分配](GettingStarted.SimpleDistribution.md)。

**重要**  
当您使用 OAC 保护 Amazon S3 源时，CloudFront 和 Amazon S3 之间的通信*始终* 通过 HTTPS 进行，但前提是您选择*始终签署请求*。您必须在控制台中选择**签署请求（推荐）**，或者在 CloudFront API、AWS CLI 或 CloudFormation 中指定 `always`。  
如果您改为选择**请勿签署请求**或**请勿覆盖授权标头**选项，则 CloudFront 将使用您在以下策略中指定的连接协议：  
[查看器协议策略](using-https-viewers-to-cloudfront.md) 
[源协议策略](DownloadDistValuesOrigin.md#DownloadDistValuesOriginProtocolPolicy)（仅自定义源）
例如，如果您选择**请勿覆盖授权标头**，并希望在 CloudFront 和 Amazon S3 源之间使用 HTTPS，请为[查看器协议策略](using-https-viewers-to-cloudfront.md)使用**将 HTTP 重定向到 HTTPS** 或**仅 HTTPS**。

### 向 CloudFront 授予访问 S3 存储桶的权限
<a name="oac-permission-to-access-s3"></a>

在 CloudFront 分配中创建来源访问控制（OAC）或对其设置之前，请确保 CloudFront 具有访问 S3 存储桶源的权限。请在创建 CloudFront 分配后，但在分配配置中将 OAC 添加到 S3 源之前，执行此操作。

使用 S3 [存储桶策略](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html)来支持 CloudFront 服务主体 (`cloudfront.amazonaws.com`) 访问存储桶。使用策略中的 `Condition` 元素，仅在请求代表包含 S3 源的 CloudFront 分配时，才允许 CloudFront 访问存储桶。这是要向其添加 OAC 的具有 S3 源的分配。

有关添加或修改存储桶策略的信息，请参阅 *Amazon S3 用户指南* 中的[使用 Amazon S3 控制台添加存储桶策略](https://docs.aws.amazon.com/AmazonS3/latest/userguide/add-bucket-policy.html)。

以下是支持启用了 OAC 的 CloudFront 分配访问 S3 源的 S3 存储桶策略示例。

**Example 支持启用了 OAC 的 CloudFront 分配进行只读访问的 S3 存储桶策略**    
****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadOnly",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/<CloudFront distribution ID>"
        }
      }
    }
  ]
}
```

**Example 支持启用了 OAC 的 CloudFront 分配进行读写访问的 S3 存储桶策略**    
****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadWrite",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/CloudFront-distribution-ID>"
        }
      }
    }
  ]
}
```

#### SSE-KMS
<a name="oac-permissions-sse-kms"></a>

如果 S3 存储桶源中的对象是通过[具有 AWS Key Management Service 的服务器端加密（SSE-KMS）](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html)进行加密的，您必须确保 CloudFront 分配具有使用 AWS KMS 密钥的权限。要向 CloudFront 分配授予使用 KMS 密钥的权限，请向 [KMS 密钥策略](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html)中添加一条语句。有关如何修改密钥策略的信息，请参阅 *AWS Key Management Service 开发人员指南* 中的[更改密钥策略](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying.html)。

**Example KMS 密钥政策语句：**  
以下示例显示了一条 AWS KMS 策略语句，该语句支持具有 OAC 的 CloudFront 分配访问 KMS 密钥来实施 SSE-KMS。  

```
{
    "Sid": "AllowCloudFrontServicePrincipalSSE-KMS",
    "Effect": "Allow",
    "Principal": {
        "Service": [
            "cloudfront.amazonaws.com"
        ]
     },
    "Action": [
        "kms:Decrypt",
        "kms:Encrypt",
        "kms:GenerateDataKey*"
    ],
    "Resource": "*",
    "Condition": {
            "StringEquals": {
                "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/<CloudFront distribution ID>"
            }
        }
}
```

### 创建源访问控制
<a name="create-oac-s3"></a>

要创建源访问控制（OAC），可以使用 AWS 管理控制台、CloudFormation、AWS CLI 或 CloudFront API。

------
#### [ Console ]

**创建源访问控制**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择 **Origin access**（源访问）。

1. 选择 **Create control setting**（创建控制设置）。

1. 在 **Create control setting**（创建控制设置）表单上，执行以下操作：

   1. 在 **Details**（详细信息）窗格中，输入源访问控制的 **Name**（名称）和（可选）**Description**（描述）。

   1. 在 **Settings**（设置）中，建议保留默认设置 [**Sign requests (recommended)**（签署请求（推荐））]。有关更多信息，请参阅 [源访问控制的高级设置](#oac-advanced-settings-s3)。

1. 从 **Origin type**（源类型）下拉列表中选择 S3。

1. 选择**创建**。

   创建 OAC 后，记下 **Name**（名称）。在以下过程中，您需要此名称。

**向分配中的 S3 源添加源访问控制**

1. 通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择具有要向其添加 OAC 的 S3 源的分配，然后选择 **Origins**（源）选项卡。

1. 选择要向其添加 OAC 的 S3 源，然后选择**编辑**。

1. 在**源访问**部分，选择**源访问控制设置（推荐）**。

1. 从**源访问控制**下拉菜单中，选择要使用的 OAC。

1. 选择**保存更改**。

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署其发送到 S3 存储桶源的所有请求。

------
#### [ CloudFormation ]

要使用 CloudFormation 创建源访问控制（OAC），请使用 `AWS::CloudFront::OriginAccessControl` 资源类型。以下示例显示了 YAML 格式的 CloudFormation 模板语法，用于创建源访问控制。

```
Type: AWS::CloudFront::OriginAccessControl
Properties: 
  OriginAccessControlConfig: 
      Description: An optional description for the origin access control
      Name: ExampleOAC
      OriginAccessControlOriginType: s3
      SigningBehavior: always
      SigningProtocol: sigv4
```

有关更多信息，请参阅 *AWS CloudFormation 用户指南* 中的 [AWS::CloudFront::OriginAccessControl](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-originaccesscontrol.html)。

------
#### [ CLI ]

要使用 AWS Command Line Interface (AWS CLI) 创建源访问控制，请使用 **aws cloudfront create-origin-access-control** 命令。您可以使用输入文件来提供命令的输入参数，而不是将每个单独的参数指定为命令行输入。

**创建源访问控制（带输入文件的 CLI）**

1. 使用以下命令创建名为 `origin-access-control.yaml` 的文件。此文件包含 **create-origin-access-control** 命令的所有输入参数。

   ```
   aws cloudfront create-origin-access-control --generate-cli-skeleton yaml-input > origin-access-control.yaml
   ```

1. 打开刚创建的 `origin-access-control.yaml` 文件。编辑文件以添加 OAC 的名称、描述（可选），然后将 `SigningBehavior` 更改为 `always`。然后保存文件。

   有关其他 OAC 设置的信息，请参阅[源访问控制的高级设置](#oac-advanced-settings-s3)。

1. 使用以下命令通过 `origin-access-control.yaml` 文件中的输入参数创建源访问控制。

   ```
   aws cloudfront create-origin-access-control --cli-input-yaml file://origin-access-control.yaml
   ```

   记下命令输出中的 `Id` 值。您需要使用它将 OAC 添加到 CloudFront 分配中的 S3 存储桶源。

**将 OAC 附加到现有分配中的 S3 存储桶源（带输入文件的 CLI）**

1. 使用以下命令保存要向其添加 OAC 的 CloudFront 分配的分配配置。该分配必须有 S3 存储桶源。

   ```
   aws cloudfront get-distribution-config --id <CloudFront distribution ID> --output yaml > dist-config.yaml
   ```

1. 打开刚创建的名为 `dist-config.yaml` 的文件。编辑文件，进行以下更改：
   + 在 `Origins` 对象中，将 OAC 的 ID 添加到名为 `OriginAccessControlId` 的字段中。
   + 从名为 `OriginAccessIdentity` 的字段中移除值（如果存在）。
   + 将 `ETag` 字段重命名为 `IfMatch`，但不更改字段的值。

   完成后保存该文件。

1. 使用以下命令更新分配以使用源访问控制。

   ```
   aws cloudfront update-distribution --id <CloudFront distribution ID> --cli-input-yaml file://dist-config.yaml
   ```

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时，它会签署其发送到 S3 存储桶源的所有请求。

------
#### [ API ]

要使用 CloudFront API 创建源访问控制，请使用 [CreateOriginAccessControl](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateOriginAccessControl.html)。有关您在此 API 调用中指定的字段的更多信息，请参阅 AWS 开发工具包或其他 API 客户端的 API 参考文档。

创建源访问控制后，可以使用下面的任何一个 API 调用将其附加到分配中的 S3 存储桶源：
+ 要将它附加到现有分配，请使用 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)。
+ 要将它附加到新分配，请使用 [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html)。

对于这两个 API 调用，请在源内的 `OriginAccessControlId` 字段中提供源访问控制 ID。有关您在这些 API 调用中指定的其他字段的更多信息，请参阅 [所有分配设置参考](distribution-web-values-specify.md) 以及有关 AWS SDK 或其他 API 客户端的 API 参考文档。

------

## 删除将其 OAC 附加到 S3 存储桶的分配
<a name="delete-oac-distribution-s3"></a>

如果您需要删除将其 OAC 附加到 S3 存储桶的分配，则应首先删除该分配，然后再删除 S3 存储桶源。或者，在源域名中包含该区域。如果无法做到这一点，则可以在删除之前切换到公共模式以便从分配中删除 OAC。有关更多信息，请参阅 [删除分配](HowToDeleteDistribution.md)。

## 从源访问身份 (OAI) 迁移到源访问控制（OAC）
<a name="migrate-from-oai-to-oac"></a>

要从旧式来源访问身份（OAI）迁移到来源访问控制（OAC），请先更新 S3 存储桶源，以支持 OAI 和启用了 OAC 的分配访问存储桶的内容。这可确保 CloudFront 在过渡期间不会丢失对存储桶的访问权限。要支持 OAI 和启用了 OAC 的分配访问 S3 存储桶，请更新[存储桶策略](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html)来包括两条语句，每种主体对应于一条语句。

以下示例 S3 存储桶策略支持 OAI 和启用了 OAC 的分配访问 S3 源。

**Example 支持 OAI 和启用了 OAC 的 CloudFront 分配进行只读访问的 S3 存储桶策略**    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipalReadOnly",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<S3 bucket name>/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/<CloudFront distribution ID>"
                }
            }
        },
        {
            "Sid": "AllowLegacyOAIReadOnly",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<S3 bucket name>/*"
        }
    ]
}
```

在更新 S3 源的存储桶策略以允许访问 OAI 和 OAC 之后，您可以更新分配配置以使用 OAC（而不是 OAI）。有关更多信息，请参阅 [创建新的源访问控制](#create-oac-overview-s3)。

完全部署分配后，您可以删除存储桶策略中允许访问 OAI 的语句。有关更多信息，请参阅 [向 CloudFront 授予访问 S3 存储桶的权限](#oac-permission-to-access-s3)。

## 源访问控制的高级设置
<a name="oac-advanced-settings-s3"></a>

CloudFront 源访问控制特征包括仅适用于特定使用案例的高级设置。除非您特别需要高级设置，否则请使用推荐的设置。

源访问控制包含一个名为 **Signing behavior**（签名行为）（在控制台中）或 `SigningBehavior`（在 API、CLI 和 CloudFormation）的设置。此设置提供以下选项：

**始终签署源请求（推荐设置）**  
我们建议使用此设置，此设置在控制台中名为 **Sign requests (recommended)** [签署请求（推荐）]，在 API、CLI 和 CloudFormation 中名为 `always`。使用此设置，CloudFront 将始终签署其发送到 S3 存储桶源的所有请求。

**切勿签署源请求**  
此设置在控制台中名为 **Do not sign requests**（请勿签署请求），在 API、CLI 和 CloudFormation 中名为 `never`。使用此设置关闭所有使用此源访问控制的分配中所有源的源访问控制。与从所有使用源访问控制的源和分配中逐一删除源访问控制相比，这可以节省时间和精力。使用此设置，CloudFront 不会签署其发送到 S3 存储桶源的任何请求。  
要使用此设置，S3 存储桶源必须可公开访问。如果您将此设置用于不可公开访问的 S3 存储桶源，则 CloudFront 无法访问该源。S3 存储桶源向 CloudFront 返回错误，而 CloudFront 会将这些错误传递给查看器。

**不要改写查看器（客户端）`Authorization` 标头**  
此设置在控制台中名为 **Do not override authorization header**（请勿改写授权标头），在 API、CLI 和 CloudFormation 中名为 `no-override`。如果您希望 CloudFront 仅在相应的查看器请求不包含 `Authorization` 标头时签署源请求，请使用此设置。使用此设置，CloudFront 会在查看器请求中存在 `Authorization` 标头时传递该标头，但在查看器请求不包含 `Authorization` 标头时对源请求进行签名（添加自己的 `Authorization` 标头）。  
要传递查看器请求的 `Authorization` 标头，您*必须* 针对所有使用与此源访问控制关联的 S3 存储桶源的缓存行为将 `Authorization` 标头添加到[缓存策略](controlling-the-cache-key.md)中。

## 使用源访问身份（旧版，不推荐）
<a name="private-content-restricting-access-to-s3-oai"></a>

### 源访问身份概述
<a name="private-content-restricting-access-to-s3-overview"></a>

CloudFront *源访问身份* (OAI) 提供与*源访问控制（OAC）*类似的功能，但它并不适用于所有场景。具体而言，OAI 不支持：
+ 所有 AWS 区域中的 Amazon S3 存储桶，包括可选择加入的区域
+ Amazon S3 [使用 AWS KMS 的服务器端加密](https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html) (SSE-KMS)
+ 对 Amazon S3 的动态请求（`PUT`、`POST` 或 `DELETE`）
+ 2023 年 1 月之后推出的新 AWS 区域

**提示**  
我们建议您改用 OAC。要设置 OAC，请参阅[创建新的源访问控制](#create-oac-overview-s3)。有关如何从 OAI 迁移到 OAC 的信息，请参阅[从源访问身份 (OAI) 迁移到源访问控制（OAC）](#migrate-from-oai-to-oac)。

### 授予源访问身份读取 Amazon S3 存储桶中文件的权限
<a name="private-content-granting-permissions-to-oai"></a>

当您使用 CloudFront 控制台创建 OAI 或将 OAI 添加到分配时，可以自动更新 Amazon S3 存储桶策略以向 OAI 提供访问存储桶的权限。您也可以选择手动创建或更新存储桶策略。无论使用哪种方法，您仍应查看权限，从而确保：
+ 您的 CloudFront OAI 可以代表通过 CloudFront 发出请求的查看器来访问存储桶中的文件。
+ 查看器不能使用 Amazon S3 URL 访问位于 CloudFront 外部的文件。

**重要**  
如果您将 CloudFront 配置为接受并转发 CloudFront 支持的所有 HTTP 方法，请确保为您的 CloudFront OAI 授予所需的权限。例如，假设您将 CloudFront 配置为接受和转发使用 `DELETE` 方法的请求，则配置您的存储桶策略以适当地处理 `DELETE` 请求，从而确保查看器只能删除您希望它们删除的文件。

#### 使用 Amazon S3 存储桶策略
<a name="private-content-updating-s3-bucket-policies"></a>

您可以通过以下方式创建或更新 Amazon S3 存储桶策略，向 CloudFront OAI 授予对该存储桶中文件的访问权限：
+ 使用 [Amazon S3 控制台](https://console.aws.amazon.com/s3/home)中 Amazon S3 存储桶的 **Permissions**（权限）选项卡。
+ 使用 Amazon S3 API 中的 [PutBucketPolicy](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketPolicy.html)。
+ 使用 [CloudFront 控制台](https://console.aws.amazon.com/cloudfront/v4/home)。在 CloudFront 控制台中向源设置添加 OAI 时，您可以选择 **Yes, update the bucket policy**（是，更新存储桶策略），从而让 CloudFront 代表您更新存储桶策略。

如果您手动更新存储桶策略，请务必：
+ 在策略中指定正确的 OAI 为 `Principal`。
+ 授予 OAI 所需的权限以便代表查看器访问对象。

有关更多信息，请参阅以下部分。

##### 在存储桶策略中将 OAI 指定为 `Principal`
<a name="private-content-updating-s3-bucket-policies-principal"></a>

要在 Amazon S3 存储桶策略中将 OAI 指定为 `Principal`，请使用 OAI 的 Amazon 资源名称 (ARN)，其中包括 OAI 的 ID。例如：

```
"Principal": {
    "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
}
```

在 CloudFront 控制台的**安全**、**源访问**、**身份（遗留）**下找到 OAI ID。或者，在 CloudFront API 中使用 [ListCloudFrontOriginAccessIdentities](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListCloudFrontOriginAccessIdentities.html)。

##### 向 OAI 授予权限
<a name="private-content-updating-s3-bucket-policies-permissions"></a>

要向 OAI 授予对 Amazon S3 存储桶中对象的访问权限，请使用策略中与特定 Amazon S3 API 操作相关的操作。例如，`s3:GetObject` 操作允许 OAI 读取存储桶中的对象。有关更多信息，请参阅下一部分的示例，或者参阅《Amazon Simple Storage Service 用户指南》**中的 [Amazon S3 操作](https://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html)。

##### Amazon S3 存储桶策略示例
<a name="private-content-updating-s3-bucket-policies-examples"></a>

以下示例显示了允许 CloudFront OAI 访问 S3 存储桶的 Amazon S3 存储桶策略。

在 CloudFront 控制台的**安全**、**源访问**、**身份（遗留）**下找到 OAI ID。或者，在 CloudFront API 中使用 [ListCloudFrontOriginAccessIdentities](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListCloudFrontOriginAccessIdentities.html)。

**Example 向 OAI 授予读取访问权限的 Amazon S3 存储桶策略**  
下面的示例允许 OAI 读取指定存储桶 (`s3:GetObject`) 中的对象。    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<S3 bucket name>/*"
        }
    ]
}
```

**Example 向 OAI 授予读写访问权限的 Amazon S3 存储桶策略**  
下面的示例允许 OAI 读取和写入指定存储桶（`s3:GetObject` 和 `s3:PutObject`）中的对象。这允许查看器通过 CloudFront 将文件上传到您的 Amazon S3 存储桶。    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::<S3 bucket name>/*"
        }
    ]
}
```

#### 使用 Amazon S3 对象 ACL（不建议）
<a name="private-content-updating-s3-acls"></a>

**重要**  
我们建议[使用 Amazon S3 存储桶策略](#private-content-updating-s3-bucket-policies)向 OAI 提供对 S3 存储桶的访问权限。您也可以按照这一部分的描述使用访问控制列表 (ACL)，但我们不建议您这样做。  
Amazon S3 建议将 [bS3 Object Ownership](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)（S3 对象所有权）设置为 **bucket owner enforced**（强制执行存储桶拥有者），这意味着存储桶以及其中的对象禁用 ACL。当您应用此对象所有权设置后，必须使用存储桶策略向 OAI 授予访问权限（请参阅上一部分）。  
以下部分仅适用于需要 ACL 的传统使用案例。

您可以通过以下方式创建或更新文件的 ACL，向 CloudFront OAI 授予对 Amazon S3 存储桶中文件的访问权限：
+ 使用 [Amazon S3 控制台](https://console.aws.amazon.com/s3/home)中 Amazon S3 对象的 **Permissions**（权限）选项卡。
+ 使用 Amazon S3 API 中的 [PutObjectAcl](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectAcl.html)。

使用 ACL 向 OAI 授予访问权限时，您必须使用其 Amazon S3 规范用户 ID 来指定 OAI。在 CloudFront 控制台中，您可以在**安全**、**源访问**、**身份（遗留）**下找到此 ID。如果使用 CloudFront API，请使用您在创建 OAI 时返回的 `S3CanonicalUserId` 元素值，或者调用 CloudFront API 中的 [ListCloudFrontOriginAccessIdentities](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListCloudFrontOriginAccessIdentities.html)。

### 在仅支持签名版本 4 身份验证的 Amazon S3 区域中使用源访问身份
<a name="private-content-origin-access-identity-signature-version-4"></a>

较新的 Amazon S3 区域要求对通过身份验证的请求使用签名版本 4。（有关各个 Amazon S3 区域支持的签名版本，请参阅《AWS 一般参考》**中的 [Amazon Simple Storage Service 端点和限额](https://docs.aws.amazon.com/general/latest/gr/s3.html)。) 如果您使用源访问身份并且存储桶位于要求使用签名版本 4 的区域之一，请注意以下几点：
+ 支持 `DELETE`、`GET`、`HEAD`、`OPTIONS` 和 `PATCH` 请求，无限定条件。
+ `POST`不支持 请求。

# 通过 VPC 源限制访问
<a name="private-content-vpc-origins"></a>

您可以使用 CloudFront，从托管在您的虚拟私有云（VPC）私有子网中的应用程序提供内容。您可以使用私有子网中的应用程序负载均衡器（ALB）、网络负载均衡器（NLB）和 EC2 实例作为 VPC 源。

以下介绍您可能需要使用 VPC 源的一些原因：
+ **安全性** – VPC 源设计用于提升应用程序的安全状况，此方法将您的负载均衡器和 EC2 实例置于私有子网中，从而使 CloudFront 成为单一入口点。用户请求通过私有、安全的连接从 CloudFront 发送到 VPC 源，为您的应用程序增加额外的安全性。
+ **管理** – VPC 源减少了在 CloudFront 与源之间建立安全连接所需的运营开销。您可以将源迁移到不具有公共访问权限的私有子网，而且您不必实施访问控制列表（ACL）或其他机制来限制对源的访问。这样，您无需投资于重复性的开发工作，即可使用 CloudFront 来保障 Web 应用程序的安全。
+ **可扩展性和性能**：VPC 源有助于您保护 Web 应用程序，将时间专注于发展业务关键型应用程序，同时使用 CloudFront 提高安全性并保持高性能和全球可扩展性。VPC 源可简化安全管理并降低操作复杂性，这样您就可以将 CloudFront 用作应用程序的单一入口点。

**提示**  
CloudFront 支持跨 AWS 账户共享 VPC 源，无论它们是否在您的组织中。您可以通过 CloudFront 控制台或 AWS Resource Access Manager（AWS RAM）共享 VPC 源。有关更多信息，请参阅 [在 CloudFront 中使用共享的资源](sharing-resources.md)。

## 先决条件
<a name="vpc-origin-prerequisites"></a>

在为 CloudFront 分配创建 VPC 源之前，您必须完成以下操作：

### VPC 配置
<a name="vpc-configuration"></a>

在 VPC 源支持的其中一个 AWS 区域中，**在 Amazon VPC 上创建虚拟私有云（VPC）**。有关创建 VPC 的信息，请参阅《Amazon VPC 用户指南》**中的[创建 VPC 以及其他 VPC 资源](https://docs.aws.amazon.com/vpc/latest/userguide/create-vpc.html#create-vpc-and-other-resources)。有关受支持区域的列表，请参阅[支持 VPC 源的 AWS 区域](#vpc-origins-supported-regions)。

VPC 必须包括以下各项：
+ **互联网网关**：您需要向具有 VPC 源资源的 VPC 添加互联网网关。互联网网关是必需的，以指明 VPC 可以接收来自互联网的流量。互联网网关并不用于将流量路由到子网内的源，您也不需要更新路由策略。
+ **私有子网，有至少一个可用的 IPv4 地址**：CloudFront 使用服务托管式弹性网络接口（ENI）路由到您的子网，CloudFront 在您使用 CloudFront 定义 VPC 源资源后创建该接口。您的私有子网中必须至少有一个可用的 IPv4 地址，这样才能成功完成 ENI 的创建过程。IPv4 地址可以是私有的，无需支付额外费用。不支持仅 IPv6 子网。

### 源资源
<a name="origin-resources"></a>

在私有子网中，启动应用程序负载均衡器、网络负载均衡器或 EC2 实例用作源。您启动的资源必须完全部署并处于活动状态，然后才能将其用作 VPC 源。

**源限制：**
+ 网关负载均衡器不能添加为源
+ 双栈网络负载均衡器不能添加为源
+ 带有 TLS 侦听器的网络负载均衡器不能添加为源
+ 要用作 VPC 源，网络负载均衡器必须附加了一个安全组

### 安全组配置
<a name="security-group-configuration"></a>

您的 VPC 源资源（应用程序负载均衡器、网络负载均衡器或 EC2 实例）必须附加了一个安全组。当您创建 VPC 源时，CloudFront 会自动创建一个采用命名模式 `CloudFront-VPCOrigins-Service-SG` 的服务管理的安全组。此安全组完全由 AWS 管理，不应编辑。

要支持来自 CloudFront 的流量到达您的 VPC 源，请使用以下方法之一更新附加到源资源（ALB、NLB 或 EC2 实例）的安全组以支持入站流量：
+ **选项 1：**支持来自 CloudFront 托管式前缀列表的流量。有关更多信息，请参阅 [使用 CloudFront 托管前缀列表](LocationsOfEdgeServers.md#managed-prefix-list)。这也可以在创建 VPC 源之前完成。
+ **选项 2：**支持来自 CloudFront 服务托管式安全组 (`CloudFront-VPCOrigins-Service-SG`) 的流量。只有在创建 VPC 源并创建服务托管式安全组之后，才能执行此操作。此配置更具限制性，因为它将流量限制为仅限您的 CloudFront 分配。

**重要**  
请勿使用以 `CloudFront-VPCOrigins-Service-SG` 开头的名称创建您自己的安全组。这是服务托管式安全组的 AWS 保留命名模式。有关更多信息，请参阅[创建安全组](https://docs.aws.amazon.com/vpc/latest/userguide/creating-security-groups.html)。

### 协议和功能限制
<a name="protocol-feature-restrictions"></a>

VPC 源不支持以下各项：
+ WebSocket
+ gRPC 流量
+ 使用 Lambda@Edge 的源请求和源响应触发器

## 创建 VPC 源（新分配）
<a name="new-vpc-origin"></a>

以下过程演示如何在 CloudFront 控制台中，为新 CloudFront 分配创建 VPC 源。或者，您可以将 [CreateVpcOrigin](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateVpcOrigin.html) 和 [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html) API 操作与 AWS CLI 或 AWS SDK 一起使用。

**为新 CloudFront 分配创建 VPC 源**

1. 通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择 **VPC 源**、**创建 VPC 源**。

1. 填写必填字段。在**源 ARN** 中，选择应用程序负载均衡器、网络负载均衡器或 EC2 实例的 ARN。如果未显示 ARN，可以复制特定资源 ARN，然后将其粘贴到此处。

1. 选择**创建 VPC 源**。

1. 等待 VPC 源状态更改为**已部署**。这一过程耗时最多 15 分钟。

1. 选择**分配**、**创建分配**。

1. 对于**源域**，从下拉列表中选择您的 VPC 源资源。

   如果您的 VPC 源是 EC2 实例，请复制实例的**私有 IP DNS 名称**并粘贴到**源域**字段中。

1. 完成分配的创建。有关更多信息，请参阅 [在控制台中创建 CloudFront 分配](distribution-web-creating-console.md#create-console-distribution)。

## 创建 VPC 源（现有分配）
<a name="existing-vpc-origin"></a>

以下过程演示如何在 CloudFront 控制台中，为现有 CloudFront 分配创建 VPC 源，这有助于确保应用程序的持续可用性。或者，您可以将 [CreateVpcOrigin](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateVpcOrigin.html) 和 [UpdateDistributionWithStagingConfig](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistributionWithStagingConfig.html) API 操作与 AWS CLI 或 AWS SDK 一起使用。

（可选）您可以选择将 VPC 源添加到现有分配中，而不创建暂存分配。

**为现有 CloudFront 分配创建 VPC 源**

1. 通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 选择 **VPC 源**、**创建 VPC 源**。

1. 填写必填字段。在**源 ARN** 中，选择应用程序负载均衡器、网络负载均衡器或 EC2 实例的 ARN。如果未显示 ARN，可以复制特定资源 ARN，然后将其粘贴到此处。

1. 选择**创建 VPC 源**。

1. 等待 VPC 源状态更改为**已部署**。这一过程耗时最多 15 分钟。

1. 在导航窗格中，选择**分配**。

1. 选择分配的 ID。

1. 在**常规**选项卡的**持续部署**下，选择**创建暂存分配**。有关更多信息，请参阅 [使用 CloudFront 持续部署来安全地测试 CDN 配置更改](continuous-deployment.md)。

1. 按照**创建暂存分配**向导中的步骤创建暂存分配。其中包括以下步骤：
   + 对于**源**，选择**创建源**。
   + 对于**源域**，从下拉菜单中选择您的 VPC 源资源。

     如果您的 VPC 源是 EC2 实例，请复制实例的**私有 IP DNS 名称**并粘贴到**源域**字段中。
   + 选择**创建源**。

1. 在暂存分配中，测试 VPC 源。

1. 将暂存分配配置提升到主分配。有关更多信息，请参阅 [提升暂存分配的配置](working-with-staging-distribution-continuous-deployment-policy.md#promote-staging-distribution-configuration)。

1. 通过将子网设为私有子网，移除对 VPC 源的公开访问。执行此操作后，VPC 源将无法通过互联网发现，但是 CloudFront 仍可对其进行私有访问。有关更多信息，请参阅**《Amazon VPC 用户指南》中的[将子网与路由表关联或取消关联](https://docs.aws.amazon.com/vpc/latest/userguide/WorkWithRouteTables.html#AssociateSubnet)。

## 更新 VPC 源
<a name="update-vpc-origin"></a>

以下过程演示如何在 CloudFront 控制台中，更新 CloudFront 分配的 VPC 源。或者，您可以将 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html) 和 [UpdateVpcOrigin](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateVpcOrigin.html) API 操作与 AWS CLI 或 AWS SDK 一起使用。

**为 CloudFront 分配更新现有 VPC 源**

1. 通过 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 打开 CloudFront 控制台

1. 在导航窗格中，选择**分配**。

1. 选择分配的 ID。

1. 选择**行为**选项卡。

1. 确保 VPC 源不是缓存行为的默认源。

1. 选择**源**选项卡。

1. 选择要更新的 VPC 源，然后选择**删除**。这会取消 VPC 源与分配的关联。重复步骤 2-7，取消 VPC 源与任何其他分配的关联。

1. 选择 **VPC 源**。

1. 选择 VPC 源并选择**编辑**。

1. 进行相应更新并选择**更新 VPC 源**。

1. 等待 VPC 源状态更改为**已部署**。这一过程耗时最多 15 分钟。

1. 在导航窗格中，选择**分配**。

1. 选择分配的 ID。

1. 选择**源**选项卡。

1. 选择**创建源**。

1. 对于**源域**，从下拉菜单中选择您的 VPC 源资源。

   如果您的 VPC 源是 EC2 实例，请复制实例的**私有 IP DNS 名称**并粘贴到**源域**字段中。

1. 选择**创建源**。这会再次将 VPC 源与您的分配相关联。重复步骤 12-17，将更新后的 VPC 源与任意其他分配相关联。

## 支持 VPC 源的 AWS 区域
<a name="vpc-origins-supported-regions"></a>

目前，以下商用 AWS 区域中支持 VPC 源。可用区（AZ）例外情况已注明。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/private-content-vpc-origins.html)

# 限制访问应用程序负载均衡器
<a name="restrict-access-to-load-balancer"></a>

您可以在 Amazon CloudFront 上同时使用内部和面向互联网的应用程序负载均衡器。您可以通过使用 VPC 源在 CloudFront 的私有子网内使用内部应用程序负载均衡器。CloudFront VPC 源可让您提供来自私有 VPC 子网中托管的应用程序的内容，而无需将其暴露于公共互联网上。有关更多信息，请参阅 [通过 VPC 源限制访问](private-content-vpc-origins.md)。

如果您在 CloudFront 中使用面向互联网的应用程序负载均衡器，则可以使用以下安全缓解措施来防止用户直接访问应用程序负载均衡器，并仅支持通过 CloudFront 进行访问。

1. 将 CloudFront 配置为将自定义 HTTP 标头添加到向应用程序负载均衡器发送的请求中。

1. 将应用程序负载均衡器配置为仅转发包含自定义 HTTP 标头的请求。

1. 需要 HTTPS 来提高此解决方案的安全性。

CloudFront 还可以帮助减少延迟，甚至吸收一些分布式拒绝服务 (DDoS) 攻击。

如果您的使用案例需要直接通过互联网从 CloudFront 和应用程序负载均衡器双重访问 Web 应用程序，请考虑按如下方式拆分 Web 应用程序 API：
+ 必须通过 CloudFront 访问的 API。在这种情况下，考虑使用单独的私有应用程序负载均衡器作为源。
+ 需要通过应用程序负载均衡器访问的 API。在这种情况下，可以绕过 CloudFront。

或者，对于 Web 应用程序或由弹性负载均衡中面向互联网的应用程序负载均衡器提供的其他内容，CloudFront 可以缓存对象并将它们直接提供给用户（查看器），从而减轻应用程序负载均衡器的负载。面向互联网的负载均衡器具有可公开解析的 DNS 名称，并通过互联网将来自客户端的请求路由到目标。

有关更多信息，请参阅以下主题。完成这些步骤后，用户只能通过 CloudFront 访问您的应用程序负载均衡器。

**Topics**
+ [配置 CloudFront 以便向请求添加自定义 HTTP 标头](#restrict-alb-add-custom-header)
+ [将应用程序负载均衡器配置为仅转发包含特定标头的请求](#restrict-alb-route-based-on-header)
+ [（可选）提高此解决方案的安全性](#restrict-alb-improve-security)
+ [（可选）使用 CloudFront 的 AWS 托管前缀列表限制对源的访问](#limit-access-to-origin-using-aws-managed-prefixes)

## 配置 CloudFront 以便向请求添加自定义 HTTP 标头
<a name="restrict-alb-add-custom-header"></a>

您可以将 CloudFront 配置为将自定义 HTTP 标头添加到向您的源（在本例中为应用程序负载均衡器）发送的请求中。

**重要**  
此使用案例依赖于对自定义标头名称和值保密。如果标头名称和值没有保密，其他 HTTP 客户端可能会将它们包含在直接发送到应用程序负载均衡器的请求中。这可能会导致应用程序负载均衡器的行为看起来就好像请求来自 CloudFront，但实际上请求并非来自 CloudFront。为防止这种情况，请将自定义标头名称和值保密。

您可以使用 CloudFront 控制台、CloudFormation 或 CloudFront API，配置 CloudFront 来向源请求添加自定义 HTTP 标头。

**要添加自定义 HTTP 标头（CloudFront 控制台）**  
在 CloudFront 控制台中，使用**源设置**中的**源自定义标题**设置。输入**标头名称**及其**值**。  
在生产中，使用随机生成的标头名称和值。将标头名称和值视为安全凭证，如用户名和密码。
当您为现有的 CloudFront 分配创建或编辑源时，以及当您创建新分配时，您可以编辑**源自定义标头**设置。有关更多信息，请参阅[更新分配](HowToUpdateDistribution.md)和[创建分配](distribution-web-creating-console.md)。

**要添加自定义 HTTP 标头 (CloudFormation)**  
在 CloudFormation 模板中，使用 `OriginCustomHeaders` 属性，如以下示例所示。  
本示例中的标头名称和值仅用于演示。在生产中，使用随机生成的值。将标题名称和值视为安全凭证，如用户名和密码。

```
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  TestDistribution:
    Type: 'AWS::CloudFront::Distribution'
    Properties:
      DistributionConfig:
        Origins:
          - DomainName: app-load-balancer.example.com
            Id: Example-ALB
            CustomOriginConfig:
              OriginProtocolPolicy: https-only
              OriginSSLProtocols:
                - TLSv1.2
            OriginCustomHeaders:
               - HeaderName: X-Custom-Header
                 HeaderValue: random-value-1234567890
        Enabled: 'true'
        DefaultCacheBehavior:
          TargetOriginId: Example-ALB
          ViewerProtocolPolicy: allow-all
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
        PriceClass: PriceClass_All
        ViewerCertificate:
          CloudFrontDefaultCertificate: 'true'
```
有关更多信息，请参阅《AWS CloudFormation 用户指南》**中的 [Origin](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origin.html) 和 [OriginCustomHeader](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origincustomheader.html) 属性。

**添加自定义 HTTP 标头（CloudFront API）**  
在 CloudFront API 中，使用 `CustomHeaders` 中的 `Origin` 对象。有关更多信息，请参阅《Amazon CloudFront API 参考》**中的 [CreateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateDistribution.html) 和 [UpdateDistribution](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html)，以及有关您的 SDK 或其他 API 客户端的文档。

有些标头名称不能指定为源自定义标头。有关更多信息，请参阅 [CloudFront 无法添加到源请求的自定义标头](add-origin-custom-headers.md#add-origin-custom-headers-denylist)。

## 将应用程序负载均衡器配置为仅转发包含特定标头的请求
<a name="restrict-alb-route-based-on-header"></a>

将 CloudFront 配置为将自定义 HTTP 标头添加到向应用程序负载均衡器发送的请求中后（请参阅[上一部分](#restrict-alb-add-custom-header)），您可以将负载均衡器配置为仅转发包含此自定义标头的请求。您可以通过添加新规则并在负载均衡器的侦听器中修改默认规则来完成此操作。

**先决条件**  
要使用以下过程，您需要至少具有一个侦听器的应用程序负载均衡器。如果您尚未创建应用程序负载均衡器，请参阅《应用程序负载均衡器用户指南》**中的[创建应用程序负载均衡器](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-application-load-balancer.html)。

以下过程将修改 HTTPS 侦听器。您可以使用相同的过程来修改 HTTP 侦听器。

**在应用程序负载均衡器侦听器中更新规则**

1. 添加新规则。按照[添加规则](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-update-rules.html#add-rule)中的说明进行以下修改：
   + 将规则添加到作为 CloudFront 分配源的负载均衡器。
   + 对于**添加条件**，选择 **HTTP 标头**。在 CloudFront 中指定作为源自定义标头添加的 HTTP 标头名称和值。
   + 对于**添加操作**，选择**转发至**。选择要转发请求的目标组。

1. 在负载均衡器的侦听器中编辑默认规则。按照[编辑规则](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-update-rules.html#edit-rule)中的说明进行以下修改：
   + 编辑作为 CloudFront 分配源的负载均衡器的默认规则。
   + 删除默认操作，对于**添加操作**，选择**返回固定响应**。
   + 对于**响应代码**，输入 **403**。
   + 对于**响应正文**，输入 **Access denied**。

完成这些步骤后，负载均衡器侦听器会有两条规则。一条规则转发包含 HTTP 标头的请求（来自 CloudFront 的请求）。另一条规则向所有其他请求（并非来自 CloudFront 的请求）发送固定响应。

您可以通过向 CloudFront 分配发送请求和向应用程序负载均衡器发送一个请求来验证该解决方案是否有效。对 CloudFront 的请求将返回您的 Web 应用程序或内容，直接发送到应用程序负载均衡器的请求将返回`403`带有纯文本消息的响应`Access denied`。

## （可选）提高此解决方案的安全性
<a name="restrict-alb-improve-security"></a>

为了提高此解决方案的安全性，您可以将 CloudFront 分配配置为在向应用程序负载均衡器发送请求时始终使用 HTTPS。请记住，此解决方案仅在您对自定义标头名称和值保密时才有效。使用 HTTPS 可以帮助防止窃听者发现标头名称和值。我们还建议定期轮换标头名称和值。

**使用 HTTPS 进行源请求**  
要将 CloudFront 配置为使用 HTTPS 处理源请求，请将**源协议策略**设置设为**仅限 HTTPS**。此设置可在 CloudFront 控制台、CloudFormation 和 CloudFront API 中使用。有关更多信息，请参阅 [协议（仅自定义源）](DownloadDistValuesOrigin.md#DownloadDistValuesOriginProtocolPolicy)。

当您将 CloudFront 配置为使用 HTTPS 处理源请求时，以下规则也适用：
+ 您必须将 CloudFront 配置为使用源请求策略将 `Host` 标头转发到源。您可以使用 [AllViewer 托管源请求策略](using-managed-origin-request-policies.md#managed-origin-request-policy-all-viewer)。
+ 确保应用程序负载均衡器具有 HTTPS 侦听器（如[上一节](#restrict-alb-route-based-on-header)中所示）。有关更多信息，请参阅《应用程序负载均衡器用户指南》**中的[创建 HTTPS 侦听器](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html)。使用 HTTPS 侦听器要求您拥有与路由到应用程序负载均衡器的域名匹配的 SSL/TLS 证书。
+ 只能在 AWS Certificate Manager（ACM）中的 `us-east-1` AWS 区域 中请求（或导入）CloudFront 的 SSL/TLS 证书。由于 CloudFront 是一项全球服务，因此它会自动将证书从 `us-east-1` 区域分发到与您的 CloudFront 分配关联的所有区域。
  + 例如，如果您在 `ap-southeast-2` 区域拥有应用程序负载均衡器（ALB），则必须在 `ap-southeast-2` 区域（用于在 CloudFront 和 ALB 源之间使用 HTTPS）和 `us-east-1` 区域（用于在查看器和 CloudFront 之间使用 HTTPS）中配置 SSL/TLS 证书。这两个证书都应与路由到应用程序负载均衡器的域名匹配。有关更多信息，请参阅 [用于 AWS Certificate Manager 的 AWS 区域](cnames-and-https-requirements.md#https-requirements-aws-region)。
+ 如果 Web 应用程序的最终用户（也称为*查看器*或*客户端*）可以使用 HTTPS，则还可以将 CloudFront 配置为首选（甚至需要）来自最终用户的 HTTPS 连接。为此，请使用**查看器协议策略**设置。您可以将其设置为将最终用户从 HTTP 重定向到 HTTPS，或拒绝使用 HTTP 的请求。此设置可在 CloudFront 控制台、CloudFormation 和 CloudFront API 中使用。有关更多信息，请参阅 [查看器协议策略](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy)。

**轮换标头名称和值**  
除了使用 HTTPS 之外，还建议定期轮换标头名称和值。执行此操作的高级步骤如下：

1. 将 CloudFront 配置为将另一个自定义 HTTP 标头添加到向应用程序负载均衡器发送的请求中。

1. 更新应用程序负载均衡器侦听器规则，以转发包含另一个自定义 HTTP 标头的请求。

1. 配置 CloudFront 以停止将原始自定义 HTTP 标头添加到向应用程序负载均衡器发送的请求中。

1. 更新应用程序负载均衡器侦听器规则，以停止转发包含原始自定义 HTTP 标头的请求。

有关完成这些步骤的更多信息，请参阅前面的部分。

## （可选）使用 CloudFront 的 AWS 托管前缀列表限制对源的访问
<a name="limit-access-to-origin-using-aws-managed-prefixes"></a>

要进一步限制对应用程序负载均衡器的访问，您可以配置与应用程序负载均衡器关联的安全组，使其在服务使用 AWS 托管前缀列表时仅接受来自 CloudFront 的流量。这可以防止来自 CloudFront 以外的流量到达应用程序负载均衡器的网络层（第 3 层）或传输层（第 4 层）。

有关更多信息，请参阅 [Limit access to your origins using the AWS-managed prefix list for Amazon CloudFront](https://aws.amazon.com//blogs/networking-and-content-delivery/limit-access-to-your-origins-using-the-aws-managed-prefix-list-for-amazon-cloudfront/) 博客文章。

# 限制您的内容的地理分配
<a name="georestrictions"></a>

您可以使用*地理限制*（有时称为*地理阻止*）来禁止特定地理位置的用户访问您通过 Amazon CloudFront 分配分发的内容。要使用地理限制功能，您有两个选项：
+ 使用 CloudFront 地理限制功能。使用该选项可限制对与某个分配关联的所有文件的访问，并在国家/地区级别限制访问。
+ 使用第三方地理定位服务。使用该选项可限制对与某个分配关联的一小部分文件的访问，或在比国家/地区级别更细化级别上限制访问。

**Topics**
+ [使用 CloudFront 地理限制](#georestrictions-cloudfront)
+ [使用第三方地理定位服务](#georestrictions-geolocation-service)

## 使用 CloudFront 地理限制
<a name="georestrictions-cloudfront"></a>

用户请求您的内容时，无论其处于何处，CloudFront 通常都会提供所请求的内容。如果您要阻止特定国家/地区的用户访问您的内容，可以使用 CloudFront 地理限制功能执行以下任一操作：
+ 仅当用户位于允许列表中的某个已批准国家/地区时，才授予他们访问您的内容的权限。
+ 当用户位于拒绝列表中被禁止的国家/地区之一时，阻止他们访问您的内容。

例如，如果一个请求来自您无权分发内容的国家/地区，您可以使用 CloudFront 地理限制功能来阻止该请求。

**注意**  
CloudFront 使用第三方数据库确定用户的位置。IP 地址和国家/地区之间映射的准确性因区域而异。根据最近的测试，整体准确性为 99.8%。如果 CloudFront 无法确定用户的位置，则它会提供用户已请求的内容。

以下是地理限制的工作机制：

1. 假设您仅有权在列支敦士登分发内容。您可以更新您的 CloudFront 分配以添加仅包含列支敦士登的允许列表。（您也可以添加包含除列支敦士登以外所有国家/地区的拒绝列表。)

1. 摩纳哥的一名用户请求您的内容，DNS 将该请求路由至意大利米兰的某个 CloudFront 边缘站点。

1. 米兰的边缘站点将检查您的分配，并确定摩纳哥的这名用户没有权限，无法下载您的内容。

1. CloudFront 向该用户返回 HTTP 状态代码 `403 (Forbidden)`。

您可以选择性地将 CloudFront 配置为向该用户返回自定义错误消息，并且可以指定您希望 CloudFront 缓存请求的文件的错误响应的时间。默认值为 10 秒。有关更多信息，请参阅 [为特定 HTTP 状态代码创建自定义错误页面](creating-custom-error-pages.md)。

地理限制适用于整个分配。如果您需要对部分内容应用一个限制，而对另一部分内容应用不同的限制（或无限制），则必须创建单独的 CloudFront 分配或[使用第三方地理定位服务](#georestrictions-geolocation-service)。

如果您启用 CloudFront [标准日志](AccessLogs.md)（访问日志），则可通过在日志条目中搜索其 `sc-status`（HTTP 状态代码）值为 `403` 的条目来确定 CloudFront 已拒绝的请求。不过，如果仅使用标准日志，则无法区分 CloudFront 根据用户位置拒绝的请求与 CloudFront 由于其他原因而使用户无权访问文件因而拒绝的请求。如果您有第三方地理定位服务 (例如 Digital Element 或 MaxMind)，则可基于访问日志中的 `c-ip` (客户端 IP) 列的 IP 地址来识别请求的位置。有关 CloudFront 标准日志的更多信息，请参阅[访问日志（标准日志）](AccessLogs.md)。

以下过程介绍如何使用 CloudFront 控制台将地理限制添加到现有的分配。有关如何使用控制台创建分配的信息，请参阅[创建分配](distribution-web-creating-console.md)。<a name="restrictions-geo-procedure"></a>

**将地理限制添加到您的 CloudFront Web 分配（控制台）**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**分配**，然后选择要更新的分配。

1. 选择**安全**选项卡，然后选择**地理限制**。

1. 选择**编辑**。

1. 选择**允许列表**以创建允许的国家/地区的列表，或选择**阻止列表**以创建被阻止的国家/地区的列表。

1. 将所需的国家/地区添加到列表中，然后选择**保存更改**。

## 使用第三方地理定位服务
<a name="georestrictions-geolocation-service"></a>

利用 CloudFront 地理限制功能，您可以在国家/地区级别控制您通过给定 Web 分配来分配的所有文件的内容分配。如果您有一个地理限制使用案例，而这些限制不遵循国家/地区界限，或者您希望限制仅可访问由给定分配提供的某些文件，则可以将 CloudFront 与第三方地理定位服务结合使用。这使您不仅可以根据国家/地区，还可以根据城市、邮政编码甚至纬度和经度来控制您的内容。

如果您使用的是第三方地理定位服务，建议您使用 CloudFront 签名的 URL，通过它可以指定过期日期和时间，在该日期和时间后，URL 不再有效。此外，建议您使用 Amazon S3 存储桶作为您的源，因为您随后可以使用 CloudFront [源访问控制](private-content-restricting-access-to-s3.md)来阻止用户直接从源访问您的内容。有关签名的 URL 和源访问控制的更多信息，请参阅[使用签名 URL 和签名 Cookie 提供私有内容](PrivateContent.md)。

以下步骤说明如何使用第三方地理定位服务来控制对您的文件的访问。

**使用第三方地理定位服务限制对 CloudFront 分配中的文件的访问**

1. 获得具有地理定位服务的账户。

1. 将您的内容上传到 Amazon S3 存储桶。

1. 配置 Amazon CloudFront 和 Amazon S3 以提供私有内容。有关更多信息，请参阅 [使用签名 URL 和签名 Cookie 提供私有内容](PrivateContent.md)。

1. 编写您的 Web 应用程序，以做到以下几点：
   + 将每个用户请求的 IP 地址发送到地理定位服务。
   + 评估地理定位服务的返回值，以确定用户是否位于您希望 CloudFront 分配您的内容的位置。
   + 如果要将内容分发到用户的位置，请为您的 CloudFront 内容生成签名的 URL。如果您不想将内容分发到该位置，请将 HTTP 状态代码 `403 (Forbidden)` 返回给用户。或者，您可配置 CloudFront 以返回自定义错误消息。有关更多信息，请参阅 [为特定 HTTP 状态代码创建自定义错误页面](creating-custom-error-pages.md)。

   有关更多信息，请参阅您使用的地理定位服务的文档。

您可以使用 Web 服务器变量来获取正在访问您网站的用户的 IP 地址。请注意以下几点：
+ 如果您的 Web 服务器未通过负载均衡器连接到 Internet，您可以使用 Web 服务器变量来获取远程 IP 地址。但是，该 IP 地址并非始终为用户的 IP 地址。它还可以是代理服务器的 IP 地址，具体取决于用户如何连接到 Internet。
+ 如果您的 Web 服务器通过负载均衡器连接到 Internet，Web 服务器变量可能包含负载均衡器的 IP 地址，而不是用户的 IP 地址。在该配置中，建议您使用 `X-Forwarded-For` HTTP 标头中的最后一个 IP 地址。该标头通常包含多个 IP 地址，其中大部分是代理或负载均衡器的 IP 地址。列表中的最后一个 IP 地址是最有可能与用户的地理位置关联的 IP 地址。

如果您的 Web 服务器未连接至负载均衡器，建议您使用 Web 服务器变量，而不是 `X-Forwarded-For` 标头，以避免 IP 地址欺骗。

# 使用字段级加密帮助保护敏感数据
<a name="field-level-encryption"></a>

借助 Amazon CloudFront，您可以使用 HTTPS 对与源服务器的端到端连接实施保护。字段级加密增加了一个额外的安全保护层，可让您在整个系统处理过程中保护特定的数据，以便只有某些应用程序才能查看它。

借助字段级加密，您可以让您的用户安全地向您的 Web 服务器上传敏感信息。用户提供的敏感信息在靠近用户的边缘进行加密，并在整个应用程序堆栈中保持加密状态。此加密确保只有需要数据的应用程序（并且具有用于解密的凭证）能够做到这一点。

要使用字段级加密，请在配置您的 CloudFront 分配时，指定要加密的 POST 请求中的字段集以及用于对其进行加密的公有密钥。您最多可以在一个请求中加密 10 个数据字段。（您无法使用字段级加密在一个请求中加密所有数据；您必须指定要加密的各个字段。）

如果带有字段级加密的 HTTPS 请求转发到源，并且请求路由经过您的整个源应用程序或子系统，则敏感数据仍然进行加密，从而降低敏感数据泄露或意外丢失的风险。出于业务原因需要访问敏感数据的组件 (例如需要访问信用号码的支付处理系统) 可以使用适当的私有密钥来解密和访问数据。

**注意**  
要使用字段级加密，您的源必须支持分块编码。

![\[CloudFront 中的字段级加密\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/images/fleoverview.png)


CloudFront 字段级加密使用非对称加密，也称为公有密钥加密。您需要向 CloudFront 提供一个公有密钥，之后系统会自动加密您指定的所有敏感数据。不能使用您向 CloudFront 提供的密钥解密经过加密的值；只有您的私有密钥才能执行解密。

![\[仅加密敏感数据\]](http://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/images/encryptedfields.png)


**Topics**
+ [字段级加密概览](#field-level-encryption-overview)
+ [设置字段级加密](#field-level-encryption-setting-up)
+ [在源端解密数据字段](#field-level-encryption-decrypt)

## 字段级加密概览
<a name="field-level-encryption-overview"></a>

以下步骤概述了如何设置字段级加密。有关具体步骤，请参阅[设置字段级加密](#field-level-encryption-setting-up)。

1. **获取公有密钥/私有密钥对。**您必须获取和添加公有密钥，然后才能开始在 CloudFront 中设置字段级加密。

1. **创建字段级加密配置文件。**您在 CloudFront 中创建的字段级加密配置文件可定义要加密的字段。

1. **创建字段级加密配置。**配置指定要用于加密特定数据字段的配置文件（根据请求的内容类型或查询参数）。您还可以为不同方案选择所需的请求转发行为选项。例如，您可以设置请求 URL 中的查询参数指定的配置文件名称在 CloudFront 中不存在时的行为。

1. **链接到缓存行为。**将配置链接到分配的缓存行为，用于指定 CloudFront 应加密数据的时间。

## 设置字段级加密
<a name="field-level-encryption-setting-up"></a>

按照以下步骤操作，开始使用字段级加密。要了解有关字段级加密的配额（以前称为限制），请参阅[配额](cloudfront-limits.md)。
+ [第 1 步：创建 RSA 密钥对](#field-level-encryption-setting-up-step1)
+ [第 2 步：将您的公有密钥添加到 CloudFront](#field-level-encryption-setting-up-step2)
+ [第 3 步：为字段级加密创建配置文件](#field-level-encryption-setting-up-step3)
+ [第 4 步：创建配置](#field-level-encryption-setting-up-step4)
+ [第 5 步：将配置添加到缓存行为](#field-level-encryption-setting-up-step5)

### 第 1 步：创建 RSA 密钥对
<a name="field-level-encryption-setting-up-step1"></a>

要开始使用，您必须创建包含公有密钥和私有密钥的 RSA 密钥对。公有密钥使 CloudFront 能够对数据进行加密，私有密钥使源处的组件能够对已加密的字段进行解密。可以使用 OpenSSL 或其他工具创建密钥对。密钥大小必须为 2048 位。

例如，如果使用 OpenSSL，则可以使用以下命令生成一个长度为 2048 位的密钥对，并将其保存到文件 `private_key.pem` 中：

```
openssl genrsa -out private_key.pem 2048
```

生成的文件同时包含公有密钥和私有密钥。要从该文件中提取公有密钥，请运行以下命令：

```
openssl rsa -pubout -in private_key.pem -out public_key.pem
```

公有密钥文件 (`public_key.pem`) 包含您在以下步骤中粘贴的已编码密钥值。

### 第 2 步：将您的公有密钥添加到 CloudFront
<a name="field-level-encryption-setting-up-step2"></a>

获取 RSA 密钥对后，将您的公有密钥添加到 CloudFront。<a name="field-level-encryption-setting-up-step2-procedure"></a>

**将您的公有密钥添加到 CloudFront（控制台）**

1. 登录 AWS 管理控制台，并通过以下网址打开 CloudFront 控制台：[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在导航窗格中，选择**公有密钥**。

1. 选择**添加公有密钥**。

1. 对于**密钥名称**，请为密钥键入唯一的名称。该名称不能包含空格，且只能包含字母数字字符、下划线 (\$1) 和连字符 (-)。最大字符数为 128。

1. 对于 **Key value (密钥值)**，粘贴公有密钥的已编码密钥值，包括 `-----BEGIN PUBLIC KEY-----` 和 `-----END PUBLIC KEY-----` 行。

1. 对于**注释**，请添加一个可选的注释。例如，您可以包含公有密钥的到期日期。

1. 选择 **Add**。

您可以重复此过程中的步骤，添加更多密钥供 CloudFront 使用。

### 第 3 步：为字段级加密创建配置文件
<a name="field-level-encryption-setting-up-step3"></a>

在您至少将一个公有密钥添加到 CloudFront 后，创建一个配置文件，在其中告诉 CloudFront 加密哪些字段。<a name="field-level-encryption-setting-up-step3-procedure"></a>

**为字段级加密创建配置文件 (控制台)**

1. 在导航窗格中，选择**字段级加密**。

1. 选择**创建配置文件**。

1. 填写以下字段：  
**配置文件名称**  
为配置文件键入唯一名称。该名称不能包含空格，且只能包含字母数字字符、下划线 (\$1) 和连字符 (-)。最大字符数为 128。  
**公有密钥名称**  
在下拉列表中，选择您在第 2 步中添加到 CloudFront 的公有密钥的名称。CloudFront 可使用该密钥加密您在此配置文件中指定的字段。  
**提供商名称**  
键入短语以帮助识别密钥，例如从其获取密钥对的提供商。在应用程序解密数据字段时，需要该信息以及私有密钥。提供商名称不能包含空格，且只能包含字母数字字符、冒号 (:)、下划线 (\$1) 和连字符 (-)。最大字符数为 128。  
**要匹配的字段名称模式**  
键入数据字段的名称，或者键入用于识别请求中希望 CloudFront 加密的数据字段名称的模式。选择 \$1 选项以添加您想要使用此密钥加密的所有字段。  
对于字段名称模式，您可以键入数据字段的整个名称，如 DateOfBirth，或者仅键入名称的第一部分及通配符 (\$1)，如 CreditCard\$1。除了可选的通配符 (\$1) 外，字段名称模式必须仅包含字母数字字符、方括号 ([ 和 ])、句点 (.)、下划线 (\$1) 和连字符 (-)。  
请确保您未针对不同的字段名称模式使用重叠字符。例如，如果您有一个字段名称模式 ABC\$1，您就不能再添加另一个字段名称模式 AB\$1。此外，字段名称区分大小写，并且您可以使用的字符的最大数目为 128。  
**评论**  
（可选）键入有关该配置文件的注释。您可以使用的最大字符数为 128。

1. 填写字段后，选择**创建配置文件**。

1. 如果您要添加更多配置文件，请选择**添加配置文件**。

### 第 4 步：创建配置
<a name="field-level-encryption-setting-up-step4"></a>

在您创建一个或多个字段级加密配置文件后，请创建一个配置，以便指定请求的内容类型，包括要加密的数据、要用于加密的配置文件以及用于指定希望 CloudFront 如何处理加密的其他选项。

例如，当 CloudFront 无法加密数据时，您可以指定在以下情况中，CloudFront 是阻止请求还是将请求转发到您的源：
+ **当请求的内容类型未在配置中时** – 如果尚未在配置中添加某种内容类型，则可以指定 CloudFront 是应将具有该内容类型的请求转发到源而不加密数据字段，还是阻止该请求并返回错误。
**注意**  
如果您将内容类型添加到配置中，但未指定要与该类型一起使用的配置文件，则 CloudFront 始终将带有该内容类型的请求转发到源。
+ **在查询参数中提供的配置文件名称未知时** – 在指定的 `fle-profile` 查询参数具有的配置文件名称对于分配不存在时，您可以指定 CloudFront 是应将请求发送到源而不加密数据字段，还是阻止该请求并返回错误。

在配置中，您还可以指定在 URL 中提供配置文件作为查询参数的操作是否覆盖您已为该查询映射到内容类型的配置文件。默认情况下，CloudFront 使用您已映射到内容类型的配置文件（如果您已指定）。这样一来，您便拥有一个默认使用的配置文件，但您可以决定要强制执行其他配置文件的某些请求。

因此，举例来说，您可以（在您的配置中）指定 **SampleProfile** 作为要使用的查询参数配置文件。然后，您可以使用 URL `https://d1234.cloudfront.net?fle-profile=SampleProfile` 而不是 `https://d1234.cloudfront.net` 以便 CloudFront 在该请求中使用 **SampleProfile** 而不是您为请求的内容类型设置的配置文件。

您可以为单个账户最多创建 10 个配置，然后将其中一个配置与该账户的任何分配的缓存行为相关联。<a name="field-level-encryption-setting-up-step4-procedure"></a>

**为字段级加密创建配置（控制台）**

1. 在**字段级加密**页面上，选择**创建配置**。

   注意：如果您尚未创建至少一个配置文件，您将不会看到配置创建选项。

1. 填写以下字段以指定要使用的配置文件。（某些字段无法更改。）  
**内容类型（无法更改）**  
内容类型将设置为 `application/x-www-form-urlencoded` 并且无法更改。  
**默认配置文件 ID（可选）**  
在下拉列表中，选择您要映射到**内容类型**字段中的内容类型的配置文件。  
**内容格式（无法更改）**  
内容格式将设置为 `URLencoded` 并且无法更改。

1. 如果您要更改以下选项的默认 CloudFront 行为，请选择适当的复选框。  
**当请求的内容类型未配置时，将请求转发到原始地址**  
如果您要允许请求转到源，*并且您尚未指定要用于请求的内容类型的配置文件，*则选中该复选框。  
**使用提供的查询参数覆盖内容类型的配置文件**  
如果您要允许某个查询参数中提供的配置文件*覆盖您为内容类型指定的配置文件*，则选中该复选框。

1. 如果您选中该复选框以允许查询参数覆盖默认配置文件，则您必须完成以下额外配置字段。您最多可以创建五个查询参数映射供查询使用。  
**查询参数**  
键入要包含在 `fle-profile` 查询参数的 URL 中的值。对于此查询的字段级加密，此值告诉 CloudFront 使用与此查询参数关联的配置文件 ID（在下一个字段中指定）。  
您可以使用的最大字符数为 128。该值不能包含空格，并且必须仅使用字母数字或以下字符：短划线 (-)、句点 (.)、下划线 (\$1)、星号 (\$1)、加号 (\$1)、百分号 (%)。  
**配置文件 ID**  
在下拉列表中，选择您要与为**查询参数**键入的值相关联的配置文件。  
**当查询参数中指定的配置文件不存在时，将请求转发到源**  
如果您要允许请求转到您的源，*并且在查询参数中指定的配置文件没有在 CloudFront 中定义，*则选择该复选框。

### 第 5 步：将配置添加到缓存行为
<a name="field-level-encryption-setting-up-step5"></a>

要使用字段级加密，需要将配置链接到分配的缓存行为，方法为添加配置 ID 作为分配的值。

**重要**  
要将字段级加密配置链接到缓存行为，必须将分配配置为始终使用 HTTPS，并接受来自查看器的 HTTP `POST` 和 `PUT` 请求。也就是说，必须满足以下条件：  
缓存行为的**查看器协议策略**必须设置为**将 HTTP 重定向到 HTTPS** 或**仅 HTTPS**。（在 CloudFormation 或 CloudFront API 中，`ViewerProtocolPolicy` 必须设置为 `redirect-to-https` 或 `https-only`。）
缓存行为的 **Allowed HTTP Methods（允许的 HTTP 方法）**必须设置为 **GET、HEAD、OPTIONS、PUT、POST、PATCH、DELETE**。（在 CloudFormation 或 CloudFront API 中，`AllowedMethods` 必须设置为 `GET`、`HEAD`、`OPTIONS`、`PUT`、`POST`、`PATCH`、`DELETE`。可以按照任意顺序指定这些项。）
源设置的**源协议策略**必须设置为**匹配查看器**或**仅 HTTPS**。（在 CloudFormation 或 CloudFront API 中，`OriginProtocolPolicy` 必须设置为 `match-viewer` 或 `https-only`。）

有关更多信息，请参阅 [所有分配设置参考](distribution-web-values-specify.md)。

## 在源端解密数据字段
<a name="field-level-encryption-decrypt"></a>

CloudFront 使用 [AWS Encryption SDK](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html) 来加密数据字段。在您的整个应用程序堆栈中，数据保持加密，并且只能由具有用于解密数据的凭证的应用程序进行访问。

加密后，密码文本采用 base64 编码。当您的应用程序解密源上的文本时，应用程序必须首先解码密码文本，然后使用 AWS 加密开发工具包解密数据。

以下代码示例说明了应用程序如何解密源上的数据。请注意以下几点：
+ 为简化示例，此示例从工作目录中的文件内加载公有密钥和私有密钥（采用 DER 格式）。实际上，您会将私有密钥存储在安全的离线位置（如离线硬件安全模块），并将公有密钥分配给您的开发团队。
+ CloudFront 在加密数据时使用特定信息，应在源上使用同一组参数进行解密。初始化主密钥时 CloudFront 使用的参数包括以下这些：
  + PROVIDER\$1NAME：您在创建字段级加密配置文件时指定该值。在此处使用相同的值。
  + KEY\$1NAME：当您将公有密钥上传到 CloudFront 时创建了其名称，然后在配置文件中指定了密钥名称。在此处使用相同的值。
  + ALGORITHM：CloudFront 使用 `RSA/ECB/OAEPWithSHA-256AndMGF1Padding` 作为加密算法，因此，您必须使用相同的算法解密数据。
+ 如果您在运行以下示例程序时使用密码文本作为输入，则解密的数据会输出到您的控制台。有关更多信息，请参阅 [加密开发工具包中的 ](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/java-example-code.html)Java 代码示例AWS。

### 代码示例
<a name="field-level-encryption-decrypt-sample"></a>

```
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CryptoResult;
import com.amazonaws.encryptionsdk.jce.JceMasterKey;

/**
 * Sample example of decrypting data that has been encrypted by CloudFront field-level encryption.
 */
public class DecryptExample {

    private static final String PRIVATE_KEY_FILENAME = "private_key.der";
    private static final String PUBLIC_KEY_FILENAME = "public_key.der";
    private static PublicKey publicKey;
    private static PrivateKey privateKey;

    // CloudFront uses the following values to encrypt data, and your origin must use same values to decrypt it.
    // In your own code, for PROVIDER_NAME, use the provider name that you specified when you created your field-level
    // encryption profile. This sample uses 'DEMO' for the value.
    private static final String PROVIDER_NAME = "DEMO";
    // In your own code, use the key name that you specified when you added your public key to CloudFront. This sample
    // uses 'DEMOKEY' for the key name.
    private static final String KEY_NAME = "DEMOKEY";
    // CloudFront uses this algorithm when encrypting data.
    private static final String ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";

    public static void main(final String[] args) throws Exception {

        final String dataToDecrypt = args[0];

        // This sample uses files to get public and private keys.
        // In practice, you should distribute the public key and save the private key in secure storage.
        populateKeyPair();

        System.out.println(decrypt(debase64(dataToDecrypt)));
    }

    private static String decrypt(final byte[] bytesToDecrypt) throws Exception {
        // You can decrypt the stream only by using the private key.

        // 1. Instantiate the SDK
        final AwsCrypto crypto = new AwsCrypto();

        // 2. Instantiate a JCE master key
        final JceMasterKey masterKey = JceMasterKey.getInstance(
                publicKey,
                privateKey,
                PROVIDER_NAME,
                KEY_NAME,
                ALGORITHM);

        // 3. Decrypt the data
        final CryptoResult <byte[], ? > result = crypto.decryptData(masterKey, bytesToDecrypt);
        return new String(result.getResult());
    }

    // Function to decode base64 cipher text.
    private static byte[] debase64(final String value) {
        return Base64.decodeBase64(value.getBytes());
    }

    private static void populateKeyPair() throws Exception {
        final byte[] PublicKeyBytes = Files.readAllBytes(Paths.get(PUBLIC_KEY_FILENAME));
        final byte[] privateKeyBytes = Files.readAllBytes(Paths.get(PRIVATE_KEY_FILENAME));
        publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(PublicKeyBytes));
        privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
    }
}
```