

# 署名付き AWS API リクエストを作成する
<a name="reference_sigv-create-signed-request"></a>

**重要**  
AWS SDK (「[サンプルコードとライブラリ](https://aws.amazon.com/developer/)」を参照) または AWS Command Line Interface (AWS CLI) ツールを使用して API リクエストを AWS に送信する場合、SDK および CLI クライアントが指定したアクセスキーを使用してリクエストを認証するため、このセクションをスキップできます。正当な理由がない限り、常に SDK または CLI を使用することをお勧めします。  
複数の署名バージョンをサポートするリージョンでは、リクエストに手動で署名する場合、使用する署名バージョンを指定する必要があります。マルチリージョンアクセスポイントにリクエストを送信すると、SDK と CLI は Signature Version 4A の使用に自動的に切り替えます。追加の設定は不要です。

AWS SigV4 署名プロトコルを使用して、AWS API リクエストを署名付きで作成できます。

1. リクエストの詳しい情報に基づいて正規リクエストを作成します。

1. 自分の AWS 認証情報を使用して署名を計算します。

1. 求めた署名を認可ヘッダーとしてリクエストに追加します。

AWS は、このプロセスをレプリケートして署名を検証し、検証結果に応じてアクセスを許可または拒否します。

AWS SigV4 を使用して API リクエストに署名する方法を確認する場合は、「[リクエスト署名の例](reference_sigv-examples.md)」を参照してください。

次の表は、署名付きリクエストの作成プロセスで使用される関数を示しています。これらの関数のコードを実装する必要があります。詳細については、「[AWS SDK のサンプルコード](reference_sigv.md#reference_aws-signing-resources)」を参照してください。


| 関数 | 説明 | 
| --- | --- | 
|  `Lowercase()`  |  文字列を小文字に変換します。  | 
|  `Hex()`  |  16 進数の小文字エンコード。  | 
|  `SHA256Hash()`  |  セキュアハッシュアルゴリズム (SHA) 暗号化ハッシュ関数。  | 
|  `HMAC-SHA256()`  |  指定した署名キーで SHA256 アルゴリズムを使用して HMAC を計算します。これは SigV4 で署名するときの最終署名です。  | 
|  `ECDSA-Sign`  |  パブリック/プライベートキー暗号化に基づく非対称署名を使用して計算された楕円曲線デジタル署名アルゴリズム (ECDSA) 署名。  | 
|  `KDF(K, Label, Context, L)`  |  [NIST SP800–108r1](https://doi.org/10.6028/NIST.SP.800-108r1-upd1) で定義されているように、PRF 関数 HMAC-SHA256 を使用するカウンターモードの NIST SP800-108 KDF。  | 
|  `Oct2Int(byte[ ])`  |  ANSI X9.62 で説明されているオクテットから整数への関数。  | 
|  `Trim()`  |  先頭または末尾の空白をすべて削除します。  | 
|  `UriEncode()`  |  すべてのバイトが URI でエンコードされます。UriEncode() は、以下のルールを適用する必要があります。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_sigv-create-signed-request.html)  開発プラットフォームが提供する標準の UriEncode 関数は、実装の違いや基礎となる RFC のあいまいさにより動作しない場合があります。エンコードが確実に機能するように、独自のカスタム UriEncode 関数を作成することをお勧めします。  Java での UriEncode 関数の例については、GitHub ウェブサイトで「[Java Utilities](https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/util/SdkHttpUtils.java#L66)」を参照してください。  | 

**注記**  
リクエストに署名するときは、AWS SigV4 または AWS SigV4a のいずれかを使用できます。これら 2 つの大きな違いを決定づけるのは、署名の計算方法です。SigV4a では、リージョンセットは署名する文字列に含まれますが、認証情報取得ステップの一部ではありません。

## 一時的なセキュリティ認証情報でリクエストに署名する
<a name="temporary-security-credentials"></a>

リクエストに署名するために長期的に認証情報を使用する代わりに、AWS Security Token Service (AWS STS) から提供された一時的なセキュリティ認証情報を使用できます。

一時的なセキュリティ認証情報を使用するときは、認可ヘッダーに `X-Amz-Security-Token` を追加するか、それをクエリ文字列に含めてセッショントークンを保持する必要があります。一部のサービスでは、正規リクエストに `X-Amz-Security-Token` を追加する必要があります。その他のサービスでは、署名の計算後に、`X-Amz-Security-Token` を末尾に追加するだけです。特定の要件については、AWS のサービスごとのドキュメントを確認してください。

## 署名手順の概要
<a name="create-signed-request-steps"></a>

**正規リクエストを作成する**  
リクエストのコンテンツ (ホスト、アクション、ヘッダーなど) を標準的な正規形式に変換します。正規リクエストは、署名文字列を作成するために使用される入力の 1 つです。正規リクエストの作成の詳細については、「[AWS API リクエスト署名の要素](reference_sigv-signing-elements.md)」を参照してください。

**正規リクエストのハッシュを作成する**  
ペイロードのハッシュを作成する際に使用したのと同じアルゴリズムを使用して、正規リクエストをハッシュ化します。正規リクエストのハッシュは、小文字の 16 進数文字の文字列として表す必要があります。

**署名文字列を作成する**  
正規リクエストに加えてアルゴリズム、リクエスト日、認証情報スコープ、正規リクエストのハッシュなどの追加情報を使用して、署名文字列を作成します。

**署名キーを取得する**  
シークレットアクセスキーを使用して、リクエストの署名に使用されるキーを取得します。

**署名を計算する**  
取得した署名キーをハッシュキーとして使用して、署名文字列に対してキー付きハッシュ操作を実行します。

**リクエストヘッダーに署名を追加する**  
算出した署名をリクエストの HTTP ヘッダーまたはクエリ文字列に追加します。

## 正規リクエストを作成する
<a name="create-canonical-request"></a>

正規リクエストを作成するには、次の文字列を改行文字で区切って連結します。これにより、自分で計算する署名と AWS が計算する署名とが必ず一致するようになります。

```
<HTTPMethod>\n
<CanonicalURI>\n
<CanonicalQueryString>\n
<CanonicalHeaders>\n
<SignedHeaders>\n
<HashedPayload>
```
+ *HTTPMethod* – `GET`、`PUT`、`HEAD`、`DELETE` などの HTTP メソッド。
+ *CanonicalUri* – 絶対パスコンポーネント URI の URI エンコード版 (ドメイン名の後の `/` から始まり、文字列の末尾まで、またはクエリ文字列パラメータがある場合は疑問符文字 (`?`) まで)。絶対パスが空値の場合は、フォワードスラッシュ文字 (`/`) を使用します。以下の例の URI (`/amzn-s3-demo-bucket/myphoto.jpg`) は絶対パスであるため、絶対パス内の `/` をエンコードしないでください。

  ```
  http://s3.amazonaws.com/amzn-s3-demo-bucket/myphoto.jpg
  ```
+ *CanonicalQueryString* – URI エンコードされたクエリ文字列パラメータ。それぞれの名前と値を個別に URI エンコードします。また、正規クエリ文字列内のパラメータをキー名のアルファベット順にソートする必要があります。ソートはエンコード後に行われます。次のサンプル URI のクエリ文字列は次のとおりです。

  ```
  http://s3.amazonaws.com/amzn-s3-demo-bucket?prefix=somePrefix&marker=someMarker&max-keys=2
  ```

  正規クエリ文字列は次のとおりです (この例では、読みやすいように改行が追加されています)。

  ```
  UriEncode("marker")+"="+UriEncode("someMarker")+"&"+
  UriEncode("max-keys")+"="+UriEncode("20") + "&" +
  UriEncode("prefix")+"="+UriEncode("somePrefix")
  ```

  リクエストがサブリソースをターゲットにしている場合、対応するクエリパラメータ値は空の文字列 (`""`) になります。例えば、次の URI は `amzn-s3-demo-bucket` バケット上の `ACL` サブリソースを特定してします。

   

  ```
  http://s3.amazonaws.com/amzn-s3-demo-bucket?acl
  ```

  この場合、CanonicalQueryString は以下のようになります。

   

  ```
  UriEncode("acl") + "=" + ""
  ```

  URI に `?` が含まれていない場合は、リクエストにクエリ文字列が存在しないため、正規クエリ文字列を空の文字列 (`""`) に設定します。この場合も、改行文字 (`"\n"`) を含める必要があります。
+ *CanonicalHeaders* – リクエストヘッダーとその値のリスト。個々のヘッダー名と値のペアは改行文字 (`"\n"`) で区切られます。以下は正規ヘッダーの例です。

  ```
  Lowercase(<HeaderName1>)+":"+Trim(<value>)+"\n"
  Lowercase(<HeaderName2>)+":"+Trim(<value>)+"\n"
  ...
  Lowercase(<HeaderNameN>)+":"+Trim(<value>)+"\n"
  ```

  CanonicalHeaders リストには以下が含まれている必要があります。
  + HTTP `host` ヘッダー。
  + `Content-Type` ヘッダーがリクエスト内に存在する場合は、それを *CanonicalHeaders* リストに追加する必要があります。
  + リクエストに含める予定の `x-amz-*` ヘッダーも追加する必要があります。例えば、一時的なセキュリティ認証情報を使用している場合は、リクエストに `x-amz-security-token` を含める必要があります。このヘッダーを *CanonicalHeaders* リストに追加する必要があります。
  + SigV4a の場合、リクエストが有効なリージョンセットを指定するリージョンセットヘッダーを含める必要があります。ヘッダー `X-Amz-Region-Set` はカンマ区切り値のリストとして指定されます。次の例は、us-east-1 リージョンと us-west-1 リージョンの両方でリクエストを行うことを許可するリージョンヘッダーを示しています。

    `X-Amz-Region-Set=us-east-1,us-west-1 `

    リージョンでワイルドカード (\$1) を使用して、複数のリージョンを指定できます。次の例では、ヘッダーを使用して、us-west-1 と us-west-2 の両方でリクエストを行うことができます。

    `X-Amz-Region-Set=us-west-*`
**注記**  
`x-amz-content-sha256` ヘッダーは Amazon S3 AWS リクエストに必要です。これにより、リクエストペイロードのハッシュが指定されます。ペイロードがない場合は、空の文字列のハッシュを指定する必要があります。

  各ヘッダー名は次の条件を満たす必要があります。
  + 小文字が使用されていること。
  + アルファベット順に表示されていること。
  + 後にコロン (`:`) が指定されていること。

  値については、次の操作を行う必要があります。
  + 先頭または末尾のスペースをすべて削除する。
  + 連続するスペースを 1 つのスペースに変換する。
  + 複数値ヘッダーの値をカンマで区切る。
  + host ヘッダー (HTTP/1.1) または :authority  ヘッダー (HTTP/2)、および任意の `x-amz-*` ヘッダーを署名に含める必要があります。署名には、content-type のような他の標準ヘッダーを指定することもできます。

  この例で使用されている関数 `Lowercase()` と `Trim()` は、前のセクションで説明されています。

  以下は `CanonicalHeaders` 文字列の例です。ヘッダー名は小文字で、ソートされています。

   

  ```
  host:s3.amazonaws.com
  x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  x-amz-date:20130708T220855Z
  ```

   
**注記**  
認可署名を計算する目的では、ホストと `x-amz-*` ヘッダーだけが必要です。ただし、データの改ざんを防ぐために、追加のヘッダーを署名計算に含めることを検討する必要があります。  
複雑なシステム間の転送中に頻繁に変更される hop-by-hop ヘッダーを含めないでください。これには `connection`、`x-amzn-trace-id`、`user-agent`、`keep-alive`、`transfer-encoding`、`TE`、`trailer`、`upgrade`、`proxy-authorization`、`proxy-authenticate` など、プロキシ、ロードバランサー、分散システムのノードによって変更されるすべての揮発性トランスポートヘッダーが含まれます。
+ *SignedHeaders* – アルファベット順にソートされ、セミコロンで区切られた小文字のリクエストヘッダー名のリスト。リスト内のリクエストヘッダーは、`CanonicalHeaders` 文字列に含めたヘッダーと同じです。前の例の場合、*SignedHeaders* の値は次のようになります。

  ```
  host;x-amz-content-sha256;x-amz-date
  ```
+ *HashedPayload* — HTTP リクエスト本文のペイロードをハッシュ関数への入力として使用して作成された文字列。この文字列には小文字の 16 進数文字を使用します。

  ```
  Hex(SHA256Hash(<payload>>))
  ```

  リクエストにペイロードがない場合は、空の文字列のハッシュを計算します。例えば、`GET` リクエストを使用してオブジェクトを取得する場合、ペイロードには何もありません。

  ```
  Hex(SHA256Hash(""))
  ```
**注記**  
Amazon S3 の場合、正規リクエストを作成するときにはリテラル文字列 `UNSIGNED-PAYLOAD` を含め、リクエストを送信するときには `x-amz-content-sha256` ヘッダー値と同じ値を設定します。  
`Hex(SHA256Hash("UNSIGNED-PAYLOAD"))`

## 正規リクエストのハッシュを作成する
<a name="create-canonical-request-hash"></a>

ペイロードのハッシュを作成する際に使用したのと同じアルゴリズムを使用して、正規リクエストのハッシュ (ダイジェスト) を作成します。正規リクエストのハッシュは、小文字の 16 進数文字の文字列として表す必要があります。

## 署名文字列を作成する
<a name="create-string-to-sign"></a>

署名文字列を作成するには、以下の文字列を改行文字で区切って連結します。この文字列は改行文字で終わらせないようにしてください。

```
Algorithm \n
RequestDateTime \n
CredentialScope  \n
HashedCanonicalRequest
```
+ *Algorithm* — 正規リクエストのハッシュを作成するために使用されるアルゴリズム。
  + SigV4a – `HMAC-SHA256` ハッシュアルゴリズムを指定するために `AWS4-HMAC-SHA256` を使用します 
  + SigV4a – `ECDSA-P256-SHA-256` ハッシュアルゴリズムを指定するために `AWS4-ECDSA-P256-SHA256` を使用します 
+ *RequestDateTime* — 認証情報のスコープで使用される日付と時刻。この値は、ISO 8601 形式の現在の UTC 時刻です (例: `20130524T000000Z`)。
+ *CredentialScope* - 認証情報スコープ。これにより、結果として生成した署名が、指定されたリージョンとサービスに制限されます。
  + SigV4 – 認証情報スコープには、アクセスキー ID、`YYYYMMDD` 形式の日付、リージョンコード、サービスコード、およびスラッシュ (/) で区切った `aws4_request` 終了文字列が含まれます。リージョンコード、サービスコード、および終了文字列には、小文字を使用する必要があります。この文字列は `YYYYMMDD/region/service/aws4_request` の形式になります。
  + SigV4a – 認証情報には、認証情報には、`YYYYMMDD` 形式の日付、サービス名、スラッシュ (/) で区切られた `aws4_request` 終了文字列が含まれます。このリージョンは別のヘッダー `X-Amz-Region-Set` でカバーされているため、認証情報スコープにはこのリージョンが含まれないことに注意してください。この文字列は `YYYYMMDD/service/aws4_request` の形式になります。
+ *HashedCanonicalRequest* - 前のステップで計算した正規リクエストのハッシュ。

以下は署名する文字列の例です。

```
"<Algorithm>" + "\n" +
timeStampISO8601Format + "\n" +
<Scope> + "\n" +
Hex(<Algorithm>(<CanonicalRequest>))
```

## 署名キーを取得する
<a name="derive-signing-key"></a>

署名キーを取得するには、次のいずれかのプロセスを選択して、SigV4 または SigV4a の署名キーを計算します。

### SigV4 の署名キーの取得
<a name="derive-signing-key-sigv4"></a>

SigV4 の署名キーを取得するには、最初のハッシュ操作のキーとして AWS のシークレットアクセスキーを使用して、リクエスト日、リージョン、およびサービスに対する一連のキー付きハッシュ操作 (HMAC) を実行します。

各ステップで、必要なキーとデータを使用してハッシュ関数を呼び出します。ハッシュ関数を呼び出すたびに、その結果はハッシュ関数への次の呼び出しの入力になります。

以下の例は、この手順の次のセクションで使用する `SigningKey` を取得する方法を示しています。入力内容は、この順序で連結されてハッシュ化されます。`HMAC-SHA256` は、以下に示すようにデータをハッシュ化する場合に使用されるハッシュ関数です。

```
DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
```

**入力必須**
+ `Key` – シークレットアクセスキーが含まれている文字列。
+ `Date` – 認証情報スコープで使用される日付が含まれている *YYYYMMDD* 形式の文字列。
+ `Region` – リージョンコードが含まれている文字列 (例: `us-east-1`)。

  リージョン文字列のリストについては、「AWS 全般のリファレンス」の「[リージョンとエンドポイント](https://docs.aws.amazon.com//general/latest/gr/rande.html#regional-endpoints)」を参照してください。
+ `Service` – サービスコードが含まれている文字列 (例: `ec2`)。
+ 上記の手順で作成したトピックを選択します。

**SigV4a の署名キーを取得するには**

1. `"AWS4"` とシークレットアクセスキーを連結します。データとしてのキーおよび日付文字列として、連結した文字列を備えたハッシュ関数を呼び出します。

   ```
   DateKey = hash("AWS4" + Key, Date)
   ```

1. データとしてのキーおよびリージョン文字列として、前回の結果を備えたハッシュ関数を呼び出します。

   ```
   DateRegionKey = hash(kDate, Region)
   ```

1. データとしてのキーおよびサービス文字列として、前回の結果を備えたハッシュ関数を呼び出します。

   サービスコードは、サービスによって定義されます。AWS Pricing CLI で [get-products](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/pricing/get-products.html) を使用して、サービスのサービスコードを返すことができます。

   ```
   DateRegionServiceKey = hash(kRegion, Service)
   ```

1. データとしてのキーおよび「aws4\$1request」として、前回の結果を備えたハッシュ関数を呼び出します

   ```
   SigningKey = hash(kService, "aws4_request")
   ```

### SigV4a の署名キーの取得
<a name="derive-signing-key-sigv4a"></a>

SigV4a の署名キーを作成するには、次のプロセスを使用してシークレットアクセスキーからキーペアを取得します。この取得の実装例については、「[C99 library implementation of AWS client-side authentication](https://github.com/awslabs/aws-c-auth/blob/e8360a65e0f3337d4ac827945e00c3b55a641a5f/source/key_derivation.c#L291.)」を参照してください。

```
n = [NIST P-256 elliptic curve group order]
G = [NIST P-256 elliptic curve base point]
label = "AWS4-ECDSA-P256-SHA256"

akid = [AWS access key ID as a UTF8 string]
sk = [AWS secret access Key as a UTF8 Base64 string]

input_key = "AWS4A" || sk
count = 1
while (counter != 255) {
  context = akid || counter // note: counter is one byte
  key = KDF(input_key, label, context, 256)
  c = Oct2Int(key)
  if (c > n - 2) {
    counter++
  } else {
    k = c + 1   // private key
    Q = k * G   // public key
  }
}

if (c < 255) {
  return [k, Q]
} else {
  return FAILURE
}
```

## 署名を計算する
<a name="calculate-signature"></a>

署名キーを取得したら、リクエストに追加する署名を計算します。この手順は、使用する署名のバージョンによって異なります。

**SigV4 の署名を計算するには**

1. 前回の呼び出しの結果をキー、**[署名する文字列]** をデータとしてハッシュ関数を呼び出します。取得した署名キーを、この操作のハッシュキーとして使用します。その結果、署名はバイナリ値になります。

   ```
   signature = hash(SigningKey, string-to-sign)
   ```

1. 署名を 2 進数から 16 進数表現に、小文字に変換します。

**SigV4a の署名を計算するには**

1. デジタル署名アルゴリズム (ECDSA P-256) を使用して、前のステップで作成した **[署名する文字列]** に署名します。この署名に使用されるキーは、上記のシークレットアクセスキーから派生したプライベート非対称キーです。

   ```
   signature = base16(ECDSA-Sign(k, string-to-sign))
   ```

1. 署名を 2 進数から 16 進数表現に、小文字に変換します。

## リクエストヘッダーに署名を追加する
<a name="add-signature-to-request"></a>

算出した署名をリクエストに追加します。

**Example 例: 認可ヘッダー**  
**SigV4**  
以下の例は、AWS SigV4 を使用する `DescribeInstances` アクションに対する `Authorization` ヘッダーを示しています。読みやすいように、この例では改行でフォーマットされています。コードでは、これは連続した文字列である必要があります。アルゴリズムと `Credential` の間にカンマはありません。ただし、他の要素はカンマで区切られている必要があります。

```
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request,
SignedHeaders=host;x-amz-date,
Signature=calculated-signature
```

**SigV4a**  
以下の例は、AWS SigV4a を使用する `CreateBucket` アクションに対する Authorization ヘッダーを示しています。読みやすいように、この例では改行でフォーマットされています。コードでは、これは連続した文字列である必要があります。アルゴリズムと認証情報の間にカンマはありません。ただし、他の要素はカンマで区切られている必要があります。

```
Authorization: AWS4-ECDSA-P256-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20220830/s3/aws4_request,
SignedHeaders=host;x-amz-date;x-amz-region-set,
Signature=calculated-signature
```

**Example 例: クエリ文字列に認証パラメータを含むリクエスト**  
**SigV4**  
次の例は、認証情報を含む AWS SigV4 を使用する `DescribeInstances` アクションのクエリを示しています。読みやすいように、この例は改行でフォーマットされており、URL はエンコードされていません。コードでは、クエリ文字列は URL エンコードされた連続した文字列である必要があります。

```
https://ec2.amazonaws.com/?
Action=DescribeInstances&
Version=2016-11-15&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request&
X-Amz-Date=20220830T123600Z&
X-Amz-SignedHeaders=host;x-amz-date&
X-Amz-Signature=calculated-signature
```

**SigV4a**  
次の例は、認証情報を含む AWS SigV4a を使用する `CreateBucket` アクションのクエリを示しています。読みやすいように、この例は改行でフォーマットされており、URL はエンコードされていません。コードでは、クエリ文字列は URL エンコードされた連続した文字列である必要があります。

```
https://ec2.amazonaws.com/?
Action=CreateBucket&
Version=2016-11-15&
X-Amz-Algorithm=AWS4-ECDSA-P256-SHA256&
X-Amz-Credential=AKIAIOSFODNN7EXAMPLE/20220830/s3/aws4_request&
X-Amz-Region-Set=us-west-1&
X-Amz-Date=20220830T123600Z&
X-Amz-SignedHeaders=host;x-amz-date;x-amz-region-set&
X-Amz-Signature=calculated-signature
```