复制项目

This commit is contained in:
kim.dev.6789
2026-01-14 22:35:45 +08:00
parent 305d526110
commit b7f8db7d08
297 changed files with 81784 additions and 0 deletions

248
pkg/common/util/aes.go Normal file
View File

@@ -0,0 +1,248 @@
// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"time"
"github.com/openimsdk/tools/errs"
)
const (
// AES密钥字符串
aesKeyString = "F9cLjK3FgkvcR6vaWZKEj3DYJfwrIHMULLwpmDAj553mpmlvMYoLNIqajLnhIsWq"
)
// DecryptVerifyCode 解密验证码
// encryptedText: Base64编码的加密字符串
// 返回: 解密后的明文(格式:验证码-时间戳)
func DecryptVerifyCode(encryptedText string) (string, error) {
// 解码Base64
encrypted, err := base64.StdEncoding.DecodeString(encryptedText)
if err != nil {
return "", errs.WrapMsg(err, "base64解码失败")
}
// 使用SHA-256哈希密钥字符串
keyBytes := sha256.Sum256([]byte(aesKeyString))
key := keyBytes[:]
// 创建AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return "", errs.WrapMsg(err, "创建AES cipher失败")
}
// 检查密文长度
if len(encrypted) < aes.BlockSize {
return "", errs.New("密文长度不足")
}
// 使用CBC模式固定IV全零
iv := make([]byte, aes.BlockSize) // 全零IV
mode := cipher.NewCBCDecrypter(block, iv)
// 解密
decrypted := make([]byte, len(encrypted))
mode.CryptBlocks(decrypted, encrypted)
// 去除PKCS7填充
decrypted = unpadPKCS7(decrypted)
return string(decrypted), nil
}
// unpadPKCS7 去除PKCS7填充
func unpadPKCS7(data []byte) []byte {
if len(data) == 0 {
return data
}
padLen := int(data[len(data)-1])
if padLen > len(data) || padLen == 0 {
return data
}
// 验证填充
for i := len(data) - padLen; i < len(data); i++ {
if data[i] != byte(padLen) {
return data
}
}
return data[:len(data)-padLen]
}
// ParseVerifyCodeWithTimestamp 解析验证码和时间戳
// format: "验证码-时间戳"
// 返回: 验证码字符串和时间戳(毫秒)
func ParseVerifyCodeWithTimestamp(decryptedText string) (string, int64, error) {
var code string
var timestamp int64
// 查找最后一个 "-" 分隔符
lastDashIndex := -1
for i := len(decryptedText) - 1; i >= 0; i-- {
if decryptedText[i] == '-' {
lastDashIndex = i
break
}
}
if lastDashIndex == -1 {
return "", 0, errs.New("格式错误:未找到时间戳分隔符")
}
code = decryptedText[:lastDashIndex]
timestampStr := decryptedText[lastDashIndex+1:]
// 解析时间戳
_, err := fmt.Sscanf(timestampStr, "%d", &timestamp)
if err != nil {
return "", 0, errs.WrapMsg(err, "解析时间戳失败")
}
return code, timestamp, nil
}
// ValidateTimestamp 验证时间戳是否在有效期内
// timestamp: 毫秒级时间戳
// maxAgeSeconds: 最大允许的秒数允许的时间差默认30秒
// 返回: 是否有效
func ValidateTimestamp(timestamp int64, maxAgeSeconds int64) bool {
if timestamp <= 0 {
return false
}
currentTime := getCurrentTimestamp()
diff := currentTime - timestamp
// 转换为毫秒
maxAgeMillis := maxAgeSeconds * 1000
// 时间戳不能是未来的允许5秒的时钟偏差
if diff < -5000 {
return false
}
// 时间差不能超过maxAgeSeconds秒
if diff > maxAgeMillis {
return false
}
return true
}
// getCurrentTimestamp 获取当前时间戳(毫秒)
func getCurrentTimestamp() int64 {
return time.Now().UnixMilli()
}
// padPKCS7 添加PKCS7填充
func padPKCS7(data []byte, blockSize int) []byte {
padLen := blockSize - len(data)%blockSize
pad := make([]byte, padLen)
for i := range pad {
pad[i] = byte(padLen)
}
return append(data, pad...)
}
// EncryptPhone 加密手机号
// phoneNumber: 手机号明文
// 返回: Base64编码的加密字符串
func EncryptPhone(phoneNumber string) (string, error) {
if phoneNumber == "" {
return "", errs.New("手机号不能为空")
}
// 使用SHA-256哈希密钥字符串
keyBytes := sha256.Sum256([]byte(aesKeyString))
key := keyBytes[:]
// 创建AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return "", errs.WrapMsg(err, "创建AES cipher失败")
}
// 使用CBC模式固定IV全零
iv := make([]byte, aes.BlockSize) // 全零IV
// 添加PKCS7填充
plaintext := padPKCS7([]byte(phoneNumber), aes.BlockSize)
// 加密
mode := cipher.NewCBCEncrypter(block, iv)
encrypted := make([]byte, len(plaintext))
mode.CryptBlocks(encrypted, plaintext)
// 编码为Base64
return base64.StdEncoding.EncodeToString(encrypted), nil
}
// DecryptPhone 解密手机号
// encryptedText: Base64编码的加密字符串
// 返回: 解密后的手机号明文
func DecryptPhone(encryptedText string) (string, error) {
// 复用DecryptVerifyCode的解密逻辑手机号没有时间戳格式直接解密
return DecryptVerifyCode(encryptedText)
}
// EncryptRealNameAuthData 加密实名认证数据(使用 AES-GCM 模式)
// data: JSON字符串例如: {"cardNo":"330329199001021122","realName":"李四"}
// key: AES密钥字符串与服务端相同的默认密钥
// 返回: Base64编码的加密字符串
func EncryptRealNameAuthData(data string, key string) (string, error) {
if data == "" {
return "", errs.New("数据不能为空")
}
if key == "" {
return "", errs.New("密钥不能为空")
}
// 使用 SHA256 哈希密钥字符串(与服务端保持一致)
// 注意:直接对密钥字符串进行哈希,而不是先转换为字节
hash := sha256.Sum256([]byte(key))
aesKey := hash[:]
// 创建AES cipher
block, err := aes.NewCipher(aesKey)
if err != nil {
return "", errs.WrapMsg(err, "创建AES cipher失败")
}
// 使用 GCM 模式
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", errs.WrapMsg(err, "创建 GCM 失败")
}
// 生成随机 nonce
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", errs.WrapMsg(err, "生成 nonce 失败")
}
// 加密
ciphertext := gcm.Seal(nonce, nonce, []byte(data), nil)
// 编码为Base64
return base64.StdEncoding.EncodeToString(ciphertext), nil
}