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