distribution/vendor/github.com/denverdino/aliyungo/oss/authenticate_callback.go

89 lines
2.2 KiB
Go
Raw Normal View History

package oss
import (
"crypto"
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"io/ioutil"
"net/http"
"regexp"
"strings"
"sync"
)
type authenticationType struct {
lock *sync.RWMutex
certificate map[string]*rsa.PublicKey
}
var (
authentication = authenticationType{lock: &sync.RWMutex{}, certificate: map[string]*rsa.PublicKey{}}
urlReg = regexp.MustCompile(`^http(|s)://gosspublic.alicdn.com/[0-9a-zA-Z]`)
)
//验证OSS向业务服务器发来的回调函数。
//该方法是并发安全的
//pubKeyUrl 回调请求头中[x-oss-pub-key-url]一项以Base64编码
//reqUrl oss所发来请求的url由path+query组成
//reqBody oss所发来请求的body
//authorization authorization为回调头中的签名
func AuthenticateCallBack(pubKeyUrl, reqUrl, reqBody, authorization string) error {
//获取证书url
keyURL, err := base64.URLEncoding.DecodeString(pubKeyUrl)
if err != nil {
return err
}
url := string(keyURL)
//判断证书是否来自于阿里云
if !urlReg.Match(keyURL) {
return errors.New("certificate address error")
}
//获取文件名
rs := []rune(url)
filename := string(rs[strings.LastIndex(url, "/") : len(rs)-1])
authentication.lock.RLock()
certificate := authentication.certificate[filename]
authentication.lock.RUnlock()
//内存中没有证书,下载
if certificate == nil {
authentication.lock.Lock()
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
block, _ := pem.Decode(body)
if block == nil {
return errors.New("certificate error")
}
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return err
}
certificate = pubKey.(*rsa.PublicKey)
authentication.certificate[filename] = certificate
authentication.lock.Unlock()
}
//证书准备完毕,开始验证
//解析签名
signature, err := base64.StdEncoding.DecodeString(authorization)
if err != nil {
return err
}
hashed := md5.New()
hashed.Write([]byte(reqUrl + "\n" + reqBody))
if err := rsa.VerifyPKCS1v15(certificate, crypto.MD5, hashed.Sum(nil), signature); err != nil {
return err
}
//验证通过
return nil
}