golang で aws s3v4 の署名キーを作成する方法

# aws s3v4 署名バージョン
aws s3 は version4 より署名プロセスが変わっています
https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-4.html

## 署名作成の流れ

1. 署名に必要なキーを作成する（本記事はここ）
2. 1.で作成したキーを使って署名を作成する

## 各言語での署名キー作成方法

各言語の署名キー作成フローは以下の文書にまとまっています。しかしながら、`Java`, `.NET (C#)`, `Python`, `Ruby`, `Javascript` のサンプルコードはあっても、Golangのサンプルコードは存在しません
https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-v4-examples.html

そのため、本記事ではGolangで version4 用の署名取得処理のサンプルコードを載せることとします
ちなみにですが、本記事では署名キーを作成する部分の説明であり、署名作成のために前もって準備しておかなければならない処理が存在します。それについては、後日記事を執筆しようと思います。

公式のサンプルを乗せて比較できるようにしていますが、さっさとGolangでの実装方法を知りたい人は、[こちら](#署名に必要なキーを作成するgolang)から

### 署名に必要なキーを作成する（Java-公式サンプルコード）

公式のサンプルコードにならって説明します
言語は何でも良いのですが、Java を載せておきます

`HmacSHA256` はある文字列を、あるキー(byte配列)を使ってhash化する関数です
`getSignatureKey` は特定の文字列をhash化して署名を得る処理です
ある文字列をハッシュ化した[]byteをキーとして、ハッシュを重ねていくことで、最終生成物の署名を得ることができます。
version4では、
- date
- region
- service
- s3のsecretキー
を使って署名を作成します

```golang
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
    String algorithm="HmacSHA256";
    Mac mac = Mac.getInstance(algorithm);
    mac.init(new SecretKeySpec(key, algorithm));
    return mac.doFinal(data.getBytes("UTF-8"));
}

static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
    byte[] kSecret = ("AWS4" + key).getBytes("UTF-8");
    byte[] kDate = HmacSHA256(dateStamp, kSecret);
    byte[] kRegion = HmacSHA256(regionName, kDate);
    byte[] kService = HmacSHA256(serviceName, kRegion);
    byte[] kSigning = HmacSHA256("aws4_request", kService);
    return kSigning;
}
```


### 署名に必要なキーを作成する（Golang）

```golang
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func main() {
	s3Secretkey := "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
	dateStamp := "20120215"
	regionName := "us-east-1"
	serviceName := "iam"
	signatureKey := getSignature(s3Secretkey, dateStamp, regionName, serviceName)
	fmt.Println(signatureKey) // f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d

}

func getBinaryByMakeHMAC(msg string, key []byte) ([]byte, error) {
	mac := hmac.New(sha256.New, key)
	_, err := mac.Write([]byte(msg))
	return mac.Sum(nil), err
}

func getSignature(s3SecretKey, dateStamp, regionName, serviceName string) string {
	kSecret := []byte("AWS4" + s3SecretKey)
	kDate, _ := getBinaryByMakeHMAC(dateStamp, kSecret)
	kRegion, _ := getBinaryByMakeHMAC(regionName, kDate)
	kService, _ := getBinaryByMakeHMAC(serviceName, kRegion)
	kSigning, _ := getBinaryByMakeHMAC("aws4_request", kService)
	return hex.EncodeToString(kSigning)
}
```

サンプルコード
https://play.golang.org/p/91EMyZsbvsx

公式が用意している以下のサンプルデータの場合に、出力される値を確認しながら進めるとデバッグしやすいです


```
key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'
```

署名キーを得るまでの中間値

```
kSecret  = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate    = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion  = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'
```

サンプルコードでこの中間値を得るためには、ハッシュ化した後の[]byteを `hex.EncodeToString([]byte)` にて[]byteをhex(16進数)へと変換する必要があることに注意してください