example/http_authorization.go (140 lines of code) (raw):
package main
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"sort"
"strings"
)
func main() {
headers := map[string]string{
"Connection": "keep-alive",
"x-mns-message-id": "AC10020B0019681A95159E591D10****",
"Content-Type": "text/plain;charset=utf-8",
"Content-md5": "5B4682CCA07FFB080FFE0A9D9821****",
"x-mns-topic-owner": "****",
"x-mns-topic-name": "HTTP-test",
"x-mns-subscriber": "****",
"x-mns-subscription-name": "sub-test-header",
"x-mns-publish-time": "1730368640272",
"x-mns-request-id": "672354803035301900003***",
"Date": "Thu, 31 Oct 2024 09:57:20 GMT",
"x-mns-version": "2015-06-06",
"User-Agent": "Aliyun Notification Service Agent",
"x-mns-signing-cert-url": "aHR0cHM6Ly9tbnN0ZXN0Lm9zcy1jbi1oYW5nemhvdS5hbGl5dW5****",
"Authorization": "pg5Prc+ADujqjHbK1XKMK+o+aZjtkAntpR19s2B0T1k1deilZ5UgUFoIsKmLbgirN+1m2srdh****",
"Host": "****",
"Content-length": "40",
"Accept": "*/*",
}
method := "POST"
path := "/api/test"
if authenticateWithHeaderMap(method, path, toLowercaseKeys(headers)) {
fmt.Println("Signature verification succeeded")
} else {
fmt.Println("Signature verification failed")
}
}
func authenticateWithResponse(method, path string, resp *http.Response) bool {
headersMap := headersToMap(resp)
return authenticateWithHeaderMap(method, path, headersMap)
}
func authenticateWithHeaderMap(method, path string, headers map[string]string) bool {
// Get string to sign
var serviceHeaders []string
for k, v := range headers {
if strings.HasPrefix(strings.ToLower(k), "x-mns-") {
serviceHeaders = append(serviceHeaders, fmt.Sprintf("%s:%s", k, v))
}
}
sort.Strings(serviceHeaders)
serviceStr := strings.Join(serviceHeaders, "\n")
var signHeaderList []string
for _, key := range []string{"content-md5", "content-type", "date"} {
if val, ok := headers[key]; ok {
signHeaderList = append(signHeaderList, val)
} else {
signHeaderList = append(signHeaderList, "")
}
}
str2sign := fmt.Sprintf("%s\n%s\n%s\n%s", method, strings.Join(signHeaderList, "\n"), serviceStr, path)
fmt.Println("String to sign:", str2sign)
// 获取证书的URL
certURLBase64, ok := headers["x-mns-signing-cert-url"]
if !ok {
fmt.Println("x-mns-signing-cert-url Header not found")
return false
}
certURLBytes, err := base64.StdEncoding.DecodeString(certURLBase64)
if err != nil {
fmt.Println("Failed to decode base64 cert URL:", err)
return false
}
certURL := string(certURLBytes)
fmt.Println("x-mns-signing-cert-url:\t", certURL)
// 根据URL获取证书,并从证书中获取公钥
resp, err := http.Get(certURL)
if err != nil {
fmt.Println("Failed to fetch certificate:", err)
return false
}
//goland:noinspection GoUnhandledErrorResult
defer resp.Body.Close()
//goland:noinspection GoDeprecation
certData, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Failed to read certificate:", err)
return false
}
block, _ := pem.Decode(certData)
if block == nil || block.Type != "CERTIFICATE" {
fmt.Println("Failed to decode PEM block containing the certificate")
return false
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
fmt.Println("Failed to parse certificate:", err)
return false
}
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
fmt.Println("Failed to cast public key to RSA public key")
return false
}
// 对Authorization字段做Base64解码
signatureBase64, ok := headers["authorization"]
if !ok {
fmt.Println("Authorization Header not found")
return false
}
signature, err := base64.StdEncoding.DecodeString(signatureBase64)
if err != nil {
fmt.Println("Failed to decode base64 signature:", err)
return false
}
// 认证
hash := sha1.New()
hash.Write([]byte(str2sign))
digest := hash.Sum(nil)
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA1, digest, signature)
if err != nil {
fmt.Println("Signature verification failed:", err)
return false
}
return true
}
func headersToMap(resp *http.Response) map[string]string {
headersMap := make(map[string]string)
for key, values := range resp.Header {
// 连接多个值为一个字符串,使用逗号分隔
// map 键值全小写
lowercaseKey := strings.ToLower(key)
headersMap[lowercaseKey] = strings.Join(values, ",")
}
return headersMap
}
// HTTP header 不区分大小写,将 map 的 keys 转换为全小写
func toLowercaseKeys(input map[string]string) map[string]string {
output := make(map[string]string)
for k, v := range input {
lowercaseKey := strings.ToLower(k)
output[lowercaseKey] = v
}
return output
}