最近專案準備從laravel遷移到go,但是為了最小範圍的影響,只能一部分一部分的慢慢遷移,所以就會涉及到相容laravel加密解密的問題。現將程式碼記錄到這裡,以備後續檢視。
加密方式
AES-256-CBC
金鑰格式
支援常規32位字串金鑰,也支援base64格式的金鑰
程式碼
package main
import (
"crypto/hmac"
"crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "os" "strings"
"github.com/forgoer/openssl" "github.com/techoner/gophp/serialize")
//支援兩種key格式
//const EncrypterKey = "9bcv6ioh0s51pjlaw2d7u8rt3fyqnxge"
//const EncrypterKey = "base64:UXn0F1XSd2peV2M6mPtEWfeKPVlf5p+j5NqCd3+4/AA="
//加密
func Encrypt(value string) (string, error) {
iv := make([]byte, 16)
_, err := rand.Read(iv)
if err != nil {
return "", err
}
//反序列化
message, err := serialize.Marshal(value)
if err != nil {
return "", err
}
key := getKey()
//加密value
res, err := openssl.AesCBCEncrypt(message, []byte(key), iv, openssl.PKCS7_PADDING)
if err != nil {
return "", err
}
//base64加密
resVal := base64.StdEncoding.EncodeToString(res)
resIv := base64.StdEncoding.EncodeToString(iv)
//生成mac值
data := resIv + resVal
mac := computeHmacSha256(data, key)
//構造ticket結構
ticket := make(map[string]interface{})
ticket["iv"] = resIv
ticket["mac"] = mac
ticket["value"] = resVal
//json序列化
resTicket, err := json.Marshal(ticket)
if err != nil {
return "", err
}
//base64加密ticket
ticketR := base64.StdEncoding.EncodeToString(resTicket)
return ticketR, nil
}
//解密
func Decrypt(value string) (string, error) {
//base64解密
token, err := base64.StdEncoding.DecodeString(value)
fmt.Println("token---", string(token))
if err != nil {
return "", err
}
//json反序列化
tokenJson := make(map[string]string)
err = json.Unmarshal(token, &tokenJson)
fmt.Println("tokenJson---", tokenJson)
if err != nil {
return "", err
}
tokenJsonIv, okIv := tokenJson["iv"]
tokenJsonValue, okValue := tokenJson["value"]
tokenJsonMac, okMac := tokenJson["mac"]
if !okIv || !okValue || !okMac {
return "", errors.New("value is not full")
}
key := getKey()
//mac檢查,防止資料篡改
data := tokenJsonIv + tokenJsonValue
check := checkMAC(data, tokenJsonMac, key)
if !check {
return "", errors.New("mac valid failed")
}
//base64解密iv和value
tokenIv, err := base64.StdEncoding.DecodeString(tokenJsonIv)
if err != nil {
return "", err
}
tokenValue, err := base64.StdEncoding.DecodeString(tokenJsonValue)
if err != nil {
return "", err
}
//aes解密value
dst, err := openssl.AesCBCDecrypt(tokenValue, []byte(key), tokenIv, openssl.PKCS7_PADDING)
fmt.Println("dst", string(dst))
if err != nil {
return "", err
}
//反序列化
res, err := serialize.UnMarshal(dst)
if err != nil {
return "", err
}
return res.(string), nil
}
//比較預期的hash和實際的hash
func checkMAC(message, msgMac, secret string) bool {
expectedMAC := computeHmacSha256(message, secret)
fmt.Println(expectedMAC, msgMac)
return hmac.Equal([]byte(expectedMAC), []byte(msgMac))
}
//計算mac值
func computeHmacSha256(message string, secret string) string {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(message))
sha := hex.EncodeToString(h.Sum(nil))
return sha
}
//處理金鑰
func getKey() string {
appKey := os.Getenv("APP_KEY")
if strings.HasPrefix(appKey, "base64:") {
split := appKey[7:]
if key, err := base64.StdEncoding.DecodeString(split); err == nil {
return string(key)
}
return split
}
return appKey
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結