同步密钥
This commit is contained in:
commit
7033b0ce5f
57
main.go
57
main.go
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
|
|
@ -62,8 +61,7 @@ func init() {
|
||||||
os.MkdirAll(proto.CID_BASE_DIR+"workspace", os.ModePerm)
|
os.MkdirAll(proto.CID_BASE_DIR+"workspace", os.ModePerm)
|
||||||
//读取配置文件
|
//读取配置文件
|
||||||
//文件地址/home/videoplayer/vp.conf
|
//文件地址/home/videoplayer/vp.conf
|
||||||
//configPath := "/home/videoplayer/vp.conf"
|
configPath := "/home/videoplayer/vp.conf"
|
||||||
configPath := "/etc/vp-app/vp.conf"
|
|
||||||
//读取配置文件
|
//读取配置文件
|
||||||
err := proto.ReadConfig(configPath)
|
err := proto.ReadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -106,17 +104,17 @@ func JWTAuthMiddleware() gin.HandlerFunc {
|
||||||
if tokenString == "" {
|
if tokenString == "" {
|
||||||
tokenString = c.Query("token")
|
tokenString = c.Query("token")
|
||||||
}
|
}
|
||||||
for k, _ := range proto.Url_map {
|
//如果请求为login或register,则不需要验证token
|
||||||
if strings.Contains(c.Request.URL.Path, k) {
|
//for k, _ := range proto.Url_map {
|
||||||
log.Println("need not check token:", c.Request.URL.Path)
|
// if strings.Contains(c.Request.URL.Path, k) {
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if proto.Url_map[c.Request.URL.Path] == true { //查看是否在不需要token的url中
|
|
||||||
// c.Next()
|
// c.Next()
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
//}
|
||||||
|
if proto.Url_map[c.Request.URL.Path] == true { //查看是否在不需要token的url中
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
if tokenString == "" {
|
if tokenString == "" {
|
||||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"message": "unauthorized", "error": "token is empty", "code": proto.TokenIsNull})
|
c.AbortWithStatusJSON(http.StatusOK, gin.H{"message": "unauthorized", "error": "token is empty", "code": proto.TokenIsNull})
|
||||||
return
|
return
|
||||||
|
|
@ -147,34 +145,22 @@ func JWTAuthMiddleware() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proto.SigningKeyRWLock.RLock() //加读锁
|
||||||
// 使用加密secret 解析 JWT 令牌
|
// 使用加密secret 解析 JWT 令牌
|
||||||
//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
// return proto.SigningKey, nil
|
|
||||||
//})
|
|
||||||
token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
|
|
||||||
// 验证签名算法
|
|
||||||
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
||||||
return nil, jwt.ErrSignatureInvalid
|
|
||||||
}
|
|
||||||
return proto.SigningKey, nil
|
return proto.SigningKey, nil
|
||||||
})
|
})
|
||||||
// 错误处理
|
proto.SigningKeyRWLock.RUnlock()
|
||||||
if err != nil {
|
|
||||||
var ve *jwt.ValidationError
|
// 验证令牌
|
||||||
if errors.As(err, &ve) {
|
if err != nil || !token.Valid {
|
||||||
switch {
|
c.AbortWithStatusJSON(http.StatusOK, gin.H{
|
||||||
case ve.Errors&jwt.ValidationErrorMalformed != 0:
|
"message": "NOT_LOGIN",
|
||||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"error": "Malformed token:" + err.Error(), "code": proto.TokenInvalid})
|
"error": "Invalid token",
|
||||||
case ve.Errors&jwt.ValidationErrorExpired != 0:
|
"code": proto.TokenExpired,
|
||||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"error": "Token expired:" + err.Error(), "code": proto.TokenExpired})
|
})
|
||||||
case ve.Errors&jwt.ValidationErrorNotValidYet != 0:
|
|
||||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"error": "Token not active yet:" + err.Error(), "code": proto.TokenInvalid})
|
|
||||||
default:
|
|
||||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"error": "Invalid token:" + err.Error(), "code": proto.TokenInvalid})
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 将用户信息添加到上下文中
|
// 将用户信息添加到上下文中
|
||||||
id := token.Claims.(jwt.MapClaims)["id"]
|
id := token.Claims.(jwt.MapClaims)["id"]
|
||||||
|
|
@ -187,9 +173,9 @@ func JWTAuthMiddleware() gin.HandlerFunc {
|
||||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"message": "unauthorized", "error": "no function permission", "code": proto.NoPermission})
|
c.AbortWithStatusJSON(http.StatusOK, gin.H{"message": "unauthorized", "error": "no function permission", "code": proto.NoPermission})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 继续处理请求
|
// 继续处理请求
|
||||||
c.Next()
|
c.Next()
|
||||||
//log.Println("JWT token is valid, user ID:", token.Claims.(jwt.MapClaims)["id"], " path:", c.Request.URL.Path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,6 +189,7 @@ func myTask() {
|
||||||
//其它定时任务-通用
|
//其它定时任务-通用
|
||||||
RunGeneralCron()
|
RunGeneralCron()
|
||||||
service.ShellWillRunFromServer()
|
service.ShellWillRunFromServer()
|
||||||
|
service.SyncTokenSecretFromUserCenter()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Config ConfigStruct
|
var Config ConfigStruct
|
||||||
|
|
@ -13,6 +14,13 @@ var SigningKey = []byte{}
|
||||||
var Url_map = map[string]bool{"/login": true, "/register": true, "/uuid": true, "/gqr": true, "/cid/callback": true, "/tool/monitor": true, "/user/sync": true, "/tool/file/": true, "/user/reset": true} // 不需要token验证的url
|
var Url_map = map[string]bool{"/login": true, "/register": true, "/uuid": true, "/gqr": true, "/cid/callback": true, "/tool/monitor": true, "/user/sync": true, "/tool/file/": true, "/user/reset": true} // 不需要token验证的url
|
||||||
var Per_menu_map = map[string]int{"/video/": 1, "/device/": 2, "/cid/": 3}
|
var Per_menu_map = map[string]int{"/video/": 1, "/device/": 2, "/cid/": 3}
|
||||||
var File_Type = map[string]int{"im": 1, "avatar": 2, "file": 3, "config": 4} // 文件类型
|
var File_Type = map[string]int{"im": 1, "avatar": 2, "file": 3, "config": 4} // 文件类型
|
||||||
|
|
||||||
|
// 配置读写锁
|
||||||
|
var ConfigRWLock = &sync.RWMutex{}
|
||||||
|
var SigningKeyRWLock = &sync.RWMutex{}
|
||||||
|
|
||||||
|
var SyncSecretReqLog int64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MYSQL_USER = "video_t2"
|
MYSQL_USER = "video_t2"
|
||||||
MYSQL_DB = "video_t2"
|
MYSQL_DB = "video_t2"
|
||||||
|
|
@ -86,6 +94,30 @@ type ConfigStruct struct {
|
||||||
MONITOR_SERVER_TOKEN string `json:"monitor_server_token"` // 监控服务器token,用于状态监控及邮件通知
|
MONITOR_SERVER_TOKEN string `json:"monitor_server_token"` // 监控服务器token,用于状态监控及邮件通知
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WriteConfigToFile() {
|
||||||
|
//系统是linux、macos还是windows
|
||||||
|
var configPath string
|
||||||
|
if os.Getenv("OS") == "Windows_NT" {
|
||||||
|
configPath = "D:/Code/videoplayer/vp.conf"
|
||||||
|
} else if os.Getenv("OS") == "linux" {
|
||||||
|
//文件地址/home/saw-ai/saw-ai.conf
|
||||||
|
configPath = "/home/videoplayer/vp.conf"
|
||||||
|
} else {
|
||||||
|
configPath = "/home/videoplayer/vp.conf"
|
||||||
|
}
|
||||||
|
configData, err := json.MarshalIndent(Config, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("WriteConfigToFile json marshal error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = os.WriteFile(configPath, configData, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("WriteConfigToFile write file error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("WriteConfigToFile write config to file success")
|
||||||
|
}
|
||||||
|
|
||||||
// 读取配置文件
|
// 读取配置文件
|
||||||
func ReadConfig(path string) error {
|
func ReadConfig(path string) error {
|
||||||
//查看配置文件是否存在,不存在则创建
|
//查看配置文件是否存在,不存在则创建
|
||||||
|
|
|
||||||
|
|
@ -117,3 +117,29 @@ type ResponseOAuth struct {
|
||||||
Email string `json:"email" form:"email"`
|
Email string `json:"email" form:"email"`
|
||||||
Token string `json:"token" form:"token"`
|
Token string `json:"token" form:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SecretSyncSettings struct {
|
||||||
|
Prev string `json:"prev"` // 前一个secret
|
||||||
|
Curr string `json:"curr"` // 当前的secret
|
||||||
|
Next string `json:"next"` // 下一个secret
|
||||||
|
CurrExpectedExpiration int64 `json:"curr_expected_expiration"` // 当前密钥的预期过期时间戳
|
||||||
|
PrevEndTimestamp int64 `json:"prev_end_timestamp"` // 前一个secret的结束时间戳
|
||||||
|
CurrStartTimestamp int64 `json:"curr_start_timestamp"` // 当前secret的开始时间戳
|
||||||
|
NextStartTimestamp int64 `json:"next_start_timestamp"` // 下一个secret的开始时间戳
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncSystemConfigRequest struct {
|
||||||
|
//时间戳
|
||||||
|
Timestamp int64 `json:"timestamp" form:"timestamp"` // 时间戳
|
||||||
|
//设备标识
|
||||||
|
DeviceApp string `json:"device_app" form:"device_app"` // 设备标识
|
||||||
|
//加密信息
|
||||||
|
Sign string `json:"sign" form:"sign"` // 加密信息,app的secret加密后的值
|
||||||
|
SecretKeyMd5 string `json:"secret_key_md5" form:"secret_key_md5"` // 密钥的MD5值,用于验证
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestSyncSecretResp struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"videoplayer/proto"
|
"videoplayer/proto"
|
||||||
"videoplayer/worker"
|
"videoplayer/worker"
|
||||||
|
|
@ -184,3 +187,133 @@ func DelMonitorDeviceListWithStatus(userId int, deviceReq []proto.GetMonitorDevi
|
||||||
}
|
}
|
||||||
return delDevices, err
|
return delDevices, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新token密钥
|
||||||
|
func SyncTokenSecretFromUserCenter() {
|
||||||
|
secretSettings, err := GetTokenSecretFromUserCenter()
|
||||||
|
//写入redis
|
||||||
|
secretSettingsBytes, err2 := json.Marshal(secretSettings)
|
||||||
|
if err2 != nil {
|
||||||
|
log.Println("SyncTokenSecretFromUserCenter json marshal error:", err2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if proto.SyncSecretReqLog%100 == 0 {
|
||||||
|
log.Println("SyncTokenSecretFromUserCenter req data:", string(secretSettingsBytes))
|
||||||
|
}
|
||||||
|
worker.SetRedis("secret_sync_settings", string(secretSettingsBytes)) //将密钥信息存入redis
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SyncTokenSecretFromUserCenter error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if secretSettings.Curr != "" && secretSettings.Curr != proto.TOKEN_SECRET && secretSettings.Next == "" { //如果当前密钥不为空且不等于配置文件中的密钥,并且下一个密钥为空,则需要更新配置文件中的密钥
|
||||||
|
log.Printf("SyncTokenSecretFromUserCenter current secret is not equal to config secret, current: %s, config: %s\n", secretSettings.Curr, proto.TOKEN_SECRET)
|
||||||
|
//如果当前密钥与配置文件中的密钥不一致,则需要更新配置文件中的密钥
|
||||||
|
proto.SigningKeyRWLock.Lock()
|
||||||
|
proto.SigningKey = []byte(secretSettings.Curr)
|
||||||
|
proto.Config.TOKEN_SECRET = secretSettings.Curr
|
||||||
|
proto.SigningKeyRWLock.Unlock()
|
||||||
|
//配置写回文件
|
||||||
|
go proto.WriteConfigToFile()
|
||||||
|
log.Println("SyncTokenSecretFromUserCenter current secret updated successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
if secretSettings.Next == "" {
|
||||||
|
log.Println("SyncTokenSecretFromUserCenter secret is empty")
|
||||||
|
return
|
||||||
|
} else if proto.SyncSecretReqLog%100 == 0 {
|
||||||
|
go SetNextSecretToCurrent(*secretSettings) //异步设置下一个密钥为当前密钥
|
||||||
|
}
|
||||||
|
|
||||||
|
proto.SyncSecretReqLog++ //记录同步密钥请求次数
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetNextSecretToCurrent(secret_copy proto.SecretSyncSettings) {
|
||||||
|
var secret_sync_settings proto.SecretSyncSettings
|
||||||
|
redisKey := "secret_sync_settings"
|
||||||
|
settingsStr := worker.GetRedis(redisKey)
|
||||||
|
err := json.Unmarshal([]byte(settingsStr), &secret_sync_settings)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error decoding secret sync settings:", err)
|
||||||
|
} else {
|
||||||
|
//如果当前密钥的下一个密钥与传入的密钥不一致,则不进行设置
|
||||||
|
if secret_copy.Next != secret_sync_settings.Next {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//获取需要等待时间
|
||||||
|
waitTime := secret_sync_settings.NextStartTimestamp - worker.GetCurrentTimestamp()
|
||||||
|
if waitTime > 0 {
|
||||||
|
log.Printf("Waiting for %d seconds before setting the next secret as current secret\n", waitTime)
|
||||||
|
time.Sleep(time.Duration(waitTime) * time.Second) //等待时间
|
||||||
|
} else {
|
||||||
|
log.Println("No need to wait, setting the next secret as current secret immediately")
|
||||||
|
}
|
||||||
|
//设置下一个密钥为当前密钥
|
||||||
|
secret_sync_settings.Prev = secret_sync_settings.Curr
|
||||||
|
secret_sync_settings.PrevEndTimestamp = worker.GetCurrentTimestamp()
|
||||||
|
secret_sync_settings.Curr = secret_sync_settings.Next
|
||||||
|
secret_sync_settings.Next = ""
|
||||||
|
secret_sync_settings.CurrStartTimestamp = secret_sync_settings.PrevEndTimestamp
|
||||||
|
|
||||||
|
//设置当前程序的密钥
|
||||||
|
//获取写锁
|
||||||
|
proto.SigningKeyRWLock.Lock()
|
||||||
|
defer proto.SigningKeyRWLock.Unlock()
|
||||||
|
proto.SigningKey = []byte(secret_sync_settings.Curr)
|
||||||
|
|
||||||
|
proto.Config.TOKEN_SECRET = secret_sync_settings.Curr
|
||||||
|
//配置写回文件
|
||||||
|
go proto.WriteConfigToFile()
|
||||||
|
}
|
||||||
|
settinsStr, err2 := json.Marshal(secret_sync_settings)
|
||||||
|
if err2 != nil {
|
||||||
|
log.Println("Error encoding set secret sync settings:", err2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
worker.SetRedis(redisKey, string(settinsStr)) //将当前的密钥信息存入redis
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取token密钥请求
|
||||||
|
func GetTokenSecretFromUserCenter() (*proto.SecretSyncSettings, error) {
|
||||||
|
url := "https://uc.ljsea.top/tool/sync_system_config"
|
||||||
|
var req proto.SyncSystemConfigRequest
|
||||||
|
proto.SigningKeyRWLock.Lock()
|
||||||
|
defer proto.SigningKeyRWLock.Unlock()
|
||||||
|
req.SecretKeyMd5 = worker.GenerateMD5(string(proto.SigningKey))
|
||||||
|
req.DeviceApp = "StuAcaWorksAI"
|
||||||
|
req.Timestamp = worker.GetCurrentTimestamp()
|
||||||
|
req.Sign = worker.GenerateMD5(req.SecretKeyMd5 + req.DeviceApp + strconv.FormatInt(req.Timestamp, 10))
|
||||||
|
reqBytes, err2 := json.Marshal(req)
|
||||||
|
if err2 != nil {
|
||||||
|
log.Println("GetTokenSecretFromUserCenter json marshal error:", err2)
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
err, resp := worker.DoPostRequestJSON(url, reqBytes, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("GetTokenSecretFromUserCenter post request error:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var respObject proto.RequestSyncSecretResp
|
||||||
|
err = json.Unmarshal(resp, &respObject)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("GetTokenSecretFromUserCenter json unmarshal error:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//对称加密密钥。通过密钥加 secret_key 取md5
|
||||||
|
secretKeyMd5 := worker.GenerateMD5(proto.TOKEN_SECRET + "_sync_secret")
|
||||||
|
|
||||||
|
//解密返回数据
|
||||||
|
dataContent, err2 := worker.AESDecrypt(respObject.Data, []byte(secretKeyMd5))
|
||||||
|
if err2 != nil {
|
||||||
|
log.Println("GetTokenSecretFromUserCenter aes decrypt error:", err2)
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
var secretResp proto.SecretSyncSettings
|
||||||
|
err = json.Unmarshal(dataContent, &secretResp)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("GetTokenSecretFromUserCenter json unmarshal error:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &secretResp, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,93 @@
|
||||||
package worker
|
package worker
|
||||||
|
|
||||||
import "math/rand"
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
mrand "math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func GetRandomString(l int) string {
|
func GetRandomString(l int) string {
|
||||||
str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
bytes := []byte(str)
|
bytes := []byte(str)
|
||||||
var result []byte
|
var result []byte
|
||||||
for i := 0; i < l; i++ {
|
for i := 0; i < l; i++ {
|
||||||
result = append(result, bytes[rand.Intn(len(bytes))])
|
result = append(result, bytes[mrand.Intn(len(bytes))])
|
||||||
}
|
}
|
||||||
return string(result)
|
return string(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCurrentTimestamp() int64 {
|
||||||
|
// 获取当前时间戳
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AESEncrypt 函数使用AES-GCM算法对明文进行加密
|
||||||
|
func AESEncrypt(plaintext []byte, key []byte) (string, error) {
|
||||||
|
// 创建AES加密块
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建GCM模式的加密器
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机的nonce
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行加密操作
|
||||||
|
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
|
||||||
|
|
||||||
|
// 将加密结果转换为Base64编码字符串
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AESDecrypt 函数使用AES-GCM算法对密文进行解密
|
||||||
|
func AESDecrypt(ciphertext string, key []byte) ([]byte, error) {
|
||||||
|
// 将Base64编码的密文转换为字节切片
|
||||||
|
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AES加密块
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建GCM模式的解密器
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取nonce和真正的密文
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
if len(data) < nonceSize {
|
||||||
|
return nil, fmt.Errorf("密文长度过短")
|
||||||
|
}
|
||||||
|
nonce, ciphertextBytes := data[:nonceSize], data[nonceSize:]
|
||||||
|
|
||||||
|
// 执行解密操作
|
||||||
|
return gcm.Open(nil, nonce, ciphertextBytes, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateMD5(secretKey string) string {
|
||||||
|
hasher := md5.New()
|
||||||
|
hasher.Write([]byte(secretKey))
|
||||||
|
return hex.EncodeToString(hasher.Sum(nil)) // 将二进制数据转换为十六进制字符串
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue