github.com/aliyun/aliyun-oss-go-sdk@v3.0.2+incompatible/oss/auth.go (about)

     1  package oss
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/hmac"
     6  	"crypto/sha1"
     7  	"crypto/sha256"
     8  	"encoding/base64"
     9  	"encoding/hex"
    10  	"fmt"
    11  	"hash"
    12  	"io"
    13  	"net/http"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  )
    19  
    20  // headerSorter defines the key-value structure for storing the sorted data in signHeader.
    21  type headerSorter struct {
    22  	Keys []string
    23  	Vals []string
    24  }
    25  
    26  // getAdditionalHeaderKeys get exist key in http header
    27  func (conn Conn) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) {
    28  	var keysList []string
    29  	keysMap := make(map[string]string)
    30  	srcKeys := make(map[string]string)
    31  
    32  	for k := range req.Header {
    33  		srcKeys[strings.ToLower(k)] = ""
    34  	}
    35  
    36  	for _, v := range conn.config.AdditionalHeaders {
    37  		if _, ok := srcKeys[strings.ToLower(v)]; ok {
    38  			keysMap[strings.ToLower(v)] = ""
    39  		}
    40  	}
    41  
    42  	for k := range keysMap {
    43  		keysList = append(keysList, k)
    44  	}
    45  	sort.Strings(keysList)
    46  	return keysList, keysMap
    47  }
    48  
    49  // getAdditionalHeaderKeysV4 get exist key in http header
    50  func (conn Conn) getAdditionalHeaderKeysV4(req *http.Request) ([]string, map[string]string) {
    51  	var keysList []string
    52  	keysMap := make(map[string]string)
    53  	srcKeys := make(map[string]string)
    54  
    55  	for k := range req.Header {
    56  		srcKeys[strings.ToLower(k)] = ""
    57  	}
    58  
    59  	for _, v := range conn.config.AdditionalHeaders {
    60  		if _, ok := srcKeys[strings.ToLower(v)]; ok {
    61  			if !strings.EqualFold(v, HTTPHeaderContentMD5) && !strings.EqualFold(v, HTTPHeaderContentType) {
    62  				keysMap[strings.ToLower(v)] = ""
    63  			}
    64  		}
    65  	}
    66  
    67  	for k := range keysMap {
    68  		keysList = append(keysList, k)
    69  	}
    70  	sort.Strings(keysList)
    71  	return keysList, keysMap
    72  }
    73  
    74  // signHeader signs the header and sets it as the authorization header.
    75  func (conn Conn) signHeader(req *http.Request, canonicalizedResource string, credentials Credentials) {
    76  	akIf := credentials
    77  	authorizationStr := ""
    78  	if conn.config.AuthVersion == AuthV4 {
    79  		strDay := ""
    80  		strDate := req.Header.Get(HttpHeaderOssDate)
    81  		if strDate == "" {
    82  			strDate = req.Header.Get(HTTPHeaderDate)
    83  			t, _ := time.Parse(http.TimeFormat, strDate)
    84  			strDay = t.Format("20060102")
    85  		} else {
    86  			t, _ := time.Parse(timeFormatV4, strDate)
    87  			strDay = t.Format("20060102")
    88  		}
    89  		signHeaderProduct := conn.config.GetSignProduct()
    90  		signHeaderRegion := conn.config.GetSignRegion()
    91  
    92  		additionalList, _ := conn.getAdditionalHeaderKeysV4(req)
    93  		if len(additionalList) > 0 {
    94  			authorizationFmt := "OSS4-HMAC-SHA256 Credential=%v/%v/%v/" + signHeaderProduct + "/aliyun_v4_request,AdditionalHeaders=%v,Signature=%v"
    95  			additionnalHeadersStr := strings.Join(additionalList, ";")
    96  			authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), strDay, signHeaderRegion, additionnalHeadersStr, conn.getSignedStrV4(req, canonicalizedResource, akIf.GetAccessKeySecret(), nil))
    97  		} else {
    98  			authorizationFmt := "OSS4-HMAC-SHA256 Credential=%v/%v/%v/" + signHeaderProduct + "/aliyun_v4_request,Signature=%v"
    99  			authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), strDay, signHeaderRegion, conn.getSignedStrV4(req, canonicalizedResource, akIf.GetAccessKeySecret(), nil))
   100  		}
   101  	} else if conn.config.AuthVersion == AuthV2 {
   102  		additionalList, _ := conn.getAdditionalHeaderKeys(req)
   103  		if len(additionalList) > 0 {
   104  			authorizationFmt := "OSS2 AccessKeyId:%v,AdditionalHeaders:%v,Signature:%v"
   105  			additionnalHeadersStr := strings.Join(additionalList, ";")
   106  			authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), additionnalHeadersStr, conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret()))
   107  		} else {
   108  			authorizationFmt := "OSS2 AccessKeyId:%v,Signature:%v"
   109  			authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret()))
   110  		}
   111  	} else {
   112  		// Get the final authorization string
   113  		authorizationStr = "OSS " + akIf.GetAccessKeyID() + ":" + conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
   114  	}
   115  
   116  	// Give the parameter "Authorization" value
   117  	req.Header.Set(HTTPHeaderAuthorization, authorizationStr)
   118  }
   119  
   120  func (conn Conn) getSignedStr(req *http.Request, canonicalizedResource string, keySecret string) string {
   121  	// Find out the "x-oss-"'s address in header of the request
   122  	ossHeadersMap := make(map[string]string)
   123  	additionalList, additionalMap := conn.getAdditionalHeaderKeys(req)
   124  	for k, v := range req.Header {
   125  		if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
   126  			ossHeadersMap[strings.ToLower(k)] = v[0]
   127  		} else if conn.config.AuthVersion == AuthV2 {
   128  			if _, ok := additionalMap[strings.ToLower(k)]; ok {
   129  				ossHeadersMap[strings.ToLower(k)] = v[0]
   130  			}
   131  		}
   132  	}
   133  	hs := newHeaderSorter(ossHeadersMap)
   134  
   135  	// Sort the ossHeadersMap by the ascending order
   136  	hs.Sort()
   137  
   138  	// Get the canonicalizedOSSHeaders
   139  	canonicalizedOSSHeaders := ""
   140  	for i := range hs.Keys {
   141  		canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
   142  	}
   143  
   144  	// Give other parameters values
   145  	// when sign URL, date is expires
   146  	date := req.Header.Get(HTTPHeaderDate)
   147  	contentType := req.Header.Get(HTTPHeaderContentType)
   148  	contentMd5 := req.Header.Get(HTTPHeaderContentMD5)
   149  
   150  	// default is v1 signature
   151  	signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
   152  	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
   153  
   154  	// v2 signature
   155  	if conn.config.AuthVersion == AuthV2 {
   156  		signStr = req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalList, ";") + "\n" + canonicalizedResource
   157  		h = hmac.New(func() hash.Hash { return sha256.New() }, []byte(keySecret))
   158  	}
   159  
   160  	if conn.config.LogLevel >= Debug {
   161  		conn.config.WriteLog(Debug, "[Req:%p]signStr:%s\n", req, EscapeLFString(signStr))
   162  	}
   163  
   164  	io.WriteString(h, signStr)
   165  	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
   166  
   167  	return signedStr
   168  }
   169  
   170  func (conn Conn) getSignedStrV4(req *http.Request, canonicalizedResource string, keySecret string, signingTime *time.Time) string {
   171  	// Find out the "x-oss-"'s address in header of the request
   172  	ossHeadersMap := make(map[string]string)
   173  	additionalList, additionalMap := conn.getAdditionalHeaderKeysV4(req)
   174  	for k, v := range req.Header {
   175  		lowKey := strings.ToLower(k)
   176  		if strings.EqualFold(lowKey, HTTPHeaderContentMD5) ||
   177  			strings.EqualFold(lowKey, HTTPHeaderContentType) ||
   178  			strings.HasPrefix(lowKey, "x-oss-") {
   179  			ossHeadersMap[lowKey] = strings.Trim(v[0], " ")
   180  		} else {
   181  			if _, ok := additionalMap[lowKey]; ok {
   182  				ossHeadersMap[lowKey] = strings.Trim(v[0], " ")
   183  			}
   184  		}
   185  	}
   186  
   187  	// get day,eg 20210914
   188  	//signingTime
   189  	signDate := ""
   190  	strDay := ""
   191  	if signingTime != nil {
   192  		signDate = signingTime.Format(timeFormatV4)
   193  		strDay = signingTime.Format(shortTimeFormatV4)
   194  	} else {
   195  		var t time.Time
   196  		// Required parameters
   197  		if date := req.Header.Get(HTTPHeaderDate); date != "" {
   198  			signDate = date
   199  			t, _ = time.Parse(http.TimeFormat, date)
   200  		}
   201  
   202  		if ossDate := req.Header.Get(HttpHeaderOssDate); ossDate != "" {
   203  			signDate = ossDate
   204  			t, _ = time.Parse(timeFormatV4, ossDate)
   205  		}
   206  
   207  		strDay = t.Format("20060102")
   208  	}
   209  
   210  	hs := newHeaderSorter(ossHeadersMap)
   211  
   212  	// Sort the ossHeadersMap by the ascending order
   213  	hs.Sort()
   214  
   215  	// Get the canonicalizedOSSHeaders
   216  	canonicalizedOSSHeaders := ""
   217  	for i := range hs.Keys {
   218  		canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
   219  	}
   220  
   221  	signStr := ""
   222  
   223  	// v4 signature
   224  	hashedPayload := DefaultContentSha256
   225  	if val := req.Header.Get(HttpHeaderOssContentSha256); val != "" {
   226  		hashedPayload = val
   227  	}
   228  
   229  	// subResource
   230  	resource := canonicalizedResource
   231  	subResource := ""
   232  	subPos := strings.LastIndex(canonicalizedResource, "?")
   233  	if subPos != -1 {
   234  		subResource = canonicalizedResource[subPos+1:]
   235  		resource = canonicalizedResource[0:subPos]
   236  	}
   237  
   238  	// get canonical request
   239  	canonicalReuqest := req.Method + "\n" + resource + "\n" + subResource + "\n" + canonicalizedOSSHeaders + "\n" + strings.Join(additionalList, ";") + "\n" + hashedPayload
   240  	rh := sha256.New()
   241  	io.WriteString(rh, canonicalReuqest)
   242  	hashedRequest := hex.EncodeToString(rh.Sum(nil))
   243  
   244  	if conn.config.LogLevel >= Debug {
   245  		conn.config.WriteLog(Debug, "[Req:%p]CanonicalRequest:%s\n", req, EscapeLFString(canonicalReuqest))
   246  	}
   247  
   248  	// Product & Region
   249  	signedStrV4Product := conn.config.GetSignProduct()
   250  	signedStrV4Region := conn.config.GetSignRegion()
   251  
   252  	signStr = "OSS4-HMAC-SHA256" + "\n" + signDate + "\n" + strDay + "/" + signedStrV4Region + "/" + signedStrV4Product + "/aliyun_v4_request" + "\n" + hashedRequest
   253  	if conn.config.LogLevel >= Debug {
   254  		conn.config.WriteLog(Debug, "[Req:%p]signStr:%s\n", req, EscapeLFString(signStr))
   255  	}
   256  
   257  	h1 := hmac.New(func() hash.Hash { return sha256.New() }, []byte("aliyun_v4"+keySecret))
   258  	io.WriteString(h1, strDay)
   259  	h1Key := h1.Sum(nil)
   260  
   261  	h2 := hmac.New(func() hash.Hash { return sha256.New() }, h1Key)
   262  	io.WriteString(h2, signedStrV4Region)
   263  	h2Key := h2.Sum(nil)
   264  
   265  	h3 := hmac.New(func() hash.Hash { return sha256.New() }, h2Key)
   266  	io.WriteString(h3, signedStrV4Product)
   267  	h3Key := h3.Sum(nil)
   268  
   269  	h4 := hmac.New(func() hash.Hash { return sha256.New() }, h3Key)
   270  	io.WriteString(h4, "aliyun_v4_request")
   271  	h4Key := h4.Sum(nil)
   272  
   273  	h := hmac.New(func() hash.Hash { return sha256.New() }, h4Key)
   274  	io.WriteString(h, signStr)
   275  	return fmt.Sprintf("%x", h.Sum(nil))
   276  }
   277  
   278  func (conn Conn) getRtmpSignedStr(bucketName, channelName, playlistName string, expiration int64, keySecret string, params map[string]interface{}) string {
   279  	if params[HTTPParamAccessKeyID] == nil {
   280  		return ""
   281  	}
   282  
   283  	canonResource := fmt.Sprintf("/%s/%s", bucketName, channelName)
   284  	canonParamsKeys := []string{}
   285  	for key := range params {
   286  		if key != HTTPParamAccessKeyID && key != HTTPParamSignature && key != HTTPParamExpires && key != HTTPParamSecurityToken {
   287  			canonParamsKeys = append(canonParamsKeys, key)
   288  		}
   289  	}
   290  
   291  	sort.Strings(canonParamsKeys)
   292  	canonParamsStr := ""
   293  	for _, key := range canonParamsKeys {
   294  		canonParamsStr = fmt.Sprintf("%s%s:%s\n", canonParamsStr, key, params[key].(string))
   295  	}
   296  
   297  	expireStr := strconv.FormatInt(expiration, 10)
   298  	signStr := expireStr + "\n" + canonParamsStr + canonResource
   299  
   300  	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
   301  	io.WriteString(h, signStr)
   302  	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
   303  	return signedStr
   304  }
   305  
   306  // newHeaderSorter is an additional function for function SignHeader.
   307  func newHeaderSorter(m map[string]string) *headerSorter {
   308  	hs := &headerSorter{
   309  		Keys: make([]string, 0, len(m)),
   310  		Vals: make([]string, 0, len(m)),
   311  	}
   312  
   313  	for k, v := range m {
   314  		hs.Keys = append(hs.Keys, k)
   315  		hs.Vals = append(hs.Vals, v)
   316  	}
   317  	return hs
   318  }
   319  
   320  // Sort is an additional function for function SignHeader.
   321  func (hs *headerSorter) Sort() {
   322  	sort.Sort(hs)
   323  }
   324  
   325  // Len is an additional function for function SignHeader.
   326  func (hs *headerSorter) Len() int {
   327  	return len(hs.Vals)
   328  }
   329  
   330  // Less is an additional function for function SignHeader.
   331  func (hs *headerSorter) Less(i, j int) bool {
   332  	return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
   333  }
   334  
   335  // Swap is an additional function for function SignHeader.
   336  func (hs *headerSorter) Swap(i, j int) {
   337  	hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
   338  	hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
   339  }