signature.go (127 lines of code) (raw):
package sls
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"encoding/base64"
"fmt"
"net/url"
"sort"
"strings"
"time"
)
const (
HTTPHeaderAuthorization = "Authorization"
HTTPHeaderContentMD5 = "Content-MD5"
HTTPHeaderContentType = "Content-Type"
HTTPHeaderContentLength = "Content-Length"
HTTPHeaderDate = "Date"
HTTPHeaderHost = "Host"
HTTPHeaderUserAgent = "User-Agent"
HTTPHeaderAcsSecurityToken = "x-acs-security-token"
HTTPHeaderAPIVersion = "x-log-apiversion"
HTTPHeaderLogDate = "x-log-date"
HTTPHeaderLogContentSha256 = "x-log-content-sha256"
HTTPHeaderSignatureMethod = "x-log-signaturemethod"
HTTPHeaderBodyRawSize = "x-log-bodyrawsize"
)
type Signer interface {
// Sign modifies @param headers only, adds signature and other http headers
// that log services authorization requires.
Sign(method, uriWithQuery string, headers map[string]string, body []byte) error
}
// GMT location
var gmtLoc = time.FixedZone("GMT", 0)
// NowRFC1123 returns now time in RFC1123 format with GMT timezone,
// eg, "Mon, 02 Jan 2006 15:04:05 GMT".
func nowRFC1123() string {
return time.Now().In(gmtLoc).Format(time.RFC1123)
}
func NewSignerV0() *SignerV0 {
return &SignerV0{}
}
type SignerV0 struct{}
func (s *SignerV0) Sign(method, uriWithQuery string, headers map[string]string, body []byte) error {
// do nothing
return nil
}
// SignerV1 version v1
type SignerV1 struct {
accessKeyID string
accessKeySecret string
}
func NewSignerV1(accessKeyID, accessKeySecret string) *SignerV1 {
return &SignerV1{
accessKeyID: accessKeyID,
accessKeySecret: accessKeySecret,
}
}
func (s *SignerV1) Sign(method, uri string, headers map[string]string, body []byte) error {
var contentMD5, contentType, date, canoHeaders, canoResource string
if body != nil {
contentMD5 = fmt.Sprintf("%X", md5.Sum(body))
headers[HTTPHeaderContentMD5] = contentMD5
}
if val, ok := headers[HTTPHeaderContentType]; ok {
contentType = val
}
date, ok := headers[HTTPHeaderDate]
if !ok {
return fmt.Errorf("Can't find 'Date' header")
}
headers[HTTPHeaderSignatureMethod] = signatureMethod
var slsHeaderKeys sort.StringSlice
// Calc CanonicalizedSLSHeaders
slsHeaders := make(map[string]string, len(headers))
for k, v := range headers {
l := strings.TrimSpace(strings.ToLower(k))
if strings.HasPrefix(l, "x-log-") || strings.HasPrefix(l, "x-acs-") {
slsHeaders[l] = strings.TrimSpace(v)
slsHeaderKeys = append(slsHeaderKeys, l)
}
}
sort.Sort(slsHeaderKeys)
for i, k := range slsHeaderKeys {
canoHeaders += k + ":" + slsHeaders[k]
if i+1 < len(slsHeaderKeys) {
canoHeaders += "\n"
}
}
// Calc CanonicalizedResource
u, err := url.Parse(uri)
if err != nil {
return err
}
canoResource += u.EscapedPath()
if u.RawQuery != "" {
var keys sort.StringSlice
vals := u.Query()
for k := range vals {
keys = append(keys, k)
}
sort.Sort(keys)
canoResource += "?"
for i, k := range keys {
if i > 0 {
canoResource += "&"
}
for _, v := range vals[k] {
canoResource += k + "=" + v
}
}
}
signStr := method + "\n" +
contentMD5 + "\n" +
contentType + "\n" +
date + "\n" +
canoHeaders + "\n" +
canoResource
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
mac := hmac.New(sha1.New, []byte(s.accessKeySecret))
_, err = mac.Write([]byte(signStr))
if err != nil {
return err
}
digest := base64.StdEncoding.EncodeToString(mac.Sum(nil))
auth := fmt.Sprintf("LOG %s:%s", s.accessKeyID, digest)
headers[HTTPHeaderAuthorization] = auth
return nil
}
// add commonHeaders to headers after signature if not conflict
func addHeadersAfterSign(commonHeaders, headers map[string]string) {
for k, v := range commonHeaders {
lowerKey := strings.ToLower(k)
if _, ok := headers[lowerKey]; !ok {
headers[k] = v
}
}
}