package auth import ( "crypto/rand" "crypto/rsa" "crypto/x509" "gitlab.ecloud.com/ecloud/ecloudsdkcore/consts" "gitlab.ecloud.com/ecloud/ecloudsdkcore/errs" "gitlab.ecloud.com/ecloud/ecloudsdkcore/request" "gitlab.ecloud.com/ecloud/ecloudsdkcore/utils" "net/url" "sort" "strings" "time" ) type ICredential interface { Sign(request *request.HttpRequest, credential *Credential) error Encrypt(data []byte, publicKey string) (string, error) Decrypt(originStr string, privateKey string) (string, error) } type MopCredential struct { } type NoneCredential struct { } type AKSKCredential struct { } func NewMopCredential() *MopCredential { return &MopCredential{} } func NewAKSKCredential() *AKSKCredential { return &AKSKCredential{} } func NewNoneCredential() *NoneCredential { return &NoneCredential{} } var MopCredentialInstance = NewMopCredential() var NoneCredentialInstance = NewNoneCredential() var AKSKCredentialInstance = NewAKSKCredential() func GetCredentialManager(credType CredentialType) ICredential { switch credType { case CredentialAkSk: return AKSKCredentialInstance case CredentialMop: return MopCredentialInstance case CredentialNone: return NoneCredentialInstance default: return AKSKCredentialInstance } } func (none *NoneCredential) Sign(request *request.HttpRequest, credential *Credential) error { request.BuildQueryParamsString() return nil } func (none *NoneCredential) Encrypt(data []byte, publicKey string) (string, error) { return string(data), nil } func (none *NoneCredential) Decrypt(originStr string, privateKey string) (string, error) { return originStr, nil } func (mop *MopCredential) Sign(request *request.HttpRequest, credential *Credential) error { if utils.IsUnSet(credential.PrivateKey) { return errs.NewInvalidParameterError("RSA private key can not be null", nil) } request.ConvertQueryParamsFromPath() parameters := make(map[string]string) for k, v := range request.QueryParams { if utils.IsSet(k) { parameters[k] = v } } flowdId := utils.Nonce() parameters["flowdId"] = flowdId keys := make([]string, len(parameters)) index := 0 for key := range parameters { keys[index] = key index++ } sort.Strings(keys) builder := strings.Builder{} pos := 0 paramsLen := len(keys) for _, key := range keys { value := parameters[key] builder.WriteString(utils.PercentEncode(key)) builder.WriteString(consts.QuerySeparator) builder.WriteString(utils.PercentEncode(value)) if pos != paramsLen-1 { builder.WriteString(consts.ParameterSeparator) pos++ } } canonicalQueryString := builder.String() keyBytes, err := utils.Base64Decode(*credential.PrivateKey) if err != nil { return err } privateKey, err := x509.ParsePKCS8PrivateKey(keyBytes) if err != nil { return errs.NewCredentialError("ParsePKCS8PrivateKey decode error", err) } rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey) if !ok { return errs.NewCredentialError("privateKey convert error", nil) } signature, err := utils.GenerateRSASignature(utils.StringToBytes(canonicalQueryString), rsaPrivateKey) if err != nil { return errs.NewCredentialError("GenerateRSASignature error", err) } signStr := utils.Base64Encode(signature) request.QueryParams["sign"] = signStr request.QueryParams["flowdId"] = flowdId GetCredentialManager(CredentialAkSk).Sign(request, credential) return nil } func (mop *MopCredential) Encrypt(data []byte, publicKey string) (string, error) { if utils.IsUnSet(publicKey) { return "", errs.NewInvalidParameterError("RSA public key can not be null", nil) } keyBytes, err := utils.Base64Decode(publicKey) if err != nil { return "", errs.NewInvalidParameterError("RSA public key is invalid", nil) } publicKeyInterface, err := x509.ParsePKIXPublicKey(keyBytes) if err != nil { return "", errs.NewCredentialError("ParsePKIXPublicKey decode error", nil) } rsaPublicKey, ok := publicKeyInterface.(*rsa.PublicKey) if !ok { return "", errs.NewCredentialError("publicKey convert error", nil) } var encryptedData []byte for len(data) > 0 { var chunk []byte if len(data) > 64 { chunk, err = rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, data[:64]) data = data[64:] } else { chunk, err = rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, data) data = nil } if err != nil { return "", errs.NewCredentialError("RSAPublicKeyEncrypt error", err) } encryptedData = append(encryptedData, chunk...) } return utils.Base64Encode(encryptedData), nil } func (mop *MopCredential) Decrypt(originStr string, privateKey string) (string, error) { if utils.IsUnSet(privateKey) { return "", errs.NewInvalidParameterError("RSA private key can not be null", nil) } data, err := utils.Base64Decode(originStr) if err != nil { return "", err } keyBytes, err := utils.Base64Decode(privateKey) if err != nil { return "", errs.NewInvalidParameterError("RSA private key is invalid", nil) } privateKeyInterface, err := x509.ParsePKCS8PrivateKey(keyBytes) if err != nil { return "", errs.NewCredentialError("ParsePKCS8PrivateKey decode error", nil) } rsaPrivateKey, ok := privateKeyInterface.(*rsa.PrivateKey) if !ok { return "", errs.NewCredentialError("privateKey convert error", nil) } var decryptedData []byte for len(data) > 0 { var chunk []byte if len(data) > 75 { chunk, err = rsa.DecryptPKCS1v15(rand.Reader, rsaPrivateKey, data[:75]) data = data[75:] } else { chunk, err = rsa.DecryptPKCS1v15(rand.Reader, rsaPrivateKey, data) data = nil } if err != nil { return "", errs.NewCredentialError("RSAPrivateKeyDecrypt error", err) } decryptedData = append(decryptedData, chunk...) } return string(decryptedData), nil } func (aksk *AKSKCredential) Sign(request *request.HttpRequest, credential *Credential) error { request.ConvertQueryParamsFromPath() params := make(map[string]string) for key, value := range request.QueryParams { params[key] = value } params[consts.AccessKey] = *credential.AccessKey loc, _ := time.LoadLocation("Asia/Shanghai") var now time.Time if loc != nil { now = time.Now().In(loc) } else { now = time.Now() } params[consts.Timetamp] = now.Format(consts.TimestampFormat) params[consts.SignatureMethod] = consts.SignatureMethodValue params[consts.SignatureVersion] = consts.SignatureVersionValue params[consts.SignatureNonce] = utils.Nonce() keys := make([]string, len(params)) index := 0 for key := range params { keys[index] = key index++ } sort.Strings(keys) builder := strings.Builder{} pos := 0 paramsLen := len(keys) for _, key := range keys { value := params[key] builder.WriteString(utils.PercentEncode(key)) builder.WriteString(consts.QuerySeparator) builder.WriteString(utils.PercentEncode(value)) if pos != paramsLen-1 { builder.WriteString(consts.ParameterSeparator) pos++ } } canonicalQueryString := builder.String() hashString := utils.ConvertToHexString(utils.Sha256Encode(canonicalQueryString)) unescapedPath, err := url.QueryUnescape(request.Path) if nil != err { return errs.NewSignatureError(err.Error(), err) } builder.Reset() builder.WriteString(strings.ToUpper(request.Method)) builder.WriteString(consts.LineSeparator) builder.WriteString(utils.PercentEncode(unescapedPath)) builder.WriteString(consts.LineSeparator) builder.WriteString(hashString) stringToSign := builder.String() signature := utils.ConvertToHexString(utils.HmacSha256(stringToSign, consts.SecretKeyPrefix+*credential.SecretKey)) builder.Reset() builder.WriteString(unescapedPath) builder.WriteString(consts.QueryStartSymbol) builder.WriteString(canonicalQueryString) builder.WriteString(consts.ParameterSeparator) builder.WriteString(consts.Signature) builder.WriteString(consts.QuerySeparator) builder.WriteString(utils.PercentEncode(signature)) request.Path = builder.String() return nil } func (aksk *AKSKCredential) Encrypt(data []byte, publicKey string) (string, error) { return string(data), nil } func (aksk *AKSKCredential) Decrypt(originStr string, privateKey string) (string, error) { return originStr, nil } func CredentialTypePointer(a CredentialType) *CredentialType { return &a } func EncryptionTypePointer(a EncryptionType) *EncryptionType { return &a }