This repository has been archived by the owner on Aug 30, 2024. It is now read-only.
forked from smarty-archives/go-aws-auth
-
Notifications
You must be signed in to change notification settings - Fork 2
/
sign4.go
117 lines (94 loc) · 3.31 KB
/
sign4.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package awsauth
import (
"encoding/hex"
"net/http"
"sort"
"strings"
)
func hashedCanonicalRequestV4(request *http.Request, meta *metadata) string {
// TASK 1. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
payload := readAndReplaceBody(request)
payloadHash := hashSHA256(payload)
request.Header.Set("X-Amz-Content-Sha256", payloadHash)
// Set this in header values to make it appear in the range of headers to sign
request.Header.Set("Host", request.Host)
var sortedHeaderKeys []string
for key, _ := range request.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(request.Header.Get(key))
if key == "host" {
//AWS does not include port in signing request.
if strings.Contains(value, ":") {
split := strings.Split(value, ":")
port := split[1]
if port == "80" || port == "443" {
value = split[0]
}
}
}
headersToSign += key + ":" + value + "\n"
}
meta.signedHeaders = concat(";", sortedHeaderKeys...)
canonicalRequest := concat("\n", request.Method, normuri(request.URL.Path), normquery(request.URL.Query()), headersToSign, meta.signedHeaders, payloadHash)
return hashSHA256([]byte(canonicalRequest))
}
func stringToSignV4(request *http.Request, hashedCanonReq string, meta *metadata) string {
// TASK 2. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
requestTs := request.Header.Get("X-Amz-Date")
meta.algorithm = "AWS4-HMAC-SHA256"
meta.service, meta.region = serviceAndRegion(request.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(request *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 request.Header.Get(header) == "" {
request.Header.Set(header, value)
}
}
if request.URL.Path == "" {
request.URL.Path += "/"
}
return request
}
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"