

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# CloudTrail Lake の保存されたクエリ結果を検証する
<a name="cloudtrail-query-results-validation"></a>

CloudTrail がクエリ結果を配信した後、クエリ結果が変更、削除、または変更されなかったかどうかを判断するには、CloudTrail クエリ結果の整合性の検証を使用することができます。この機能は、業界標準のアルゴリズムを使用して構築されています。ハッシュ用の SHA-256 とデジタル署名用の RSA を備えた SHA-256。これにより、CloudTrail クエリ結果ファイルを検出せずに変更、削除、または偽造することは計算上実行不可能になります。コマンドラインを使用してクエリ結果ファイルを検証できます。

## 使用する理由
<a name="cloudtrail-query-results-validation-use-cases"></a>

検証されたクエリ結果ファイルは、セキュリティおよびフォレンシック調査で非常に重要です。たとえば、検証済みのクエリ結果ファイルを使用することで、クエリ結果ファイル自体が変更されていないことを明確に主張できます。CloudTrail クエリ結果ファイルの整合性の検証プロセスでは、クエリ結果ファイルが削除または変更されたかどうかを知ることもできます。

**Topics**
+ [使用する理由](#cloudtrail-query-results-validation-use-cases)
+ [を使用して保存されたクエリ結果を検証する AWS CLI](#cloudtrail-query-results-validation-cli)
+ [CloudTrail 署名ファイル構造](#cloudtrail-results-file-validation-sign-file-structure)
+ [CloudTrail のクエリファイルの整合性検証のカスタム実装](#cloudtrail-results-file-custom-validation)

## を使用して保存されたクエリ結果を検証する AWS CLI
<a name="cloudtrail-query-results-validation-cli"></a>

[https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/verify-query-results.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/verify-query-results.html) コマンドを使用して、クエリ結果ファイルの整合性を検証してファイルに署名できます。

### 前提条件
<a name="cloudtrail-query-results-validation-cli-prerequisites"></a>

コマンドラインを使用してクエリ結果の整合性を検証するには、次の条件を満たしている必要があります。
+ へのオンライン接続が必要です AWS。
+  AWS CLI バージョン 2 を使用する必要があります。
+ ローカルでクエリ結果ファイルを検証してファイルに署名する場合、次の条件が適用されます。
  + 指定したファイルパスにクエリ結果ファイルと署名ファイルを配置する必要があります。**--local-export-path** パラメータの値としてファイルパスを指定します。
  + クエリ結果ファイルと署名ファイルの名前は変更しません。
+ S3 バケットでクエリ結果ファイルを検証してファイルに署名する場合、次の条件が適用されます。
  + クエリ結果ファイルと署名ファイルの名前は変更しません。
  + クエリ結果ファイルの署名ファイルを含む Amazon S3 バケットへの読み取りアクセスが必要です。
  + 指定した S3 プレフィックスには、クエリ結果ファイルと署名ファイルが含まれている必要があります。**--s3-prefix** パラメータの値として S3 プレフィックスを指定します。

### verify-query-results
<a name="cloudtrail-query-results-validation-cli-command"></a>

 **verify-query-results** コマンドは、各クエリ結果ファイルのハッシュ値を署名ファイル内の `fileHashValue` と比較し、署名ファイルの `hashSignature` を検証することによってクエリ結果ファイルのハッシュ値を検証します。

クエリ結果を検証する場合、**--s3-bucket** および **--s3-prefix** のいずれかのコマンドラインオプションを使用して S3 バケットに保存されているクエリ結果ファイルと署名ファイルを検証するか、**--local-export-path** コマンドラインオプションを使用して、ダウンロードしたクエリ結果ファイルと署名ファイルのローカル検証を実行することができます。

**注記**  
**verify-query-results** コマンドはリージョン固有です。特定の のクエリ結果を検証するには、**--region**グローバルオプションを指定する必要があります AWS リージョン。

**verify-query-results** コマンドのオプションを以下に示します。

**--s3-bucket** {{<文字列>}}  
クエリ結果ファイルと署名ファイルを保存する S3 バケット名を指定します。このパラメータは **--local-export-path** と共に使用できません。

**--s3-prefix** {{<文字列>}}  
クエリ結果ファイルと署名ファイルを含む S3 フォルダーの S3 パスを指定します (`s3/path/` など)。このパラメータは **--local-export-path** と共に使用できません。ファイルが S3 バケットのルートディレクトリにある場合は、このパラメータを指定する必要はありません。

**--local-export-path** {{<文字列>}}  
クエリ結果ファイルと署名ファイルを含むローカルディレクトリを指定します (`/local/path/to/export/file/` など)。このパラメータは **--s3-bucket** や **--s3-prefix** と共に使用できません。

#### 例
<a name="cloudtrail-query-results-validation-cli-examples"></a>

次の例では、**--s3-bucket** および **--s3-prefix** コマンドラインオプションを使用してクエリ結果を検証し、クエリ結果ファイルと署名ファイルを含む S3 バケット名とプレフィックスを指定します。

```
aws cloudtrail verify-query-results --s3-bucket {{amzn-s3-demo-bucket}} --s3-prefix {{prefix}} --region {{region}}
```

次の例では、**--local-export-path** コマンドラインオプションを使用して、ダウンロードしたクエリ結果を検証し、クエリ結果ファイルと署名ファイルのローカルパスを指定します。クエリ結果ファイルのダウンロードの詳細については、「[CloudTrail Lake の保存済みクエリ結果をダウンロードする](view-download-cloudtrail-lake-query-results.md#cloudtrail-download-lake-query-results)」を参照してください。

```
aws cloudtrail verify-query-results --local-export-path {{local_file_path}} --region {{region}}
```

#### 検証結果
<a name="cloudtrail-query-results-validation-cli-command-messages"></a>

次の表は、クエリ結果ファイルと書名ファイルの検証メッセージを示しています。


****  

| ファイルタイプ | 検証メッセージ | 説明 | 
| --- | --- | --- | 
| Sign file | Successfully validated sign and query result files | 書名ファイルの署名は有効です。参照しているクエリ結果ファイルを確認できます。 | 
| Query result file | `ValidationError: "File {{file_name}} has inconsistent hash value with hash value recorded in sign file, hash value in sign file is {{expected_hash}}, but get {{computed_hash}}` | クエリ結果ファイルのハッシュ値が署名ファイルの fileHashValue と一致しなかったため、検証に失敗しました。 | 
| Sign file | `ValidationError: Invalid signature in sign file` | 署名が無効なため、署名ファイルの検証に失敗しました。 | 

## CloudTrail 署名ファイル構造
<a name="cloudtrail-results-file-validation-sign-file-structure"></a>

署名ファイルには、クエリ結果を保存したときに Amazon S3 バケットに送られた各クエリ結果ファイルの名前、各クエリ結果ファイルのハッシュ値、ファイルのデジタル署名が含まれます。デジタル署名とハッシュ値は、クエリ結果ファイルおよび署名ファイル自体の整合性を検証するために使用されます。

### 署名ファイルの場所
<a name="cloudtrail-results-file-validation-sign-file-location"></a>

署名ファイルは、次の構文で表される Amazon S3 バケットの場所に送られます。

```
s3://{{amzn-s3-demo-bucket}}/{{optional-prefix/}}AWSLogs/{{aws-account-ID}}/CloudTrail-Lake/Query/{{year}}/{{month}}/{{date}}/{{query-ID}}/result_sign.json
```

### 署名ファイルのコンテンツの例
<a name="cloudtrail-results-file-validation-sign-file-contents"></a>

次に示すのは、CloudTrail Lake クエリ結果の情報が含まれる署名ファイルの例です。

```
{
  "version": "1.0",
  "region": "us-east-1",
  "files": [
    {
      "fileHashValue" : "de85a48b8a363033c891abd723181243620a3af3b6505f0a44db77e147e9c188",
      "fileName" : "result_1.csv.gz"
    }
  ],
  "hashAlgorithm" : "SHA-256",
  "signatureAlgorithm" : "SHA256withRSA",
  "queryCompleteTime": "2022-05-10T22:06:30Z",
  "hashSignature" : "7664652aaf1d5a17a12ba50abe6aca77c0ec76264bdf7dce71ac6d1c7781117c2a412e5820bccf473b1361306dff648feae20083ad3a27c6118172a81635829bdc7f7b795ebfabeb5259423b2fb2daa7d1d02f55791efa403dac553171e7ce5f9307d13e92eeec505da41685b4102c71ec5f1089168dacde702c8d39fed2f25e9216be5c49769b9db51037cb70a84b5712e1dffb005a74580c7fdcbb89a16b9b7674e327de4f5414701a772773a4c98eb008cca34228e294169901c735221e34cc643ead34628aabf1ba2c32e0cdf28ef403e8fe3772499ac61e21b70802dfddded9bea0ddfc3a021bf2a0b209f312ccee5a43f2b06aa35cac34638f7611e5d7",
  "publicKeyFingerprint" : "67b9fa73676d86966b449dd677850753"
}
```

### 署名ファイルのフィールドの説明
<a name="cloudtrail-results-file-validation-sign-file-descriptions"></a>

以下では、署名ファイルの各フィールドについて説明します。

`version`  
署名ファイルのバージョン。

`region`  
クエリ結果の保存に使用される AWS アカウントのリージョン。

`files.fileHashValue`  
圧縮されたクエリ結果ファイルの内容の 16 進エンコードされたハッシュ値です。

`files.fileName`  
クエリ結果ファイルの名前。

`hashAlgorithm`  
クエリ結果ファイルのハッシュ計算に使用されたハッシュアルゴリズムです。

`signatureAlgorithm`  
ファイルの署名に使用されるアルゴリズムです。

`queryCompleteTime`  
CloudTrail がいつクエリ結果を S3 バケットに配信したかを示します。この値を使用してパブリックキーを検索できます。

`hashSignature`  
ファイルのハッシュ署名。

`publicKeyFingerprint`  
このファイルの署名に使用されたパブリックキーの 16 進エンコードされたフィンガープリントです。

## CloudTrail のクエリファイルの整合性検証のカスタム実装
<a name="cloudtrail-results-file-custom-validation"></a>

CloudTrail では、オープンで提供されている業界標準の暗号化アルゴリズムとハッシュ関数が使用されるため、CloudTrail クエリ結果ファイルの整合性を検証するために独自のツールを作成することができます。Amazon S3 バケットにクエリ結果を保存すると、CloudTrail は署名ファイルを S3 バケットに送信します。独自の検証ソリューションを実装して、署名ファイルとクエリ結果ファイルを検証できます。署名ファイルの詳細については、「[CloudTrail 署名ファイル構造](#cloudtrail-results-file-validation-sign-file-structure)」を参照してください。

このトピックでは、署名ファイルの署名方法について説明し、署名ファイルと、署名ファイルによって参照される署名ファイルを検証するソリューションの実装に必要な手順を詳しく示します。

### CloudTrail 署名ファイルの署名の方法を理解する
<a name="cloudtrail-results-file-custom-validation-how-cloudtrail-sign-files-are-signed"></a>

CloudTrail 署名ファイルは RSA デジタル署名で署名されます。CloudTrail は各署名ファイルを次のように処理します。

1. 各クエリ結果ファイルのハッシュ値を含むハッシュリストを作成します。

1. リージョンに固有のプライベートキーを取得します。

1. 文字列の SHA-256 ハッシュとプライベートキーを RSA 署名アルゴリズムに渡すと、そこでデジタル署名が作成されます。

1. 署名のバイトコードを 16 進形式にエンコードします。

1. デジタル署名を署名ファイルに入力します。

#### データ署名文字列の内容
<a name="cloudtrail-results-file-custom-validation-data-signing-string-summary"></a>

データ署名文字列は、スペースで区切られた各クエリ結果ファイルのハッシュ値で構成されます。署名ファイルには、各クエリ結果ファイルの `fileHashValue` がリストされています。

### カスタム検証を実装する手順
<a name="cloudtrail-results-file-custom-validation-steps"></a>

カスタム検証ソリューションを実装するときは、最初にダイジェストファイルを検証してから、署名ファイルと参照するクエリ結果ファイルを検証する必要があります。

#### 署名ファイルを検証する
<a name="cloudtrail-results-file-custom-validation-steps-sign"></a>

署名ファイルを検証するには、署名、対応するプライベートキーが署名に使用されたパブリックキー、計算したデータ署名文字列が必要です。

1. 署名ファイルを入手してください。

1. 本来の場所から署名ファイルが取得されたことを確認します。

1. 署名ファイルの 16 進エンコードされた署名を取得します。

1. パブリックキー (対応するプライベートキーが署名ファイルの署名に使用された) の 16 進エンコードされたフィンガープリントを取得します。

1. 署名ファイルで `queryCompleteTime` に対応する時間範囲のパブリックキーを取得します。時間範囲には、「`StartTime` より早い `queryCompleteTime`」および「`EndTime` より遅い `queryCompleteTime`」を選択します。

1. 取得したパブリックキーの中から、フィンガープリントが署名ファイルの `publicKeyFingerprint` の値と一致するパブリックキーを選択します。

1. 各クエリ結果ファイルのハッシュ値をスペースで区切ったハッシュリストを使用して、署名ファイルの署名を検証するために使用するデータ署名文字列を再作成します。署名ファイルには、各クエリ結果ファイルの `fileHashValue` がリストされています。

   たとえば、署名ファイルの `files` 配列に次の 3 つのクエリ結果ファイルが含まれている場合、ハッシュリストは「aaa bbb ccc」になります。

   ```
   “files": [ 
      { 
           "fileHashValue" : “aaa”, 
           "fileName" : "result_1.csv.gz" 
      },
      {       
           "fileHashValue" : “bbb”,       
           "fileName" : "result_2.csv.gz"      
      },
      { 
           "fileHashValue" : “ccc”,       
           "fileName" : "result_3.csv.gz" 
      }
   ],
   ```

1. 文字列の SHA-256 ハッシュ、パブリックキー、署名を、パラメータとしてRSA 署名検証アルゴリズムに渡して、署名を検証します。結果が true の場合、署名ファイルは有効です。

#### クエリ結果ファイルを検証する
<a name="cloudtrail-results-file-custom-validation-steps-logs"></a>

署名ファイルが有効な場合は、署名ファイルが参照するクエリ結果ファイルを検証します。クエリ結果ファイルの整合性を検証するには、圧縮されたコンテンツの SHA-256 ハッシュ値を計算し、その結果を署名ファイルに記録されているクエリ結果ファイルの `fileHashValue` と比較します。ハッシュが一致する場合、クエリ結果ファイルは有効です。

以下のセクションではこの検証を詳しく説明します。

#### A. 署名ファイルを取得する
<a name="cloudtrail-results-file-custom-validation-steps-get-the-sign-file"></a>

最初の手順は、署名ファイルを取得し、パブリックキーのフィンガープリントを取得することです。

1. 検証するクエリ結果の署名ファイルを Amazon S3 バケットから取得します。

1. 次に、署名ファイルから `hashSignature` の値を取得します。

1. 署名ファイルで、署名ファイルの署名に使用されたプライベートキーに対応するパブリックキーのフィンガープリントを `publicKeyFingerprint` フィールドから取得します。

#### B. 署名ファイルの検証のためにパブリックキーを取得する
<a name="cloudtrail-results-file-custom-validation-steps-retrieve-public-key"></a>

署名ファイルを検証するためのパブリックキーを取得するには、 AWS CLI または CloudTrail API を使用できます。どちらの場合も、検証しようとする署名ファイルの時間範囲 (開始時刻と終了時刻) を指定します。署名ファイル内の `queryCompleteTime` に対応する時間範囲を使用してください。指定した時間範囲について 1 つ以上のパブリックキーが返されることがあります。返されたキーの有効な時間範囲が重複する可能性があります。

**注記**  
CloudTrail では、リージョンごとに異なるプライベート/パブリックキーのペアが使用されるため、各署名ファイルはリージョン固有のプライベートキーで署名されます。したがって、特定のリージョンの署名ファイルを検証するときは、同じリージョンからパブリックキーを取得する必要があります。

##### を使用してパブリックキー AWS CLI を取得する
<a name="cloudtrail-results-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 タイムスタンプを使用します。これらはオプションです。指定しない場合、現在の時刻が使用され、現在アクティブなパブリックキー (1 つまたは複数) が返されます。

 **レスポンス例** 

レスポンスは、返されるキー (1 つまたは複数) を表す JSON オブジェクトのリストです。

##### CloudTrail API を使用してパブリックキーを取得する
<a name="cloudtrail-results-file-custom-validation-steps-retrieve-public-key-api"></a>

CloudTrail API を使用して署名ファイルのパブリックキーを取得するには、開始時刻と終了時刻の値を `ListPublicKeys` API に渡します。この `ListPublicKeys` API は、指定された時間範囲内の、対応するプライベートキーが署名ファイルの署名に使用されたパブリックキーを返します。API は、各パブリックキーに対応するフィンガープリントも返します。

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

このセクションでは、`ListPublicKeys` API のリクエストパラメータとレスポンス要素について説明します。

**注記**  
`ListPublicKeys` のバイナリフィールドのエンコードは変更される可能性があります。

 **リクエストパラメータ** 


****  

| 名前 | 説明 | 
| --- | --- | 
|  StartTime  | オプションとして、CloudTrail 署名ファイルのパブリックキーを検索する時間範囲の開始時刻を UTC で指定します。StartTime が指定されない場合、現在の時刻が使用され、現在のパブリックキーが返されます。<br />型: DateTime  | 
|  EndTime  | オプション。CloudTrail 署名ファイルのパブリックキーを検索する時間範囲の終了時刻を UTC で指定します。EndTime が指定されない場合、現在の時刻が使用されます。<br />型: DateTime  | 

 **レスポンス要素** 

`PublicKeyList` は、次の要素を含む `PublicKey` オブジェクトの配列です。


****  

|  |  | 
| --- |--- |
|  名前  |  説明  | 
|  Value  | DER エンコードされたパブリックキー値 (PKCS \#1 形式)。<br />型: Blob  | 
|  ValidityStartTime  | パブリックキーの有効期間の開始時刻。<br />型: DateTime  | 
|  ValidityEndTime  | パブリックキーの有効期間の終了時刻。<br />型: DateTime  | 
|  Fingerprint  | パブリックキーのフィンガープリント。フィンガープリントを使用して、署名ファイルの検証に使用する必要があるパブリックキーを特定できます。<br />タイプ: 文字列  | 

#### C. 検証に使用するパブリックキーを選択する
<a name="cloudtrail-results-file-custom-validation-steps-choose-public-key"></a>

`list-public-keys` または `ListPublicKeys` によって取得されたパブリックキーの中から、そのフィンガープリントが署名ファイルの `publicKeyFingerprint` フィールドに記録されているフィンガープリントと一致するパブリックキーを選択します。これは署名ファイルの検証に使用するパブリックキーです。

#### D. データ署名文字列を再作成する
<a name="cloudtrail-results-file-custom-validation-steps-recreate-data-signing-string"></a>

署名ファイルの署名と、関連付けられたパブリックキーを取得しました。次は、データ署名文字列を計算する必要があります。データ署名文字列の計算が完了すると、署名の検証に必要な入力を得られます。

データ署名文字列は、スペースで区切られた各クエリ結果ファイルのハッシュ値で構成されます。この文字列を再作成した後、署名ファイルを検証できます。

#### E. 署名ファイルを検証する
<a name="cloudtrail-results-file-custom-validation-steps-validate-sign-file"></a>

再作成したデータ署名文字列、デジタル署名、パブリックキーを、RSA 署名検証アルゴリズムに渡します。出力が true の場合、署名ファイルの署名が検証され、署名ファイルは有効です。

#### F. クエリ結果ファイルを検証する
<a name="cloudtrail-results-file-custom-validation-steps-validate-log-files"></a>

署名ファイルの検証が完了したら、クエリ結果ファイルが参照するログファイルを検証することができます。署名ファイルにはクエリ結果ファイルの SHA-256 ハッシュが含まれています。CloudTrail から送られた後にクエリ結果ファイルのいずれかが変更された場合、SHA-256 が変更され、署名ファイルの署名が一致しなくなります。

以下の手順を使用して、署名ファイルの `files` 配列にリストされているクエリ結果ファイルを検証します。

1. 署名ファイル内で、`files.fileHashValue` フィールドからファイルの元のハッシュを取得します。

1. `hashAlgorithm` で指定されたハッシュアルゴリズムを使用して、クエリ結果ファイルの圧縮されたコンテンツをハッシュします。

1. クエリ結果ファイルごとに生成したハッシュ値を署名ファイルの `files.fileHashValue` と比較します。ハッシュが一致する場合、クエリ結果ファイルは有効です。

### 署名とクエリ結果ファイルのオフライン検証
<a name="cloudtrail-results-file-custom-validation-offline"></a>

署名ファイルとクエリ結果ファイルをオフラインで検証するとき、通常は前のセクションで説明した手順に従います。ただし、パブリックキーに関する次の情報を考慮する必要があります。

#### パブリックキー
<a name="cloudtrail-results-file-custom-validation-offline-public-keys"></a>

オフラインで検証するには、所定の時間範囲のクエリ結果ファイルの検証に必要なパブリックキーを最初にオンラインで取得し (たとえば、`ListPublicKeys` を呼び出す)、オフラインで保存する必要があります。指定した最初の時間範囲外の他のファイルを検証するには、常にこの手順を繰り返す必要があります。

### 検証のサンプルスニペット
<a name="cloudtrail-results-file-custom-validation-sample-code"></a>

次に示すサンプルスニペットは、CloudTrail 署名ファイルとクエリ結果ファイルを検証するためのスケルトンコードです。このスケルトンコードはオンラインでもオフラインでも使用できます。つまり、実装する際に AWSとのオンライン接続を使用するかどうかはユーザーが決めることができます。推奨の実装では、[Java Cryptography Extension (JCE)](https://en.wikipedia.org/wiki/Java_Cryptography_Extension) と [Bouncy Castle](https://www.bouncycastle.org/) をセキュリティ プロバイダーとして使用しています。

サンプルスニペットには次の内容が含まれます。
+ 署名ファイルの署名の検証に使用されるデータ署名文字列を作成する方法。
+ 署名ファイルの署名を確認する方法。
+ クエリ結果ファイルのハッシュ値を計算し、それを署名ファイルにリストされている `fileHashValue` と比較して、クエリ結果ファイルの信頼性を検証する方法。

```
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSAPublicKey;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.json.JSONArray;
import org.json.JSONObject;
 
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
 
 
public class SignFileValidationSampleCode {
 
    public void validateSignFile(String s3Bucket, String s3PrefixPath) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
 
        // Load the sign file from S3 (using Amazon S3 Client) or from your local copy
        JSONObject signFile = loadSignFileToMemory(s3Bucket, String.format("%s/%s", s3PrefixPath, "result_sign.json"));
 
        // Using the Bouncy Castle provider as a JCE security provider - http://www.bouncycastle.org/
        Security.addProvider(new BouncyCastleProvider());
 
        List<String> hashList = new ArrayList<>();
 
        JSONArray jsonArray = signFile.getJSONArray("files");
 
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject file = jsonArray.getJSONObject(i);
            String fileS3ObjectKey = String.format("%s/%s", s3PrefixPath, file.getString("fileName"));
 
            // Load the export file from S3 (using Amazon S3 Client) or from your local copy
            byte[] exportFileContent = loadCompressedExportFileInMemory(s3Bucket, fileS3ObjectKey);
            messageDigest.update(exportFileContent);
            byte[] exportFileHash = messageDigest.digest();
            messageDigest.reset();
            byte[] expectedHash = Hex.decodeHex(file.getString("fileHashValue"));
 
            boolean signaturesMatch = Arrays.equals(expectedHash, exportFileHash);
            if (!signaturesMatch) {
                System.err.println(String.format("Export file: %s/%s hash doesn't match.\tExpected: %s Actual: %s",
                        s3Bucket, fileS3ObjectKey,
                        Hex.encodeHexString(expectedHash), Hex.encodeHexString(exportFileHash)));
            } else {
                System.out.println(String.format("Export file: %s/%s hash match",
                        s3Bucket, fileS3ObjectKey));
            }
 
            hashList.add(file.getString("fileHashValue"));
        }
        String hashListString = hashList.stream().collect(Collectors.joining(" "));
 
        /*
            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 sign 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
            }
        */
        byte[] pkcs1PublicKeyBytes = getPublicKey(signFile.getString("queryCompleteTime"),
                signFile.getString("publicKeyFingerprint"));
        byte[] signatureContent = Hex.decodeHex(signFile.getString("hashSignature"));
 
        // 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(hashListString.getBytes("UTF-8"));
 
        if (signature.verify(signatureContent)) {
            System.out.println("Sign file signature is valid.");
        } else {
            System.err.println("Sign file signature failed validation.");
        }
 
        System.out.println("Sign file validation completed.");
    }
}
```