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  }