

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

# 验证 CloudTrail 日志文件完整性
<a name="cloudtrail-log-file-validation-intro"></a>

要确定日志文件在 CloudTrail 传送后是被修改、删除还是未更改，可以使用 CloudTrail 日志文件完整性验证。该功能是使用业界标准算法构建的：哈希采用 SHA-256，数字签名采用带 RSA 的 SHA-256。这使得在没有检测到的情况下修改、删除或伪造 CloudTrail 日志文件在计算上是不可行的。您可以使用 AWS CLI 在文件 CloudTrail 交付地点验证文件。

## 为什么使用它？
<a name="cloudtrail-log-file-validation-intro-use-cases"></a>

在安全和事故调查中，经验证的日志文件非常重要。例如，通过经验证的日志文件，您可以十分确定日志文件本身未更改，或者特定用户凭证执行了特定 API 活动。 CloudTrail 日志文件完整性验证过程还可以让您知道日志文件是否已被删除或更改，或者肯定地断言在给定时间段内没有向您的账户发送任何日志文件。

## 工作原理
<a name="cloudtrail-log-file-validation-intro-how-it-works"></a>

启用日志文件完整性验证后， CloudTrail 会为其提供的每个日志文件创建一个哈希值。每隔一小时， CloudTrail 还会创建并传送一个文件，该文件引用了过去一小时的日志文件，并包含每个文件的哈希值。此文件称为摘要文件。 CloudTrail 使用公钥和私钥对的私钥签署每个摘要文件。交付后，您可以使用公钥来验证摘要文件。 CloudTrail 对每个密钥对使用不同的密钥对 AWS 区域。

摘要文件将与您的 CloudTrail 日志文件传输到与您的跟踪关联的 Amazon S3 存储桶。如果您的日志文件从所有区域或多个账户传输到单个 Amazon S3 存储桶，则 CloudTrail 会将来自这些区域和账户的摘要文件传送到同一个存储桶中。

摘要文件放在与日志文件不同的文件夹中。摘要文件与日志文件分开放置，您就可以执行细粒度安全策略，允许现有日志处理解决方案继续运行，无需进行修改。每个摘要文件还包含之前摘要文件（如果存在）的数字签名。当前摘要文件的签名位于摘要文件 Amazon S3 对象的元数据属性中。有关摘要文件内容的更多信息，请参阅[CloudTrail 摘要文件结构](cloudtrail-log-file-validation-digest-file-structure.md)。

### 存储日志文件和摘要文件
<a name="cloudtrail-log-file-validation-intro-storage"></a>

您可以安全、耐用、廉价地将 CloudTrail 日志文件和摘要文件无限期地存储在 Amazon S3 或 Amazon Glacier 中。为增强存储在 Amazon S3 中的摘要文件的安全性，您可以使用 [Amazon S3 MFA 删除](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMFADelete.html)。

### 启用验证并验证文件
<a name="cloudtrail-log-file-validation-intro-enabling-and-using"></a>

要启用日志文件完整性验证，可以使用 AWS 管理控制台 AWS CLI、或 CloudTrail API。启用日志文件完整性验证允许 CloudTrail 将摘要日志文件传输到您的 Amazon S3 存储桶，但不能验证文件的完整性。有关更多信息，请参阅 [为启用日志文件完整性验证 CloudTrail](cloudtrail-log-file-validation-enabling.md)。

要验证 CloudTrail 日志文件的完整性，您可以使用 AWS CLI 或创建自己的解决方案。 AWS CLI 将在文件 CloudTrail 交付地点对文件进行验证。如果您要验证已移到其他位置（Amazon S3 或别处）的日志，您可以创建自己的验证工具。

有关使用验证日志的信息 AWS CLI，请参阅[CloudTrail 使用验证日志文件的完整性 AWS CLI](cloudtrail-log-file-validation-cli.md)。有关开发 CloudTrail 日志文件验证的自定义实现的信息，请参阅[CloudTrail 日志文件完整性验证的自定义实现](cloudtrail-log-file-custom-validation.md)。

# 为启用日志文件完整性验证 CloudTrail
<a name="cloudtrail-log-file-validation-enabling"></a>

您可以使用 AWS 管理控制台、 AWS 命令行界面 (AWS CLI) 或 CloudTrail API 启用日志文件完整性验证。 CloudTrail 大约一小时后开始交付摘要文件。

## AWS 管理控制台
<a name="cloudtrail-log-file-validation-enabling-console"></a>

要使用 CloudTrail 控制台启用日志文件完整性验证，请在创建或更新跟踪时为 “**启用日志文件验证**” 选项选择 “**是**”。默认情况下会对新的跟踪记录启用此功能。有关更多信息，请参阅 [使用控制台创建和更新跟踪](cloudtrail-create-and-update-a-trail-by-using-the-console.md)。

## AWS CLI
<a name="cloudtrail-log-file-validation-enabling-cli"></a>

要使用启用日志文件完整性验证 AWS CLI，请将`--enable-log-file-validation`选项与 create-t [rail 或 [up](https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/update-trail.html) date-trail 命令](https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/create-trail.html)一起使用。要禁用日志文件完整性验证，请使用 `--no-enable-log-file-validation` 选项。

**示例**

下面的 `update-trail` 命令启用日志文件验证并开始将摘要文件传送到指定跟踪的 Amazon S3 存储桶中。

```
aws cloudtrail update-trail --name your-trail-name --enable-log-file-validation
```

## CloudTrail API
<a name="cloudtrail-log-file-validation-enabling-api"></a>

要使用 CloudTrail API 启用日志文件完整性验证，请在调用`CreateTrail`或`true`时将`EnableLogFileValidation`请求参数设置为`UpdateTrail`。

有关更多信息，请参阅《[AWS CloudTrail API 参考](https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/)》中的[CreateTrail](https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_CreateTrail.html)和[UpdateTrail](https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_UpdateTrail.html)。

# CloudTrail 使用验证日志文件的完整性 AWS CLI
<a name="cloudtrail-log-file-validation-cli"></a>

要使用验证日志 AWS Command Line Interface，请使用 CloudTrail `validate-logs`命令。此命令使用提交到 Amazon S3 存储桶的摘要文件执行验证。有关摘要文件的信息，请参阅 [CloudTrail 摘要文件结构](cloudtrail-log-file-validation-digest-file-structure.md)。

 AWS CLI 允许您检测以下类型的更改：
+ 修改或删除 CloudTrail 日志文件
+ 修改或删除 CloudTrail 摘要文件
+ 上述两者的修改或删除

**注意**  
仅 AWS CLI 验证摘要文件引用的日志文件。有关更多信息，请参阅 [检查特定文件是否由 CloudTrail](#cloudtrail-log-file-validation-cli-validate-logs-check-file)。

## 先决条件
<a name="cloudtrail-log-file-validation-cli-prerequisites"></a>

要使用验证日志文件的完整性 AWS CLI，必须满足以下条件：
+ 您必须联机连接到 AWS。
+ 您必须拥有包含摘要文件和日志文件的 Amazon S3 存储桶的读取访问权限。
+ 摘要和日志文件不得从 CloudTrail 交付它们的原始 Amazon S3 位置移出。
+ 执行命令的角色必须有权为跟踪引用的每个 S3 存储桶调用 `ListObjects`、`GetObject` 和 `GetBucketLocation`。

**注意**  
 AWS CLI无法验证下载到本地磁盘的日志文件。有关自行创建验证工具的指南，请参阅 [CloudTrail 日志文件完整性验证的自定义实现](cloudtrail-log-file-custom-validation.md)。

## validate-logs
<a name="cloudtrail-log-file-validation-cli-validate-logs"></a>

### 语法
<a name="cloudtrail-log-file-validation-cli-validate-logs-syntax"></a>

`validate-logs` 采用下面的语法形式。括号内为可选参数。

`aws cloudtrail validate-logs --trail-arn <trailARN> --start-time <start-time> [--end-time <end-time>] [--s3-bucket <amzn-s3-demo-bucket>] [--s3-prefix <prefix>] [--account-id <account-id>] [--verbose]` 

**注意**  
`validate-logs` 命令与特定区域相关。必须指定`--region`全局选项才能验证特定日志的日志 AWS 区域。

### 选项
<a name="cloudtrail-log-file-validation-cli-validate-logs-options"></a>

`validate-logs` 提供以下命令行选项。`--trail-arn` 和 `--start-time` 为必需选项。组织跟踪还需要 `--account-id` 选项。

`--start-time`  
指定将验证在指定 UTC 时间戳值当时或之后提交的日志文件。示例：`2015-01-08T05:21:42Z`。

`--end-time`  
（可选）指定将验证在指定 UTC 时间戳值当时或之前提交的日志文件。默认值为当前 UTC 时间（`Date.now()`）。示例：`2015-01-08T12:31:41Z`。  
对于指定的时间范围，`validate-logs` 命令只检查其对应的摘要文件引用的日志文件。不检查 Amazon S3 存储桶中的任何其他日志文件。有关更多信息，请参阅 [检查特定文件是否由 CloudTrail](#cloudtrail-log-file-validation-cli-validate-logs-check-file)。

`--s3-bucket`  
（可选）指定存储摘要文件的 Amazon S3 存储桶。如果未指定存储桶名称，则 AWS CLI 将通过调用来检索存储桶名称`DescribeTrails()`。

`--s3-prefix`  
（可选）指定表示摘要文件存储位置的 Amazon S3 前缀。如果未指定，则 AWS CLI 将通过调用来检索它`DescribeTrails()`。  
仅在当前前缀不同于指定时间范围期间使用的前缀时，才应使用此选项。

`--account-id`  
也可选择指定用于验证日志的账户。组织跟踪需要此参数来验证组织内特定账户的日志。

`--trail-arn`  
指定要验证的跟踪的 Amazon 资源名称（ARN）。跟踪 ARN 遵循的格式。  

```
arn:aws:cloudtrail:us-east-2:111111111111:trail/MyTrailName
```
要获取跟踪的 ARN，您可以先使用 `describe-trails` 命令，然后再运行 `validate-logs`。  
如果在您指定的时间范围内日志文件被提交到多个存储桶，而您需要将对日志文件的验证限制在一个存储桶，则除了跟踪 ARN，您可能还需要指定存储桶的名称和前缀。

`--verbose`  
（可选）输出指定时间范围内的每个日志文件或摘要文件的验证信息。输出指示文件保持不变还是发生过修改或已删除。在非详细模式（默认）下，仅当验证失败时才返回信息。

### 示例
<a name="cloudtrail-log-file-validation-cli-validate-logs-example"></a>

下面的示例验证从指定起始时间到当前时间的日志文件，使用为当前跟踪配置的 Amazon S3 存储桶并指定详细输出。

```
aws cloudtrail validate-logs --start-time 2015-08-27T00:00:00Z --end-time 2015-08-28T00:00:00Z --trail-arn arn:aws:cloudtrail:us-east-2:111111111111:trail/my-trail-name --verbose
```

### `validate-logs` 的工作原理
<a name="cloudtrail-log-file-validation-cli-validate-logs-how-it-works"></a>

`validate-logs` 命令从验证指定时间范围内最新的摘要文件开始。首先，它验证摘要文件是否已从其声明的所属位置下载。换句话说，如果 CLI 从 S3 位置 `p1` 下载摘要文件 `df1`，则 validate-logs 会验证 `p1 == df1.digestS3Bucket + '/' + df1.digestS3Object`。

如果摘要文件的签名有效，则它检查摘要文件中引用的每个日志的哈希值。之后，此命令按时间倒序连续验证之前的摘要文件及其引用的日志文件。它继续进行这一操作，直到到达指定的 `start-time` 值或摘要链结束为止。如果有摘要文件缺失或无效，则此命令在输出中指出无法验证的时间范围。该`validate-logs`命令首先在标准摘要链上运行。完成标准摘要验证后，它会验证回填摘要文件（如果存在）。回填摘要形成一个单独的验证链，并且独立于标准摘要进行处理。

## 验证结果
<a name="cloudtrail-log-file-validation-cli-results"></a>

验证结果从摘要头开始，采用以下格式：

```
Validating log files for trail trail_ARN  between time_stamp and time_stamp
```

主输出的每一行都包含以下格式的单个摘要或日志文件的验证结果。前缀为前缀的行`(backfill)`表示回填摘要文件，这些文件与标准摘要文件形成单独的验证链。

```
<optional (backfill)> <Digest file | Log file> <S3 path> <Validation Message>
```

下表描述了可能会出现的日志文件和摘要文件的验证消息。


****  

| 文件类型 | 验证消息 | 说明 | 
| --- | --- | --- | 
| Digest file | valid | 摘要文件签名有效。可以检查所引用的日志文件。仅详细模式包含此消息。 | 
| Digest file | INVALID: has been moved from its original location | 检索摘要文件的 S3 存储桶或 S3 对象与摘要文件中记录的 S3 存储桶或 S3 对象位置不匹配。 | 
| Digest file | INVALID: invalid format | 摘要文件格式无效。无法验证与摘要文件表示的时间范围对应的日志文件。 | 
| Digest file | INVALID: not found | 找不到摘要文件。无法验证与摘要文件表示的时间范围对应的日志文件。 | 
| Digest file | INVALID: public key not found for fingerprint fingerprint | 找不到与摘要文件中记录的指纹对应的公有密钥。无法验证摘要文件。 | 
| Digest file | INVALID: signature verification failed | 摘要文件签名无效。摘要文件无效，无法验证其引用的日志文件，也无法确定其中所列的 API 活动。 | 
| Digest file | INVALID: Unable to load PKCS \$11 key with fingerprint fingerprint | 无法加载含有指定指纹的 DER 编码公有密钥（PKCS \$11 格式），无法验证摘要文件。 | 
| Log file | valid | 日志文件已验证且自提交以来未发生过修改。仅详细模式包含此消息。 | 
| Log file | INVALID: hash value doesn't match | 日志文件的哈希值不匹配。日志文件自 CloudTrail 提交后发生过修改。 | 
| Log file | INVALID: invalid format | 日志文件格式无效。无法验证日志文件。 | 
| Log file | INVALID: not found | 找不到日志文件，无法验证。 | 

输出包含有关返回结果的摘要信息。

## 示例输出
<a name="cloudtrail-log-file-validation-cli-results-examples"></a>

### 详细
<a name="cloudtrail-log-file-validation-cli-results-verbose"></a>

下面的示例 `validate-logs` 命令使用 `--verbose` 标志并生成后面的示例输出。`[...]` 表示示例输出已省略。

```
aws cloudtrail validate-logs --trail-arn arn:aws:cloudtrail:us-east-2:111111111111:trail/example-trail-name --start-time 2015-08-31T22:00:00Z --end-time 2015-09-01T19:17:29Z --verbose
```

```
Validating log files for trail arn:aws:cloudtrail:us-east-2:111111111111:trail/example-trail-name between 2015-08-31T22:00:00Z and 2015-09-01T19:17:29Z
                                       
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2015/09/01/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T201728Z.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1925Z_WZZw1RymnjCRjxXc.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1915Z_POuvV87nu6pfAV2W.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1930Z_l2QgXhAKVm1QXiIA.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1920Z_eQJteBBrfpBCqOqw.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1950Z_9g5A6qlR2B5KaRdq.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1920Z_i4DNCC12BuXd6Ru7.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1915Z_Sg5caf2RH6Jdx0EJ.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2015/09/01/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T191728Z.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/09/01/111111111111_CloudTrail_us-east-2_20150901T1910Z_YYSFiuFQk4nrtnEW.json.gz	valid
[...]
Log file       s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail/us-east-2/2015/09/01/144218288521_CloudTrail_us-east-2_20150901T1055Z_0Sfy6m9f6iBzmoPF.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail/us-east-2/2015/09/01/144218288521_CloudTrail_us-east-2_20150901T1040Z_lLa3QzVLpOed7igR.json.gz	valid

Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2015/09/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T101728Z.json.gz	INVALID: signature verification failed

Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2015/09/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T091728Z.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail/us-east-2/2015/09/01/144218288521_CloudTrail_us-east-2_20150901T0830Z_eaFvO3dwHo4NCqqc.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2015/09/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T081728Z.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2015/09/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T071728Z.json.gz	valid
[...]
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/08/31/111111111111_CloudTrail_us-east-2_20150831T2245Z_mbJkEO5kNcDnVhGh.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/08/31/111111111111_CloudTrail_us-east-2_20150831T2225Z_IQ6kXy8sKU03RSPr.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/08/31/111111111111_CloudTrail_us-east-2_20150831T2230Z_eRPVRTxHQ5498ROA.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2015/08/31/111111111111_CloudTrail_us-east-2_20150831T2255Z_IlWawYZGvTWB5vYN.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2015/08/31/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150831T221728Z.json.gz	valid

Results requested for 2015-08-31T22:00:00Z to 2015-09-01T19:17:29Z
Results found for 2015-08-31T22:17:28Z to 2015-09-01T20:17:28Z:

22/23 digest files valid, 1/23 digest files INVALID
63/63 log files valid
```

以下示例`validate-logs`命令在存在回填摘要文件的时段上使用该`--verbose`标志，并生成以下示例输出。回填摘要以`(backfill)`前缀显示，并且与标准摘要链分开验证。 `[...]`表示样本输出已被缩短。

```
aws cloudtrail validate-logs --trail-arn arn:aws:cloudtrail:us-east-2:111111111111:trail/example-trail-name --start-time 2024-07-31T22:00:00Z --end-time 2024-08-01T19:17:29Z --verbose
```

```
Validating log files for trail arn:aws:cloudtrail:us-east-2:111111111111:trail/example-trail-name between 2024-07-31T22:00:00Z and 2024-08-01T19:17:29Z

Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2024/08/01/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T201728Z.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1925Z_Xm3pK9vN2wQ5rT8h.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1915Z_Bj7cL4nM6pR9sU2v.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1930Z_Fy1dG8kN3qT6wX0z.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1920Z_Hn5jM2pQ7sV9yB4e.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1950Z_Kp8rN1tW4xZ7aC3f.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1920Z_Mq6sP9uX2yB5dE8g.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1915Z_Rt4vQ7wZ0aC3fG6h.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2024/08/01/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T191728Z.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/08/01/111111111111_CloudTrail_us-east-2_20240801T1910Z_Uw9xR2yB5dH8jK1m.json.gz	valid
[...]
Log file       s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail/us-east-2/2024/08/01/144218288521_CloudTrail_us-east-2_20240801T1055Z_Vz3aS6cE9fL2nP5q.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail/us-east-2/2024/08/01/144218288521_CloudTrail_us-east-2_20240801T1040Z_Xy7bT0dG3hM6pR9s.json.gz	valid

Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T101728Z.json.gz	INVALID: signature verification failed

Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T091728Z.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T081728Z.json.gz	valid
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T071728Z.json.gz	valid
[...]
Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2024/07/31/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240731T221728Z.json.gz	valid
(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2024/08/01/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T201728Z_backfill.json.gz	valid
(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2024/08/01/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T191728Z_backfill.json.gz	valid
[...]

(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T101728Z_backfill.json.gz	INVALID: signature verification failed

(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T091728Z_backfill.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail/us-east-2/2024/08/01/144218288521_CloudTrail_us-east-2_20240801T0830Z_Rn6uk0wY5aD9fJ3n.json.gz	valid
(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T081728Z_backfill.json.gz	valid
(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T071728Z_backfill.json.gz	valid
[...]
(backfill) Digest file    s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail-Digest/us-east-2/2024/07/31/111111111111_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240731T221728Z_backfill.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/07/31/111111111111_CloudTrail_us-east-2_20240731T2145Z_Sp3vm7xZ2bE6gK0p.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/07/31/111111111111_CloudTrail_us-east-2_20240731T2125Z_Tq0wn4ya9cF3hL7q.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/07/31/111111111111_CloudTrail_us-east-2_20240731T2130Z_Ur7xp1zb6dG0jM4r.json.gz	valid
Log file       s3://amzn-s3-demo-bucket/AWSLogs/111111111111/CloudTrail/us-east-2/2024/07/31/111111111111_CloudTrail_us-east-2_20240731T2155Z_Vs4yq8ac3eH7kN1s.json.gz	valid

Results requested for 2024-07-31T22:00:00Z to 2024-08-01T19:17:29Z
Results found for 2024-07-31T22:17:28Z to 2024-08-01T20:17:28Z:

22/23 digest files valid, 1/23 digest files INVALID
22/23 backfill digest files valid, 1/23 backfill digest files INVALID
63/63 log files valid
```

### 非详细
<a name="cloudtrail-log-file-validation-cli-results-non-verbose"></a>

下面的示例 `validate-logs` 命令不使用 `--verbose` 标志。在后面的示例输出中，出现一个错误。只返回了头、错误和摘要信息。

```
aws cloudtrail validate-logs --trail-arn arn:aws:cloudtrail:us-east-2:111111111111:trail/example-trail-name --start-time 2015-08-31T22:00:00Z --end-time 2015-09-01T19:17:29Z
```

```
Validating log files for trail arn:aws:cloudtrail:us-east-2:111111111111:trail/example-trail-name between 2015-08-31T22:00:00Z and 2015-09-01T19:17:29Z

Digest file	s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2015/09/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150901T101728Z.json.gz	INVALID: signature verification failed

(backfill) Digest file	s3://amzn-s3-demo-bucket/AWSLogs/144218288521/CloudTrail-Digest/us-east-2/2024/08/01/144218288521_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20240801T101728Z_backfill.json.gz	INVALID: signature verification failed

Results requested for 2015-08-31T22:00:00Z to 2015-09-01T19:17:29Z
Results found for 2015-08-31T22:17:28Z to 2015-09-01T20:17:28Z:

22/23 digest files valid, 1/23 digest files INVALID
22/23 backfill digest files valid, 1/23 backfill digest files INVALID
63/63 log files valid
```

## 检查特定文件是否由 CloudTrail
<a name="cloudtrail-log-file-validation-cli-validate-logs-check-file"></a>

要检查存储桶中的特定文件是否由传送 CloudTrail，请在包含该文件的时间段内以详细模式运行`validate-logs`。如果文件出现在的输出中`validate-logs`，则该文件由传送 CloudTrail。

# CloudTrail 摘要文件结构
<a name="cloudtrail-log-file-validation-digest-file-structure"></a>

每个摘要文件均包含前一小时提交到您的 Amazon S3 存储桶的日志文件的名称，这些日志文件的哈希值，以及前一日志文件的数字签名。最新摘要文件的签名存储在摘要文件对象的元数据属性中。数字签名和哈希值用于验证日志文件和摘要文件本身的完整性。

## 摘要文件位置
<a name="cloudtrail-log-file-validation-digest-file-location"></a>

摘要文件提交到 Amazon S3 存储桶位置，语法如下。

```
s3://amzn-s3-demo-bucket/optional-prefix/AWSLogs/aws-account-id/CloudTrail-Digest/
    region/digest-end-year/digest-end-month/digest-end-date/
    aws-account-id_CloudTrail-Digest_region_trail-name_region_digest_end_timestamp.json.gz
```

**注意**  
对于组织跟踪记录，存储桶位置还包括组织单位 ID，如下所示：  

```
s3://amzn-s3-demo-bucket/optional-prefix/AWSLogs/O-ID/aws-account-id/CloudTrail-Digest/
    region/digest-end-year/digest-end-month/digest-end-date/
    aws-account-id_CloudTrail-Digest_region_trail-name_region_digest_end_timestamp.json.gz
```

**注意**  
如果 CloudTrail 需要包含由于处理延迟而未在原始摘要中引用的日志文件，则会交付回填摘要文件。回填摘要文件在其文件名中使用`_backfill`后缀，如下所示：  

```
s3://amzn-s3-demo-bucket/optional-prefix/AWSLogs/aws-account-id/CloudTrail-Digest/
    region/digest-end-year/digest-end-month/digest-end-date/
    aws-account-id_CloudTrail-Digest_region_trail-name_region_digest_end_timestamp_backfill.json.gz
```

## 示例摘要文件内容
<a name="cloudtrail-log-file-validation-digest-file-contents"></a>

以下示例摘要文件包含 CloudTrail 日志信息。

```
{
  "awsAccountId": "111122223333",
  "digestStartTime": "2015-08-17T14:01:31Z",
  "digestEndTime": "2015-08-17T15:01:31Z",
  "digestS3Bucket": "amzn-s3-demo-bucket",
  "digestS3Object": "AWSLogs/111122223333/CloudTrail-Digest/us-east-2/2015/08/17/111122223333_CloudTrail-Digest_us-east-2_your-trail-name_us-east-2_20150817T150131Z.json.gz",
  "digestPublicKeyFingerprint": "31e8b5433410dfb61a9dc45cc65b22ff",
  "digestSignatureAlgorithm": "SHA256withRSA",
  "newestEventTime": "2015-08-17T14:52:27Z",
  "oldestEventTime": "2015-08-17T14:42:27Z",
  "previousDigestS3Bucket": "amzn-s3-demo-bucket",
  "previousDigestS3Object": "AWSLogs/111122223333/CloudTrail-Digest/us-east-2/2015/08/17/111122223333_CloudTrail-Digest_us-east-2_your-trail-name_us-east-2_20150817T140131Z.json.gz",
  "previousDigestHashValue": "97fb791cf91ffc440d274f8190dbdd9aa09c34432aba82739df18b6d3c13df2d",
  "previousDigestHashAlgorithm": "SHA-256",
  "previousDigestSignature": "50887ccffad4c002b97caa37cc9dc626e3c680207d41d27fa5835458e066e0d3652fc4dfc30937e4d5f4cc7f796e7a258fb50a43ac427f2237f6e505d4efaf373d156e15e3b68dea9f58111d395b62628d6bd367a9024d2183b5c5f6e19466d3a996b92df705bc997b8a0e13430f241d733cf95df4e41bb6c304c3f58363043572ea57a27085639ce187e679c0d81c7519b1184fa77fb7ab0b0e40a32dace6e1eefc3995c5ae182da49b62b26398cebb52a2201a6387b75b89c83e5570bcb9bba6c34a80f2f00a1c6ebe07d1ff149eccd812dc805bb3eeff6657db32a6cb48d2d096404eb76181877bc6ebb8cd0b23f823200155b2fd8848d428e46e8456328a",
  "logFiles": [
    {
      "s3Bucket": "amzn-s3-demo-bucket",
      "s3Object": "AWSLogs/111122223333/CloudTrail/us-east-2/2015/08/17/111122223333_CloudTrail_us-east-2_20150817T1445Z_9nYN7gp2eWAJHIfT.json.gz",
      "hashValue": "9bb6196fc6b84d6f075a56548feca262bd99ba3c2de41b618e5b6e22c1fc71f6",
      "hashAlgorithm": "SHA-256",
      "newestEventTime": "2015-08-17T14:52:27Z",
      "oldestEventTime": "2015-08-17T14:42:27Z"
    }
  ]
}
```

## 摘要文件字段描述
<a name="cloudtrail-log-file-validation-digest-file-descriptions"></a>

下面是摘要文件中每个字段的描述：

`awsAccountId`  
已发送摘要文件的 AWS 账户 ID。

`digestStartTime`  
摘要文件所涵盖的起始 UTC 时间范围，以日志文件交付的时间为参考。 CloudTrail这意味着，如果时间范围是 [Ta, Tb]，则摘要包含在 Ta 与 Tb 之间提交给客户的所有日志文件。

`digestEndTime`  
摘要文件所涵盖的 UTC 结束时间范围，以日志文件交付的时间为参考。 CloudTrail这意味着，如果时间范围是 [Ta, Tb]，则摘要包含在 Ta 与 Tb 之间提交给客户的所有日志文件。

`digestS3Bucket`  
已将最新摘要文件传送到的 Amazon S3 存储桶的名称。

`digestS3Object`  
最新摘要文件的 Amazon S3 对象密钥（即，Amazon S3 存储桶位置）。字符串中的前两个区域显示摘要文件是从哪个区域提交的。最后的区域（`your-trail-name` 之后）是跟踪的主区域。主区域是创建跟踪的区域。如果是多区域跟踪，主区域可能不是提交摘要文件的区域。

`newestEventTime`  
摘要的日志文件中的所有事件中最近事件的 UTC 时间。

`oldestEventTime`  
摘要的日志文件中的所有事件中最早事件的 UTC 时间。  
如果摘要文件延迟提交，则 `oldestEventTime` 的值将早于 `digestStartTime` 的值。

`previousDigestS3Bucket`  
上一个摘要文件传送到的 Amazon S3 存储桶。

`previousDigestS3Object`  
上一个摘要文件的 Amazon S3 对象密钥（即，Amazon S3 存储桶位置）。

`previousDigestHashValue`  
前一摘要文件的未压缩内容的十六进制编码哈希值。

`previousDigestHashAlgorithm`  
用于对前一摘要文件进行哈希处理的哈希算法的名称。

`publicKeyFingerprint`  
与用于对摘要文件进行签名的私有密钥相匹配的公有密钥的十六进制编码指纹。您可以使用 AWS CLI 或 CloudTrail API 检索与摘要文件对应的时间范围内的公钥。对于返回的公有密钥，其指纹与此值匹配的公有密钥可用于验证摘要文件。有关检索摘要文件公钥的信息，请参阅 AWS CLI [https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/list-public-keys.html](https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/list-public-keys.html)命令或 CloudTrail [https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_ListPublicKeys.html](https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_ListPublicKeys.html)API。  
CloudTrail 每个区域使用不同的 private/public 密钥对。每个摘要文件都使用对其区域唯一的私钥进行签名。因此，当您验证来自特定区域的摘要文件时，必须从同一区域检索其相应公钥。

`digestSignatureAlgorithm`  
用于对摘要文件进行签名的算法。

`logFiles.s3Bucket`  
日志文件的 Amazon S3 存储桶的名称。

`logFiles.s3Object`  
最新日志文件的 Amazon S3 对象密钥。

`logFiles.newestEventTime`  
日志文件中最近事件的 UTC 时间。此时间还与日志文件本身的时间戳相对应。

`logFiles.oldestEventTime`  
日志文件中最早事件的 UTC 时间。

`logFiles.hashValue`  
未压缩日志文件内容的十六进制编码哈希值。

`logFiles.hashAlgorithm`  
用于对日志文件进行哈希处理的哈希算法。

## 启动摘要文件
<a name="cloudtrail-log-file-validation-digest-file-starting"></a>

启动日志文件完整性验证时，将生成一个启动摘要文件。重新启动日志文件完整性验证（通过禁用后重新启用日志文件完整性验证，或者通过停止记录然后在启用验证时重新启动记录）时，也将生成一个启动摘要文件。在启动摘要文件中，与前一摘要文件相关的以下字段将为空：
+ `previousDigestS3Bucket`
+ `previousDigestS3Object`
+ `previousDigestHashValue`
+ `previousDigestHashAlgorithm`
+ `previousDigestSignature`

## “空”摘要文件
<a name="cloudtrail-log-file-validation-digest-file-empty"></a>

CloudTrail 即使在摘要文件所代表的一小时内您的账户中没有 API 活动，也会提供摘要文件。如果需要确定在摘要文件报告的小时内未提交日志文件，这非常有用。

下面的示例说明当未出现 API 活动时记录了 1 小时的摘要文件的内容。注意，摘要文件内容最后的 `logFiles:[ ]` 字段为空。

```
{
  "awsAccountId": "111122223333",
  "digestStartTime": "2015-08-20T17:01:31Z",
  "digestEndTime": "2015-08-20T18:01:31Z",
  "digestS3Bucket": "amzn-s3-demo-bucket",
  "digestS3Object": "AWSLogs/111122223333/CloudTrail-Digest/us-east-2/2015/08/20/111122223333_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150820T180131Z.json.gz",
  "digestPublicKeyFingerprint": "31e8b5433410dfb61a9dc45cc65b22ff",
  "digestSignatureAlgorithm": "SHA256withRSA",
  "newestEventTime": null,
  "oldestEventTime": null,
  "previousDigestS3Bucket": "amzn-s3-demo-bucket",
  "previousDigestS3Object": "AWSLogs/111122223333/CloudTrail-Digest/us-east-2/2015/08/20/111122223333_CloudTrail-Digest_us-east-2_example-trail-name_us-east-2_20150820T170131Z.json.gz",
  "previousDigestHashValue": "ed96c4bac9eaa8fe9716ca0e515da51938be651b1db31d781956416a9d05cdfa",
  "previousDigestHashAlgorithm": "SHA-256",
  "previousDigestSignature": "82705525fb0fe7f919f9434e5b7138cb41793c776c7414f3520c0242902daa8cc8286b29263d2627f2f259471c745b1654af76e2073264b2510fd45236b3aea4d80c0e8e6455223d7bd54ff80af0edf22a5f14fa856626daec919f0591479aa4f213787ba1e1076328dcf8ff624e03a977fa5612dcf58594c590fd8c1c5b48bddf43fc84ecc00b41bedd0ff7f293c3e2de8dcdc78f98b03e17577f5822ba842399d69eb79921c0429773509520e08c8b518702d987dfbb3a4e5d8c5f17673ce1f989dfff82d4becf24e452f20d3bcac94ad50131f93e57f10155536acb54c60efbe9d57228c2b930bc6082b2318e3ccd36834a8e835b8d112dbf32145f445c11",
  "logFiles": []
}
```

## 摘要文件的签名
<a name="cloudtrail-log-file-validation-digest-file-signature"></a>

摘要文件的签名信息位于 Amazon S3 摘要文件对象的两个对象元数据属性中。每个摘要文件都有下面的元数据项：
+ `x-amz-meta-signature`

  摘要文件签名的十六进制编码值。下面是示例签名：

  ```
  3be472336fa2989ef34de1b3c1bf851f59eb030eaff3e2fb6600a082a23f4c6a82966565b994f9de4a5989d053d9d15d20fc5c43e66358652d93326550a4acc5c5f541bb52e9b455897ab723bd7cbabfe963a406a41d600f3658f7a3135e5ed9fcae7b79bb5857d1e5eb78fcce8595ce0ade2f3ad1d9f2d62be7bc4660d83166ce24586489b7da9ee9883eaf0b9efabb5dd3cbba565cc4aab5c9c46c9fa7e9cda310afcc5e8adcd9e48d0597ec5f8174a52c3bebb3e845eeb1d18904fbf4cc14cd117080098e10022ddf55e017a9431446acad8560de0ba1e477af9f8a3048bc6196350adad0cc0cb4ab99b5e7c9944437a3c674a038009220684ced7be07b4f
  28f1cc237f372264a51b611c01da429565def703539f4e71009051769469231bc22232fa260df02740047af532229885ea2b0e95ecd353326b7104941e0cbddb076a391f1fcf2923c19565f4841770a78723451aeb732ff1b6162dc40e601fc6720bc5325987942ebd817783b322f0ac77698523bf742fdea7aa44f4911b3101221b7e1233387f16a52077610498f4a1254211258e37da0fb4cb207aef593b4c1baa13674e85acc52046b3adb889e63331a66abac5de7e42ffdd6952987c31ae871650e130bd2e63bfe145b22bbd39ea192210f6df64d49b888a321e02d3fc4cf126accae30d2857ccd6b2286a7c9feba6c35c44161b24147d645e6ca26844ba
  05d3ffcb5d2dd5dc28f8bb5b7993938e8a5f912a82b448a367eccb2ec0f198ba71e23eb0b97278cf65f3c8d1e652c6de33a22ca8428821ffc95bf8b726ba9f37cfbc20c54dc5bd6159bdea1c4d951b68cb8e0528852c55bb0c5e499ea60560f7c2bb3af7f694407da863a2594f7a2f2838cb09254afbaf8003587746e719a0437f85eeffae534f283f3837eb939a9bccc3c71573500661245891051231b580ac92d9e0e68c6f47ad38975f493e2c40e7f303353c4adc7d563ef1e875977afac2e085f0c824045d998c9543d8a3293ad3c063b7a109d0bfd84b0b1e3f72c4f057e744e6a2cf9cc97727b08584f44bfa47799c5072b60f0b619aea88a17de585e9
  ```
+ `x-amz-meta-signature-algorithm`

  下面是用于生成摘要签名的算法示例值：

  `SHA256withRSA`
+ `x-amz-meta-backfill-generation-timestamp`

  生成回填摘要时的 UTC 时间戳。此元数据属性仅存在于回填摘要文件中，用于识别用于签名验证的正确公钥。以下是时间戳示例：

  `2025-05-20T00:00:00.000Z`

## 摘要文件链
<a name="cloudtrail-log-file-validation-digest-file-chaining"></a>

每个摘要文件都包含对其先前摘要文件的引用，这一事实实现了 “链接”，允许像这样的验证工具检测摘要文件是否已被删除。 AWS CLI 通过它，在指定时间范围内的摘要文件可以从最近开始往前连续检查。

**注意**  
禁用日志文件完整性验证后，摘要文件链将在一小时后中断。 CloudTrail 不会为在禁用日志文件完整性验证期间传送的日志文件创建摘要文件。例如，如果您在 1 月 1 日中午启用日志文件完整性验证，在 1 月 2 日中午禁用它，在 1 月 10 日中午再次启用它，则不会为从 1 月 2 日中午到 1 月 10 日中午之间提交的日志文件创建摘要文件。每当您停止 CloudTrail 记录或删除跟踪时，这同样适用。

如果您的跟踪的 [S3 存储桶策略](create-s3-bucket-policy-for-cloudtrail.md)配置错误或服务 CloudTrail 出现意外中断，则可能无法收到全部或部分摘要文件。要确认您的跟踪是否有任何摘要传输错误，请运行 [https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_GetTrailStatus.html](https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_GetTrailStatus.html) 命令并检查 `LatestDigestDeliveryError` 参数是否有错误。在交付问题得到解决（例如，通过修复存储桶策略）后， CloudTrail 将尝试重新传送任何丢失的摘要文件。在重新传输期间，摘要文件可能会乱序传输，因此链条可能会暂时显得中断。

如果日志记录停止或跟踪被删除， CloudTrail 将提供最终摘要文件。此摘要文件包含有关所有剩余日志文件的信息，所涵盖的事件直至（包含）`StopLogging` 事件。

# CloudTrail 日志文件完整性验证的自定义实现
<a name="cloudtrail-log-file-custom-validation"></a>

由于 CloudTrail 使用行业标准、公开可用的加密算法和哈希函数，因此您可以创建自己的工具来验证 CloudTrail 日志文件的完整性。启用日志文件完整性验证后，会将摘要文件 CloudTrail 传送到您的 Amazon S3 存储桶。您可以使用这些文件实现自己的验证解决方案。有关摘要文件的更多信息，请参阅[CloudTrail 摘要文件结构](cloudtrail-log-file-validation-digest-file-structure.md)。

本主题介绍如何为摘要文件签名，然后详述了要对摘要文件及其引用的日志文件实现验证解决方案所需采取的步骤。

## 了解 CloudTrail 摘要文件的签名方式
<a name="cloudtrail-log-file-custom-validation-how-cloudtrail-digest-files-are-signed"></a>

CloudTrail 摘要文件使用 RSA 数字签名进行签名。对于每个摘要文件， CloudTrail 执行以下操作：

1. 创建一个字符串，以基于指定的摘要文件字段进行数据签名（在下一章节中讲解）。

1. 获取区域唯一的私钥。

1. 将此字符串的 SHA-256 哈希值和私钥传递给 RSA 签名算法（生成数字签名）。

1. 将签名的字节代码编码成十六进制格式。

1. 将此数字签名放入 Amazon S3 摘要文件对象的 `x-amz-meta-signature` 元数据属性中。

### 数据签名字符串的内容
<a name="cloudtrail-log-file-custom-validation-data-signing-string-summary"></a>

用于数据签名的字符串中包含以下 CloudTrail 对象：
+ UTC 扩展格式的摘要文件结束时间戳（如 `2015-05-08T07:19:37Z`）
+ 当前摘要文件的 S3 路径
+ 当前摘要文件的 SHA-256 哈希值（十六进制编码）
+ 之前摘要文件的十六进制编码签名

本文档的稍后部分提供了计算此字符串的格式和作为示例的字符串。

## 自定义验证实现步骤
<a name="cloudtrail-log-file-custom-validation-steps"></a>

实现自定义验证解决方案时，您需要先验证摘要文件，然后再验证其引用的日志文件。

### 验证摘要文件
<a name="cloudtrail-log-file-custom-validation-steps-digest"></a>

要验证摘要文件，您需要其签名、与用于对其进行签名的私钥对应的公钥以及您计算的数据签名字符串。

1. 获取摘要文件。

1. 验证已从摘要文件的原始位置检索了摘要文件。

1. 获取摘要文件的十六进制编码签名。

1. 获取与用于对摘要文件进行签名的私钥对应的公钥的十六进制编码指纹。

1. 检索与摘要文件对应的时间范围的公钥。

1. 从检索到的公钥中，选择指纹与摘要文件中的指纹匹配的公钥。

1. 使用摘要文件哈希值及其他摘要文件字段，重新创建用于验证摘要文件签名的数据签名字符串。

1. 将此字符串的 SHA-256 哈希值、公钥及签名作为参数传递给 RSA 签名验证算法，以验证签名。如果结果为 true，则摘要文件有效。

### 验证日志文件
<a name="cloudtrail-log-file-custom-validation-steps-logs"></a>

如果摘要文件有效，则验证摘要文件引用的每个日志文件。

1. 为验证日志文件的完整性，系统会计算未压缩内容的 SHA-256 哈希值并将结果与摘要中记录的十六进制日志文件哈希值进行比较。如果哈希值匹配，则日志文件有效。

1. 通过使用当前摘要文件中包含的有关前一个摘要文件的信息，连续验证前一个摘要文件及其对应的日志文件。

以下部分详细介绍了这些步骤。

### A. 获取摘要文件
<a name="cloudtrail-log-file-custom-validation-steps-get-the-digest-file"></a>

第一步是获取最新的摘要文件，验证您已从其来源位置检索到它，然后验证其数字签名并获取公钥的指纹。

1. 使用 S3 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) 或 AmazonS3Client 类（举例）从 Amazon S3 存储桶获取需要验证的时间范围的最新摘要文件。

1. 检查用于检索此文件的 S3 存储桶和 S3 对象是否与摘要文件中记录的 S3 存储桶 S3 对象位置匹配。

1. 接下来，从 Amazon S3 中摘要文件对象的 `x-amz-meta-signature` 元数据属性获取摘要文件的数字签名。

1. In the digest file, get the fingerprint of the public key whose private key was used to sign the digest file from the `digestPublicKeyFingerprint` field。

### B. 检索用于验证摘要文件的公钥
<a name="cloudtrail-log-file-custom-validation-steps-retrieve-public-key"></a>

要获取用于验证摘要文件的公钥，您可以使用 AWS CLI 或 CloudTrail API。在这两种情况下，您都需要指定要验证的摘要文件的时间范围（即，起始时间和结束时间）。对于您指定的时间范围，可能会返回一个或多个公钥。返回的密钥的有效时间范围可能会发生重叠。

**注意**  
由于每个区域 CloudTrail 使用不同的 private/public 密钥对，因此每个摘要文件都使用其区域独有的私钥进行签名。因此，当您验证来自特定区域的摘要文件时，必须从同一区域检索其公钥。

#### 使用检 AWS CLI 索公钥
<a name="cloudtrail-log-file-custom-validation-steps-retrieve-public-key-cli"></a>

要使用检索摘要文件的公钥 AWS CLI，请使用`cloudtrail list-public-keys`命令。此命令采用以下格式：

 `aws cloudtrail list-public-keys [--start-time <start-time>] [--end-time <end-time>]` 

start-time 和 end-time 参数为 UTC 时间戳且是可选的。如果未指定，则使用当前时间，且返回当前有效的一个或多个公钥。

 **示例响应** 

响应是代表所返回的一个或多个密钥的 JSON 对象的列表：

```
{
    "publicKeyList": [
        {
            "ValidityStartTime": "1436317441.0",
            "ValidityEndTime": "1438909441.0",
            "Value": "MIIBCgKCAQEAn11L2YZ9h7onug2ILi1MWyHiMRsTQjfWE+pHVRLk1QjfWhirG+lpOa8NrwQ/r7Ah5bNL6HepznOU9XTDSfmmnP97mqyc7z/upfZdS/AHhYcGaz7n6Wc/RRBU6VmiPCrAUojuSk6/GjvA8iOPFsYDuBtviXarvuLPlrT9kAd4Lb+rFfR5peEgBEkhlzc5HuWO7S0y+KunqxX6jQBnXGMtxmPBPP0FylgWGNdFtks/4YSKcgqwH0YDcawP9GGGDAeCIqPWIXDLG1jOjRRzWfCmD0iJUkz8vTsn4hq/5ZxRFE7UBAUiVcGbdnDdvVfhF9C3dQiDq3k7adQIziLT0cShgQIDAQAB",
            "Fingerprint": "8eba5db5bea9b640d1c96a77256fe7f2"
        },
        {
            "ValidityStartTime": "1434589460.0",
            "ValidityEndTime": "1437181460.0",
            "Value": "MIIBCgKCAQEApfYL2FiZhpN74LNWVUzhR+VheYhwhYm8w0n5Gf6i95ylW5kBAWKVEmnAQG7BvS5g9SMqFDQx52fW7NWV44IvfJ2xGXT+wT+DgR6ZQ+6yxskQNqV5YcXj4Aa5Zz4jJfsYjDuO2MDTZNIzNvBNzaBJ+r2WIWAJ/Xq54kyF63B6WE38vKuDE7nSd1FqQuEoNBFLPInvgggYe2Ym1Refe2z71wNcJ2kY+q0h1BSHrSM8RWuJIw7MXwF9iQncg9jYzUlNJomozQzAG5wSRfbplcCYNY40xvGd/aAmO0m+Y+XFMrKwtLCwseHPvj843qVno6x4BJN9bpWnoPo9sdsbGoiK3QIDAQAB",
            "Fingerprint": "8933b39ddc64d26d8e14ffbf6566fee4"
        },
        {
            "ValidityStartTime": "1434589370.0",
            "ValidityEndTime": "1437181370.0",
            "Value": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqlzPJbvZJ42UdcmLfPUqXYNfOs6I8lCfao/tOs8CmzPOEdtLWugB9xoIUz78qVHdKIqxbaG4jWHfJBiOSSFBM0lt8cdVo4TnRa7oG9io5pysS6DJhBBAeXsicufsiFJR+wrUNh8RSLxL4k6G1+BhLX20tJkZ/erT97tDGBujAelqseGg3vPZbTx9SMfOLN65PdLFudLP7Gat0Z9p5jw/rjpclKfo9Bfc3heeBxWGKwBBOKnFAaN9V57pOaosCvPKmHd9bg7jsQkI9Xp22IzGLsTFJZYVA3KiTAElDMu80iFXPHEq9hKNbt9e4URFam+1utKVEiLkR2disdCmPTK0VQIDAQAB",
            "Fingerprint": "31e8b5433410dfb61a9dc45cc65b22ff"
        }
    ]
}
```

#### 使用 CloudTrail API 检索公钥
<a name="cloudtrail-log-file-custom-validation-steps-retrieve-public-key-api"></a>

要使用 CloudTrail API 检索摘要文件的公钥，请将开始时间和结束时间值传递给 `ListPublicKeys` API。`ListPublicKeys` API 返回与用于在指定时间范围对摘要文件进行签名的私钥对应的公钥。对于每个公钥，此 API 还返回相应的指纹。

##### `ListPublicKeys`
<a name="cloudtrail-log-file-custom-validation-steps-list-public-keys"></a>

本部分介绍 `ListPublicKeys` API 的请求参数和响应元素。

**注意**  
`ListPublicKeys` 的二进制字段的编码可能随时发生变化。

 **请求参数** 


****  

| Name | 说明 | 
| --- | --- | 
|  StartTime  |  （可选）以 UTC 为单位指定查找 CloudTrail 摘要文件公钥的时间范围的起始时间。如果 StartTime 未指定，则使用当前时间，并返回当前的公钥。 类型： DateTime   | 
|  EndTime  |  （可选）以 UTC 为单位指定查找 CloudTrail 摘要文件公钥的时间范围的结束时间。如果 EndTime 未指定，则使用当前时间。 类型： DateTime   | 

 **响应元素** 

`PublicKeyList`：`PublicKey` 对象数组，包含：


****  

|  |  | 
| --- |--- |
|  名称  |  描述  | 
|  Value  |  DER 编码的公钥值（采用 PKCS \$11 格式）。 类型：Blob   | 
|  ValidityStartTime  |  公钥有效的起始时间。 类型： DateTime   | 
|  ValidityEndTime  |  公钥有效的结束时间。 类型： DateTime   | 
|  Fingerprint  |  公钥的指纹。指纹可用于识别验证摘要文件所必需的公钥。 类型：字符串   | 

### C. 选择要用于验证的公钥
<a name="cloudtrail-log-file-custom-validation-steps-choose-public-key"></a>

从 `list-public-keys` 或 `ListPublicKeys` 返回的公钥中，选择指纹与摘要文件的 `digestPublicKeyFingerprint` 字段中记录的指纹匹配的公钥。此即为用于验证摘要文件的公钥。

### D. 重新创建数据签名字符串
<a name="cloudtrail-log-file-custom-validation-steps-recreate-data-signing-string"></a>

现在，您有了摘要文件的签名及关联公钥，接下来，您需要计算数据签名字符串。算出数据签名字符串后，您就有了验证签名所需的输入。

数据签名字符串采用以下格式：

```
Data_To_Sign_String = 
  Digest_End_Timestamp_in_UTC_Extended_format + '\n' +
  Current_Digest_File_S3_Path + '\n' +
  Hex(Sha256(current-digest-file-content)) + '\n' +
  Previous_digest_signature_in_hex
```

之后是示例 `Data_To_Sign_String`。

```
2015-08-12T04:01:31Z
amzn-s3-demo-bucket/AWSLogs/111122223333/CloudTrail-Digest/us-east-2/2015/08/12/111122223333_us-east-2_CloudTrail-Digest_us-east-2_20150812T040131Z.json.gz
4ff08d7c6ecd6eb313257e839645d20363ee3784a2328a7d76b99b53cc9bcacd
6e8540b83c3ac86a0312d971a225361d28ed0af20d70c211a2d405e32abf529a8145c2966e3bb47362383a52441545ed091fb81
d4c7c09dd152b84e79099ce7a9ec35d2b264eb92eb6e090f1e5ec5d40ec8a0729c02ff57f9e30d5343a8591638f8b794972ce15bb3063a01972
98b0aee2c1c8af74ec620261529265e83a9834ebef6054979d3e9a6767dfa6fdb4ae153436c567d6ae208f988047ccfc8e5e41f7d0121e54ed66b1b904f80fb2ce304458a2a6b91685b699434b946c52589e9438f8ebe5a0d80522b2f043b3710b87d2cda43e5c1e0db921d8d540b9ad5f6d4$31b1f4a8ef2d758424329583897339493a082bb36e782143ee5464b4e3eb4ef6
```

重新创建此字符串后，您即可验证摘要文件。

### E. 验证摘要文件
<a name="cloudtrail-log-file-custom-validation-steps-validate-digest-file"></a>

将重新创建的数据签名字符串的 SHA-256 哈希值、数字签名和公钥传给 RSA 签名验证算法。如果输出为 true，则已验证摘要文件签名，且摘要文件有效。

### F. 验证日志文件
<a name="cloudtrail-log-file-custom-validation-steps-validate-log-files"></a>

验证摘要文件后，您可以验证其引用的日志文件。摘要文件包含日志文件的 SHA-256 哈希值。如果其中一个日志文件在 CloudTrail 交付后被修改，则 SHA-256 哈希值将发生变化，并且摘要文件的签名将不匹配。

下面的内容介绍如何验证日志文件：

1. 使用摘要文件的 `logFiles.s3Bucket` 和 `logFiles.s3Object` 字段中的 S3 位置信息对日志文件执行 `S3 Get` 操作。

1. 如果 `S3 Get` 操作成功，则按照以下步骤循环访问摘要文件的 logFiles 数组中列出的日志文件：

   1. 从摘要文件中相应日志的 `logFiles.hashValue` 字段检索文件的原始哈希值。

   1. 使用 `logFiles.hashAlgorithm` 中指定的哈希算法计算未压缩的日志文件内容的哈希值。

   1. 比较您生成的哈希值和摘要文件中日志的哈希值。如果哈希值匹配，则日志文件有效。

### G. 验证其他摘要和日志文件
<a name="cloudtrail-log-file-custom-validation-steps-validate-additional-files"></a>

在每个摘要文件中，以下字段提供前一个摘要文件的位置和签名：
+  `previousDigestS3Bucket` 
+  `previousDigestS3Object` 
+  `previousDigestSignature` 

使用此信息顺序访问之前的摘要文件，按照前述部分中的步骤验证每个摘要文件的签名及其引用的日志文件。唯一的区别在于：对于之前的摘要文件，您不需要从摘要文件对象的 Amazon S3 元数据属性检索数字签名。`previousDigestSignature` 字段提供了前一个摘要文件的签名。

您可以一直向前进行此操作，直到到达起始的摘要文件，或摘要文件链断开，以先到者为准。

## 离线验证摘要和日志文件
<a name="cloudtrail-log-file-custom-validation-offline"></a>

离线验证摘要和日志文件时，您通常可以按照前述部分中介绍的流程进行。但是，您必须考虑到以下方面：

### 处理最新的摘要文件
<a name="cloudtrail-log-file-custom-validation-offline-most-recent-digest"></a>

最新（即“当前”）摘要文件的数字签名位于摘要文件对象的 Amazon S3 元数据属性中。在离线情况下，当前摘要文件的数字签名不可用。

对于这种情况，有两种处理方式：
+ 由于前一个摘要文件的数字签名位于当前摘要文件中，因此请从 next-to-last摘要文件开始验证。使用这种方法时，不会验证最新的摘要文件。
+ 作为预备步骤，从摘要文件对象的元数据属性获取当前摘要文件的签名，然后将其安全地离线存储。这样，除了链中前面的文件，此最新的摘要文件也可得到验证。

### 路径解决方案
<a name="cloudtrail-log-file-custom-validation-offline-path-resolution"></a>

已下载的摘要文件中的字段（如 `s3Object` 和 `previousDigestS3Object`）仍将指向日志文件和摘要文件的 Amazon S3 在线位置。离线解决方案必须找到一种方法，将它们重新路由到已下载的日志和摘要文件的当前路径。

### 公钥
<a name="cloudtrail-log-file-custom-validation-offline-public-keys"></a>

要进行离线验证，首先必须在线获取验证给定时间范围内的日志文件所需的所有公钥（例如，通过调用 `ListPublicKeys` 实现），然后将它们安全地离线存储。每当您需要验证超出指定的初始时间范围的其他文件时，都必须重复执行这一步。

## 示例验证代码段
<a name="cloudtrail-log-file-custom-validation-sample-code"></a>

以下示例片段提供了用于验证 CloudTrail 摘要和日志文件的基本代码。基本代码是 online/offline 不可知论的；也就是说，由你来决定是否在联机连接的情况下实现它。 AWS建议在实现中使用 [Java Cryptography Extension（JCE）](https://en.wikipedia.org/wiki/Java_Cryptography_Extension)和 [Bouncy Castle](https://www.bouncycastle.org/) 作为安全提供程序。

示例代码段：
+ 如何创建用于验证摘要文件签名的数据签名字符串。
+ 如何验证摘要文件签名。
+ 如何验证日志文件哈希值。
+ 用于验证摘要文件链的代码结构。

```
import java.util.Arrays;
import java.security.MessageDigest;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import org.json.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.apache.commons.codec.binary.Hex;

public class DigestFileValidator {

    public void validateDigestFile(String digestS3Bucket, String digestS3Object, String digestSignature) {
 
        // Using the Bouncy Castle provider as a JCE security provider - http://www.bouncycastle.org/
        Security.addProvider(new BouncyCastleProvider());
 
        // Load the digest file from S3 (using Amazon S3 Client) or from your local copy
        JSONObject digestFile = loadDigestFileInMemory(digestS3Bucket, digestS3Object);
 
        // Check that the digest file has been retrieved from its original location
        if (!digestFile.getString("digestS3Bucket").equals(digestS3Bucket) ||
                !digestFile.getString("digestS3Object").equals(digestS3Object)) {
            System.err.println("Digest file has been moved from its original location.");
        } else {
            // Compute digest file hash
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(convertToByteArray(digestFile));
            byte[] digestFileHash = messageDigest.digest();
            messageDigest.reset();
 
            // Compute the data to sign
            String dataToSign = String.format("%s%n%s/%s%n%s%n%s",
                                digestFile.getString("digestEndTime"),
                                digestFile.getString("digestS3Bucket"), digestFile.getString("digestS3Object"), // Constructing the S3 path of the digest file as part of the data to sign
                                Hex.encodeHexString(digestFileHash),
                                digestFile.getString("previousDigestSignature"));
 
            byte[] signatureContent = Hex.decodeHex(digestSignature);
 
            /*
                NOTE: 
                To find the right public key to verify the signature, call CloudTrail ListPublicKey API to get a list 
                of public keys, then match by the publicKeyFingerprint in the digest file. Also, the public key bytes 
                returned from ListPublicKey API are DER encoded in PKCS#1 format:
 
                PublicKeyInfo ::= SEQUENCE {
                    algorithm       AlgorithmIdentifier,
                    PublicKey       BIT STRING
                }
 
                AlgorithmIdentifier ::= SEQUENCE {
                    algorithm       OBJECT IDENTIFIER,
                    parameters      ANY DEFINED BY algorithm OPTIONAL
                }                
            */
            pkcs1PublicKeyBytes = getPublicKey(digestFile.getString("digestPublicKeyFingerprint")));
 
            // Transform the PKCS#1 formatted public key to x.509 format.
            RSAPublicKey rsaPublicKey = RSAPublicKey.getInstance(pkcs1PublicKeyBytes);
            AlgorithmIdentifier rsaEncryption = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null);
            SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(rsaEncryption, rsaPublicKey);
 
            // Create the PublicKey object needed for the signature validation
            PublicKey publicKey = KeyFactory.getInstance("RSA", "BC").generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded()));
 
            // Verify signature
            Signature signature = Signature.getInstance("SHA256withRSA", "BC");
            signature.initVerify(publicKey);
            signature.update(dataToSign.getBytes("UTF-8"));
 
            if (signature.verify(signatureContent)) {
                System.out.println("Digest file signature is valid, validating log files…");
                for (int i = 0; i < digestFile.getJSONArray("logFiles").length(); i++) {
 
                    JSONObject logFileMetadata = digestFile.getJSONArray("logFiles").getJSONObject(i);
 
                    // Compute log file hash
                    byte[] logFileContent = loadUncompressedLogFileInMemory(
                                                logFileMetadata.getString("s3Bucket"),
                                                logFileMetadata.getString("s3Object")
                                            );
                    messageDigest.update(logFileContent);
                     byte[] logFileHash = messageDigest.digest();
                    messageDigest.reset();
 
                    // Retrieve expected hash for the log file being processed
                    byte[] expectedHash = Hex.decodeHex(logFileMetadata.getString("hashValue"));
 
                    boolean signaturesMatch = Arrays.equals(expectedHash, logFileHash);
                    if (!signaturesMatch) {
                        System.err.println(String.format("Log file: %s/%s hash doesn't match.\tExpected: %s Actual: %s",
                               logFileMetadata.getString("s3Bucket"), logFileMetadata.getString("s3Object"),
                               Hex.encodeHexString(expectedHash), Hex.encodeHexString(logFileHash)));
                    } else {
                        System.out.println(String.format("Log file: %s/%s hash match",
                               logFileMetadata.getString("s3Bucket"), logFileMetadata.getString("s3Object")));
                    }
                }
 
            } else {
                System.err.println("Digest signature failed validation.");
            }
 
            System.out.println("Digest file validation completed.");
 
            if (chainValidationIsEnabled()) {
                // This enables the digests' chain validation
                validateDigestFile(
                        digestFile.getString("previousDigestS3Bucket"),
                        digestFile.getString("previousDigestS3Object"),
                        digestFile.getString("previousDigestSignature"));
            }
        }
    }
}
```