signer.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package huawei
  15. import (
  16. "bytes"
  17. "crypto/hmac"
  18. "crypto/sha256"
  19. "io"
  20. "fmt"
  21. "net/http"
  22. "sort"
  23. "strings"
  24. "time"
  25. )
  26. const (
  27. DateFormat = "20060102T150405Z"
  28. SignAlgorithm = "SDK-HMAC-SHA256"
  29. HeaderXDateTime = "X-Sdk-Date"
  30. HeaderXHost = "host"
  31. HeaderXAuthorization = "Authorization"
  32. HeaderXContentSha256 = "X-Sdk-Content-Sha256"
  33. )
  34. func hmacsha256(keyByte []byte, dataStr string) ([]byte, error) {
  35. hm := hmac.New(sha256.New, []byte(keyByte))
  36. if _, err := hm.Write([]byte(dataStr)); err != nil {
  37. return nil, err
  38. }
  39. return hm.Sum(nil), nil
  40. }
  41. // Build a CanonicalRequest from a regular request string
  42. func CanonicalRequest(request *http.Request, signedHeaders []string) (string, error) {
  43. var hexencode string
  44. var err error
  45. if hex := request.Header.Get(HeaderXContentSha256); hex != "" {
  46. hexencode = hex
  47. } else {
  48. bodyData, err := RequestPayload(request)
  49. if err != nil {
  50. return "", err
  51. }
  52. hexencode, err = HexEncodeSHA256Hash(bodyData)
  53. if err != nil {
  54. return "", err
  55. }
  56. }
  57. 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
  58. }
  59. // CanonicalURI returns request uri
  60. func CanonicalURI(request *http.Request) string {
  61. pattens := strings.Split(request.URL.Path, "/")
  62. var uriSlice []string
  63. for _, v := range pattens {
  64. uriSlice = append(uriSlice, escape(v))
  65. }
  66. urlpath := strings.Join(uriSlice, "/")
  67. if len(urlpath) == 0 || urlpath[len(urlpath)-1] != '/' {
  68. urlpath = urlpath + "/"
  69. }
  70. return urlpath
  71. }
  72. // CanonicalQueryString
  73. func CanonicalQueryString(request *http.Request) string {
  74. var keys []string
  75. queryMap := request.URL.Query()
  76. for key := range queryMap {
  77. keys = append(keys, key)
  78. }
  79. sort.Strings(keys)
  80. var query []string
  81. for _, key := range keys {
  82. k := escape(key)
  83. sort.Strings(queryMap[key])
  84. for _, v := range queryMap[key] {
  85. kv := fmt.Sprintf("%s=%s", k, escape(v))
  86. query = append(query, kv)
  87. }
  88. }
  89. queryStr := strings.Join(query, "&")
  90. request.URL.RawQuery = queryStr
  91. return queryStr
  92. }
  93. // CanonicalHeaders
  94. func CanonicalHeaders(request *http.Request, signerHeaders []string) string {
  95. var canonicalHeaders []string
  96. header := make(map[string][]string)
  97. for k, v := range request.Header {
  98. header[strings.ToLower(k)] = v
  99. }
  100. for _, key := range signerHeaders {
  101. value := header[key]
  102. if strings.EqualFold(key, HeaderXHost) {
  103. value = []string{request.Host}
  104. }
  105. sort.Strings(value)
  106. for _, v := range value {
  107. canonicalHeaders = append(canonicalHeaders, key+":"+strings.TrimSpace(v))
  108. }
  109. }
  110. return fmt.Sprintf("%s\n", strings.Join(canonicalHeaders, "\n"))
  111. }
  112. // SignedHeaders
  113. func SignedHeaders(r *http.Request) []string {
  114. var signedHeaders []string
  115. for key := range r.Header {
  116. signedHeaders = append(signedHeaders, strings.ToLower(key))
  117. }
  118. sort.Strings(signedHeaders)
  119. return signedHeaders
  120. }
  121. // RequestPayload
  122. func RequestPayload(request *http.Request) ([]byte, error) {
  123. if request.Body == nil {
  124. return []byte(""), nil
  125. }
  126. bodyByte, err := io.ReadAll(request.Body)
  127. if err != nil {
  128. return []byte(""), err
  129. }
  130. request.Body = io.NopCloser(bytes.NewBuffer(bodyByte))
  131. return bodyByte, err
  132. }
  133. // Create a "String to Sign".
  134. func StringToSign(canonicalRequest string, t time.Time) (string, error) {
  135. hashStruct := sha256.New()
  136. _, err := hashStruct.Write([]byte(canonicalRequest))
  137. if err != nil {
  138. return "", err
  139. }
  140. return fmt.Sprintf("%s\n%s\n%x",
  141. SignAlgorithm, t.UTC().Format(DateFormat), hashStruct.Sum(nil)), nil
  142. }
  143. // Create the HWS Signature.
  144. func SignStringToSign(stringToSign string, signingKey []byte) (string, error) {
  145. hmsha, err := hmacsha256(signingKey, stringToSign)
  146. return fmt.Sprintf("%x", hmsha), err
  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. // Get the finalized value for the "Authorization" header. The signature parameter is the output from SignStringToSign
  158. func AuthHeaderValue(signatureStr, accessKeyStr string, signedHeaders []string) string {
  159. return fmt.Sprintf("%s Access=%s, SignedHeaders=%s, Signature=%s", SignAlgorithm, accessKeyStr, strings.Join(signedHeaders, ";"), signatureStr)
  160. }
  161. // Signature HWS meta
  162. type Signer struct {
  163. Key string
  164. Secret string
  165. }
  166. // SignRequest set Authorization header
  167. func (s *Signer) Sign(request *http.Request) error {
  168. var t time.Time
  169. var err error
  170. var date string
  171. if date = request.Header.Get(HeaderXDateTime); date != "" {
  172. t, err = time.Parse(DateFormat, date)
  173. }
  174. if err != nil || date == "" {
  175. t = time.Now()
  176. request.Header.Set(HeaderXDateTime, t.UTC().Format(DateFormat))
  177. }
  178. signedHeaders := SignedHeaders(request)
  179. canonicalRequest, err := CanonicalRequest(request, signedHeaders)
  180. if err != nil {
  181. return err
  182. }
  183. stringToSignStr, err := StringToSign(canonicalRequest, t)
  184. if err != nil {
  185. return err
  186. }
  187. signatureStr, err := SignStringToSign(stringToSignStr, []byte(s.Secret))
  188. if err != nil {
  189. return err
  190. }
  191. authValueStr := AuthHeaderValue(signatureStr, s.Key, signedHeaders)
  192. request.Header.Set(HeaderXAuthorization, authValueStr)
  193. return nil
  194. }
  195. func shouldEscape(c byte) bool {
  196. if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '~' || c == '.' {
  197. return false
  198. }
  199. return true
  200. }
  201. func escape(s string) string {
  202. hexCount := 0
  203. for i := 0; i < len(s); i++ {
  204. c := s[i]
  205. if shouldEscape(c) {
  206. hexCount++
  207. }
  208. }
  209. if hexCount == 0 {
  210. return s
  211. }
  212. t := make([]byte, len(s)+2*hexCount)
  213. j := 0
  214. for i := 0; i < len(s); i++ {
  215. switch c := s[i]; {
  216. case shouldEscape(c):
  217. t[j] = '%'
  218. t[j+1] = "0123456789ABCDEF"[c>>4]
  219. t[j+2] = "0123456789ABCDEF"[c&15]
  220. j += 3
  221. default:
  222. t[j] = s[i]
  223. j++
  224. }
  225. }
  226. return string(t)
  227. }