package awsauth import ( "encoding/hex" "net/http" "sort" "strings" ) func hashedCanonicalRequestV4(req *http.Request, meta *metadata) string { // TASK 1. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html payload := readAndReplaceBody(req) payloadHash := hashSHA256(payload) req.Header.Set("X-Amz-Content-Sha256", payloadHash) // Set this in header values to make it appear in the range of headers to sign req.Header.Set("Host", req.Host) var sortedHeaderKeys []string for key, _ := range req.Header { switch key { case "Content-Type", "Content-Md5", "Host": default: if !strings.HasPrefix(key, "X-Amz-") { continue } } sortedHeaderKeys = append(sortedHeaderKeys, strings.ToLower(key)) } sort.Strings(sortedHeaderKeys) var headersToSign string for _, key := range sortedHeaderKeys { value := strings.TrimSpace(req.Header.Get(key)) headersToSign += key + ":" + value + "\n" } meta.signedHeaders = concat(";", sortedHeaderKeys...) canonicalRequest := concat("\n", req.Method, normuri(req.URL.Path), normquery(req.URL.Query()), headersToSign, meta.signedHeaders, payloadHash) return hashSHA256([]byte(canonicalRequest)) } func stringToSignV4(req *http.Request, hashedCanonReq string, meta *metadata) string { // TASK 2. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html requestTs := req.Header.Get("X-Amz-Date") meta.algorithm = "AWS4-HMAC-SHA256" meta.service, meta.region = serviceAndRegion(req.Host) meta.date = tsDateV4(requestTs) meta.credentialScope = concat("/", meta.date, meta.region, meta.service, "aws4_request") return concat("\n", meta.algorithm, requestTs, meta.credentialScope, hashedCanonReq) } func signatureV4(signingKey []byte, stringToSign string) string { // TASK 3. http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html return hex.EncodeToString(hmacSHA256(signingKey, stringToSign)) } func prepareRequestV4(req *http.Request) *http.Request { necessaryDefaults := map[string]string{ "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", "X-Amz-Date": timestampV4(), } for header, value := range necessaryDefaults { if req.Header.Get(header) == "" { req.Header.Set(header, value) } } if req.URL.Path == "" { req.URL.Path += "/" } return req } func signingKeyV4(secretKey, date, region, service string) []byte { kDate := hmacSHA256([]byte("AWS4"+secretKey), date) kRegion := hmacSHA256(kDate, region) kService := hmacSHA256(kRegion, service) kSigning := hmacSHA256(kService, "aws4_request") return kSigning } func buildAuthHeaderV4(signature string, meta *metadata, keys Credentials) string { credential := keys.AccessKeyID + "/" + meta.credentialScope return meta.algorithm + " Credential=" + credential + ", SignedHeaders=" + meta.signedHeaders + ", Signature=" + signature } func timestampV4() string { return now().Format(timeFormatV4) } func tsDateV4(timestamp string) string { return timestamp[:8] } const timeFormatV4 = "20060102T150405Z"