

# 使用签名 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)