github.com/chnsz/golangsdk@v0.0.0-20240506093406-85a3fbfa605b/auth/core/signer/signer.go (about) 1 package signer 2 3 import ( 4 "bytes" 5 "crypto/hmac" 6 "crypto/sha256" 7 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "sort" 12 "strings" 13 "time" 14 ) 15 16 const ( 17 DateFormat = "20060102T150405Z" 18 SignAlgorithm = "SDK-HMAC-SHA256" 19 HeaderXDateTime = "X-Sdk-Date" 20 HeaderXHost = "host" 21 HeaderXAuthorization = "Authorization" 22 HeaderXContentSha256 = "X-Sdk-Content-Sha256" 23 ) 24 25 func hmacsha256(keyByte []byte, dataStr string) ([]byte, error) { 26 hm := hmac.New(sha256.New, []byte(keyByte)) 27 if _, err := hm.Write([]byte(dataStr)); err != nil { 28 return nil, err 29 } 30 return hm.Sum(nil), nil 31 } 32 33 // Build a CanonicalRequest from a regular request string 34 func CanonicalRequest(request *http.Request, signedHeaders []string) (string, error) { 35 var hexencode string 36 var err error 37 if hex := request.Header.Get(HeaderXContentSha256); hex != "" { 38 hexencode = hex 39 } else { 40 bodyData, err := RequestPayload(request) 41 if err != nil { 42 return "", err 43 } 44 hexencode, err = HexEncodeSHA256Hash(bodyData) 45 if err != nil { 46 return "", err 47 } 48 } 49 return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", request.Method, CanonicalURI(request), CanonicalQueryString(request), CanonicalHeaders(request, signedHeaders), strings.Join(signedHeaders, ";"), hexencode), err 50 } 51 52 // CanonicalURI returns request uri 53 func CanonicalURI(request *http.Request) string { 54 pattens := strings.Split(request.URL.Path, "/") 55 var uriSlice []string 56 for _, v := range pattens { 57 uriSlice = append(uriSlice, escape(v)) 58 } 59 urlpath := strings.Join(uriSlice, "/") 60 if len(urlpath) == 0 || urlpath[len(urlpath)-1] != '/' { 61 urlpath = urlpath + "/" 62 } 63 return urlpath 64 } 65 66 // CanonicalQueryString 67 func CanonicalQueryString(request *http.Request) string { 68 var keys []string 69 queryMap := request.URL.Query() 70 for key := range queryMap { 71 keys = append(keys, key) 72 } 73 sort.Strings(keys) 74 var query []string 75 for _, key := range keys { 76 k := escape(key) 77 sort.Strings(queryMap[key]) 78 for _, v := range queryMap[key] { 79 kv := fmt.Sprintf("%s=%s", k, escape(v)) 80 query = append(query, kv) 81 } 82 } 83 queryStr := strings.Join(query, "&") 84 request.URL.RawQuery = queryStr 85 return queryStr 86 } 87 88 // CanonicalHeaders 89 func CanonicalHeaders(request *http.Request, signerHeaders []string) string { 90 var canonicalHeaders []string 91 header := make(map[string][]string) 92 for k, v := range request.Header { 93 header[strings.ToLower(k)] = v 94 } 95 for _, key := range signerHeaders { 96 value := header[key] 97 if strings.EqualFold(key, HeaderXHost) { 98 value = []string{request.Host} 99 } 100 sort.Strings(value) 101 for _, v := range value { 102 canonicalHeaders = append(canonicalHeaders, key+":"+strings.TrimSpace(v)) 103 } 104 } 105 return fmt.Sprintf("%s\n", strings.Join(canonicalHeaders, "\n")) 106 } 107 108 // SignedHeaders 109 func SignedHeaders(r *http.Request) []string { 110 var signedHeaders []string 111 for key := range r.Header { 112 signedHeaders = append(signedHeaders, strings.ToLower(key)) 113 } 114 sort.Strings(signedHeaders) 115 return signedHeaders 116 } 117 118 // RequestPayload 119 func RequestPayload(request *http.Request) ([]byte, error) { 120 if request.Body == nil { 121 return []byte(""), nil 122 } 123 bodyByte, err := ioutil.ReadAll(request.Body) 124 if err != nil { 125 return []byte(""), err 126 } 127 request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyByte)) 128 return bodyByte, err 129 } 130 131 // Create a "String to Sign". 132 func StringToSign(canonicalRequest string, t time.Time) (string, error) { 133 hashStruct := sha256.New() 134 _, err := hashStruct.Write([]byte(canonicalRequest)) 135 if err != nil { 136 return "", err 137 } 138 return fmt.Sprintf("%s\n%s\n%x", 139 SignAlgorithm, t.UTC().Format(DateFormat), hashStruct.Sum(nil)), nil 140 } 141 142 // Create the HWS Signature. 143 func SignStringToSign(stringToSign string, signingKey []byte) (string, error) { 144 hmsha, err := hmacsha256(signingKey, stringToSign) 145 return fmt.Sprintf("%x", hmsha), err 146 } 147 148 // HexEncodeSHA256Hash returns hexcode of sha256 149 func HexEncodeSHA256Hash(body []byte) (string, error) { 150 hashStruct := sha256.New() 151 if len(body) == 0 { 152 body = []byte("") 153 } 154 _, err := hashStruct.Write(body) 155 return fmt.Sprintf("%x", hashStruct.Sum(nil)), err 156 } 157 158 // Get the finalized value for the "Authorization" header. The signature parameter is the output from SignStringToSign 159 func AuthHeaderValue(signatureStr, accessKeyStr string, signedHeaders []string) string { 160 return fmt.Sprintf("%s Access=%s, SignedHeaders=%s, Signature=%s", SignAlgorithm, accessKeyStr, strings.Join(signedHeaders, ";"), signatureStr) 161 } 162 163 // Signature HWS meta 164 type Signer struct { 165 Key string 166 Secret string 167 } 168 169 // SignRequest set Authorization header 170 func (s *Signer) Sign(request *http.Request) error { 171 var t time.Time 172 var err error 173 var date string 174 if date = request.Header.Get(HeaderXDateTime); date != "" { 175 t, err = time.Parse(DateFormat, date) 176 } 177 if err != nil || date == "" { 178 t = time.Now() 179 request.Header.Set(HeaderXDateTime, t.UTC().Format(DateFormat)) 180 } 181 signedHeaders := SignedHeaders(request) 182 canonicalRequest, err := CanonicalRequest(request, signedHeaders) 183 if err != nil { 184 return err 185 } 186 stringToSignStr, err := StringToSign(canonicalRequest, t) 187 if err != nil { 188 return err 189 } 190 signatureStr, err := SignStringToSign(stringToSignStr, []byte(s.Secret)) 191 if err != nil { 192 return err 193 } 194 authValueStr := AuthHeaderValue(signatureStr, s.Key, signedHeaders) 195 request.Header.Set(HeaderXAuthorization, authValueStr) 196 return nil 197 }