yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/client/auth/signer.go (about) 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 15 package auth 16 17 import ( 18 "crypto/md5" 19 "crypto/sha256" 20 "encoding/hex" 21 "fmt" 22 "io/ioutil" 23 "sort" 24 "strings" 25 "time" 26 27 "yunion.io/x/cloudmux/pkg/multicloud/hcso/client/auth/credentials" 28 "yunion.io/x/cloudmux/pkg/multicloud/hcso/client/auth/signers" 29 "yunion.io/x/cloudmux/pkg/multicloud/hcso/client/requests" 30 ) 31 32 type Signer interface { 33 GetName() string // 签名算法名称 34 GetAccessKeyId() (accessKeyId string, err error) // 签名access key 35 GetSecretKey() (secretKey string, err error) // access secret 36 Sign(stringToSign, secretSuffix string) string // 生成签名结果 37 } 38 39 func NewSignerWithCredential(credential Credential) (signer Signer, err error) { 40 switch instance := credential.(type) { 41 case *credentials.AccessKeyCredential: 42 return signers.NewAccessKeySigner(instance), nil 43 default: 44 return nil, fmt.Errorf("unsupported credential error") 45 } 46 } 47 48 // 对request进行签名 49 func Sign(request requests.IRequest, signer Signer) (err error) { 50 return signRequest(request, signer) 51 } 52 53 func signRequest(request requests.IRequest, signer Signer) error { 54 // https://support.huaweicloud.com/api-dis/dis_02_0508.html 55 // requestTime 56 reqTime := time.Now() 57 // 添加 必须的Headers 58 fillRequiredHeaders(request, reqTime) 59 // 计算CanonicalRequest 60 canonicalRequest := canonicalRequest(request) 61 // stringToSign 62 credentialScope := strings.Join([]string{ 63 formattedSignTime(reqTime, "Date"), 64 request.GetRegionId(), 65 request.GetProduct(), 66 "sdk_request", 67 }, "/") 68 stringToSign := strings.Join([]string{"SDK-HMAC-SHA256", 69 formattedSignTime(reqTime, "DateTime"), 70 credentialScope, 71 hashSha256([]byte(canonicalRequest)), 72 }, "\n") 73 // 计算SigningKey 74 secret, _ := signer.GetSecretKey() 75 signKey := getSigningKey(secret, formattedSignTime(reqTime, "Date"), 76 request.GetRegionId(), request.GetProduct()) 77 // 计算Signature 78 signature := signer.Sign(stringToSign, signKey) 79 accesskey, _ := signer.GetAccessKeyId() 80 addAuthorizationHeader(request, accesskey, credentialScope, signature) 81 return nil 82 } 83 84 // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0132456728.html 85 // X-Project-Id 如果是专属云场景采用AK/SK 认证方式的接口请求或者多project场景采用AK/SK认证的接口请求则该字段必选。 86 func fillRequiredHeaders(request requests.IRequest, t time.Time) { 87 request.AddHeaderParam("HOST", request.GetHost()) 88 request.AddHeaderParam("X-Sdk-Date", formattedSignTime(t, "DateTime")) 89 if len(request.GetProjectId()) > 0 { 90 request.AddHeaderParam("X-Project-Id", request.GetProjectId()) 91 } 92 return 93 } 94 95 func buildRequestStringToSign(request requests.IRequest) string { 96 return "" 97 } 98 99 func formattedSignTime(t time.Time, format string) string { 100 switch format { 101 case "Date": 102 return t.UTC().Format("20060102") 103 case "DateTime": 104 return t.UTC().Format("20060102T150405Z") 105 default: 106 return t.UTC().Format("20060102T150405Z") 107 } 108 } 109 110 func contentSha256(request requests.IRequest) string { 111 method := strings.ToUpper(request.GetMethod()) 112 content := []byte{} 113 body := request.GetBodyReader() 114 content, _ = ioutil.ReadAll(body) 115 if method == "POST" { 116 if len(content) == 0 { 117 // other http method use query as content 118 content = []byte(request.BuildQueries()) 119 } 120 } 121 122 return hashSha256(content) 123 } 124 125 func hashSha256(msg []byte) string { 126 sh256 := sha256.New() 127 sh256.Write(msg) 128 129 return hex.EncodeToString(sh256.Sum(nil)) 130 } 131 132 func canonicalRequest(request requests.IRequest) string { 133 sha256 := contentSha256(request) 134 uri := request.GetURI() 135 if !strings.HasSuffix(uri, "/") { 136 uri = uri + "/" 137 } 138 139 return strings.Join([]string{ 140 request.GetMethod(), 141 uri, 142 canonicalQueryString(request), 143 canonicalHeaders(request), 144 canonicalHeaderNames(request), 145 sha256, 146 }, "\n") 147 } 148 149 func sortedHeaderNames(request requests.IRequest) []string { 150 headers := request.GetHeaders() 151 keys := make([]string, 0) 152 for k := range headers { 153 keys = append(keys, k) 154 } 155 156 sort.Slice(keys, func(i, j int) bool { 157 return strings.ToLower(keys[i]) < strings.ToLower(keys[j]) 158 }) 159 160 return keys 161 } 162 163 func canonicalQueryString(request requests.IRequest) string { 164 if strings.ToUpper(request.GetMethod()) != "POST" { 165 return request.BuildQueries() 166 } 167 168 return "" 169 } 170 171 func canonicalHeaders(request requests.IRequest) string { 172 keys := sortedHeaderNames(request) 173 headers := request.GetHeaders() 174 ret := []string{} 175 for _, k := range keys { 176 ret = append(ret, strings.ToLower(k)+":"+strings.TrimSpace(headers[k])) 177 } 178 179 return strings.Join(ret, "\n") + "\n" 180 } 181 182 func canonicalHeaderNames(request requests.IRequest) string { 183 keys := sortedHeaderNames(request) 184 ret := strings.Join(keys, ";") 185 return strings.ToLower(ret) 186 } 187 188 var SigningKeyCache = map[string][]byte{} 189 190 func getSigningKey(secretKey, date, regionId, service string) string { 191 joinedKey := strings.Join([]string{secretKey, date, regionId, service}, "") 192 cacheKey := fmt.Sprintf("%x", md5.Sum([]byte(joinedKey))) 193 if v, ok := SigningKeyCache[cacheKey]; ok { 194 return string(v) 195 } 196 197 ret := []byte("SDK" + secretKey) 198 for _, k := range []string{date, regionId, service, "sdk_request"} { 199 ret = signers.HmacSha256(k, ret) 200 } 201 202 SigningKeyCache[cacheKey] = ret 203 return string(ret) 204 } 205 206 func addAuthorizationHeader(request requests.IRequest, accessKey, credentialScope, signature string) { 207 auth := "SDK-HMAC-SHA256" + " " + strings.Join([]string{ 208 "Credential=" + accessKey + "/" + credentialScope, 209 "SignedHeaders=" + canonicalHeaderNames(request), 210 "Signature=" + signature, 211 }, ", ") 212 213 request.AddHeaderParam("Authorization", auth) 214 }