go 实现 Laravel 的 encrypt () 和 decrypt () 方法

最近项目准备从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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!