package oss import ( "bytes" "crypto" "crypto/md5" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "io/ioutil" "net/http" "net/url" "strings" "github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/request" ) // GetPublicKey 从回调请求或缓存中获取OSS的回调签名公钥 func GetPublicKey(r *http.Request) ([]byte, error) { var pubKey []byte // 尝试从缓存中获取 pub, exist := cache.Get("oss_public_key") if exist { return pub.([]byte), nil } // 从请求中获取 pubURL, err := base64.StdEncoding.DecodeString(r.Header.Get("x-oss-pub-key-url")) if err != nil { return pubKey, err } // 确保这个 public key 是由 OSS 颁发的 if !strings.HasPrefix(string(pubURL), "http://gosspublic.alicdn.com/") && !strings.HasPrefix(string(pubURL), "https://gosspublic.alicdn.com/") { return pubKey, errors.New("公钥URL无效") } // 获取公钥 client := request.HTTPClient{} body, err := client.Request("GET", string(pubURL), nil). CheckHTTPResponse(200). GetResponse() if err != nil { return pubKey, err } // 写入缓存 _ = cache.Set("oss_public_key", []byte(body), 86400*7) return []byte(body), nil } func getRequestMD5(r *http.Request) ([]byte, error) { var byteMD5 []byte // 获取请求正文 body, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { return byteMD5, err } r.Body = ioutil.NopCloser(bytes.NewReader(body)) strURLPathDecode, err := url.PathUnescape(r.URL.Path) if err != nil { return byteMD5, err } strAuth := fmt.Sprintf("%s\n%s", strURLPathDecode, string(body)) md5Ctx := md5.New() md5Ctx.Write([]byte(strAuth)) byteMD5 = md5Ctx.Sum(nil) return byteMD5, nil } // VerifyCallbackSignature 验证OSS回调请求 func VerifyCallbackSignature(r *http.Request) error { bytePublicKey, err := GetPublicKey(r) if err != nil { return err } byteMD5, err := getRequestMD5(r) if err != nil { return err } strAuthorizationBase64 := r.Header.Get("authorization") if strAuthorizationBase64 == "" { return errors.New("no authorization field in Request header") } authorization, _ := base64.StdEncoding.DecodeString(strAuthorizationBase64) pubBlock, _ := pem.Decode(bytePublicKey) if pubBlock == nil { return errors.New("pubBlock not exist") } pubInterface, err := x509.ParsePKIXPublicKey(pubBlock.Bytes) if (pubInterface == nil) || (err != nil) { return err } pub := pubInterface.(*rsa.PublicKey) errorVerifyPKCS1v15 := rsa.VerifyPKCS1v15(pub, crypto.MD5, byteMD5, authorization) if errorVerifyPKCS1v15 != nil { return errorVerifyPKCS1v15 } return nil }