mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
* fix: 保存 LastResult 时截断字符串过长部分,以避免数据库报错 * refactor: 新增 entity.DbTaskBase 和 persistence.dbTaskBase, 用于实现数据库备份和恢复任务处理相关部分 * fix: aeskey变更后,解密密码出现数组越界访问错误 * fix: 时间属性为零值时,保存到 mysql 数据库报错 * refactor db.infrastructure.service.scheduler * feat: 实现立即备份功能 * refactor db.infrastructure.service.db_instance * refactor: 从数据库中获取数据库备份目录、mysql文件路径等配置信息 * fix: 数据库备份和恢复问题 * fix: 修改 .gitignore 文件,忽略数据库备份目录和数据库程序目录
281 lines
7.1 KiB
Go
281 lines
7.1 KiB
Go
package cryptox
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/aes"
|
||
"crypto/cipher"
|
||
"crypto/md5"
|
||
"crypto/rand"
|
||
"crypto/rsa"
|
||
"crypto/x509"
|
||
"encoding/base64"
|
||
"encoding/hex"
|
||
"encoding/pem"
|
||
"errors"
|
||
"mayfly-go/pkg/cache"
|
||
"mayfly-go/pkg/logx"
|
||
"os"
|
||
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
// md5
|
||
func Md5(str string) string {
|
||
h := md5.New()
|
||
h.Write([]byte(str))
|
||
return hex.EncodeToString(h.Sum(nil))
|
||
}
|
||
|
||
// bcrypt加密密码
|
||
func PwdHash(password string) string {
|
||
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||
return string(bytes)
|
||
}
|
||
|
||
// 检查密码是否一致
|
||
func CheckPwdHash(password, hash string) bool {
|
||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
|
||
}
|
||
|
||
// 生成RSA私钥和公钥字符串
|
||
// bits 证书大小
|
||
// @return privateKeyStr publicKeyStr error
|
||
func GenerateRSAKey(bits int) (string, string, error) {
|
||
var privateKeyStr, publicKeyStr string
|
||
|
||
//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
|
||
//Reader是一个全局、共享的密码用强随机数生成器
|
||
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
|
||
if err != nil {
|
||
return privateKeyStr, publicKeyStr, err
|
||
}
|
||
//保存私钥
|
||
//通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
|
||
X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
|
||
//构建一个pem.Block结构体对象
|
||
privateBlock := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
|
||
|
||
privateBuf := new(bytes.Buffer)
|
||
pem.Encode(privateBuf, &privateBlock)
|
||
privateKeyStr = privateBuf.String()
|
||
|
||
//保存公钥
|
||
//获取公钥的数据
|
||
publicKey := privateKey.PublicKey
|
||
//X509对公钥编码
|
||
X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
|
||
if err != nil {
|
||
return publicKeyStr, privateKeyStr, err
|
||
}
|
||
//创建一个pem.Block结构体对象
|
||
publicBlock := pem.Block{Type: "PUBLIC KEY", Bytes: X509PublicKey}
|
||
|
||
publicBuf := new(bytes.Buffer)
|
||
pem.Encode(publicBuf, &publicBlock)
|
||
publicKeyStr = publicBuf.String()
|
||
|
||
return privateKeyStr, publicKeyStr, nil
|
||
}
|
||
|
||
// rsa加密
|
||
func RsaEncrypt(publicKeyStr string, data []byte) ([]byte, error) {
|
||
block, _ := pem.Decode([]byte(publicKeyStr))
|
||
if block == nil {
|
||
return nil, errors.New("private key error")
|
||
}
|
||
|
||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return rsa.EncryptPKCS1v15(rand.Reader, pub.(*rsa.PublicKey), data)
|
||
}
|
||
|
||
// rsa解密
|
||
func RsaDecrypt(privateKeyStr string, data []byte) ([]byte, error) {
|
||
block, _ := pem.Decode([]byte(privateKeyStr))
|
||
if block == nil {
|
||
return nil, errors.New("private key error")
|
||
}
|
||
|
||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return rsa.DecryptPKCS1v15(rand.Reader, priv, data)
|
||
}
|
||
|
||
// 使用系统默认的私钥解密
|
||
// @param base64 字符串是否使用base64编码
|
||
func DefaultRsaDecrypt(data string, useBase64 bool) (string, error) {
|
||
// 空字符串不解密
|
||
if data == "" {
|
||
return "", nil
|
||
}
|
||
if useBase64 {
|
||
if decodeBase64, err := base64.StdEncoding.DecodeString(data); err != nil {
|
||
return "", err
|
||
} else {
|
||
data = string(decodeBase64)
|
||
}
|
||
}
|
||
priKey, err := GetRsaPrivateKey()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
val, err := RsaDecrypt(priKey, []byte(data))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return string(val), nil
|
||
}
|
||
|
||
const (
|
||
// 公钥文件路径
|
||
publicKeyFile = "./mayfly_rsa.pub"
|
||
// 私钥文件路径
|
||
privateKeyFile = "./mayfly_rsa"
|
||
|
||
publicKeyK = "mayfly:public-key"
|
||
privateKeyK = "mayfly:private-key"
|
||
)
|
||
|
||
// 获取系统的RSA公钥
|
||
func GetRsaPublicKey() (string, error) {
|
||
content, err := os.ReadFile(publicKeyFile)
|
||
if err != nil {
|
||
publicKey := cache.GetStr(publicKeyK)
|
||
if publicKey != "" {
|
||
return publicKey, nil
|
||
}
|
||
_, pubKey, err := GenerateAndSaveRSAKey()
|
||
return pubKey, err
|
||
}
|
||
|
||
return string(content), nil
|
||
}
|
||
|
||
// 获取系统私钥
|
||
func GetRsaPrivateKey() (string, error) {
|
||
content, err := os.ReadFile(privateKeyFile)
|
||
if err != nil {
|
||
privateKey := cache.GetStr(privateKeyK)
|
||
if privateKey != "" {
|
||
return privateKey, nil
|
||
}
|
||
privateKey, _, err := GenerateAndSaveRSAKey()
|
||
return privateKey, err
|
||
}
|
||
|
||
return string(content), nil
|
||
}
|
||
|
||
// 生成并保存rsa key,优先保存于磁盘,若磁盘保存失败,则保存至缓存
|
||
//
|
||
// 依次返回 privateKey, publicKey, error
|
||
func GenerateAndSaveRSAKey() (string, string, error) {
|
||
privateKey, publicKey, err := GenerateRSAKey(1024)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
|
||
err = os.WriteFile(privateKeyFile, []byte(privateKey), 0644)
|
||
if err != nil {
|
||
logx.ErrorTrace("RSA私钥写入磁盘文件失败, 使用缓存存储该私钥", err)
|
||
cache.SetStr(privateKeyK, privateKey, -1)
|
||
}
|
||
|
||
err = os.WriteFile(publicKeyFile, []byte(publicKey), 0644)
|
||
if err != nil {
|
||
logx.ErrorTrace("RSA公钥写入磁盘文件失败, 使用缓存存储该公钥", err)
|
||
cache.SetStr(publicKeyK, publicKey, -1)
|
||
}
|
||
|
||
return privateKey, publicKey, nil
|
||
}
|
||
|
||
// AesEncrypt 加密
|
||
func AesEncrypt(data []byte, key []byte) ([]byte, error) {
|
||
//创建加密实例
|
||
block, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
//判断加密快的大小
|
||
blockSize := block.BlockSize()
|
||
//填充
|
||
encryptBytes := pkcs7Padding(data, blockSize)
|
||
//初始化加密数据接收切片
|
||
crypted := make([]byte, len(encryptBytes))
|
||
//使用cbc加密模式
|
||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
|
||
//执行加密
|
||
blockMode.CryptBlocks(crypted, encryptBytes)
|
||
return crypted, nil
|
||
}
|
||
|
||
// AesDecrypt 解密
|
||
func AesDecrypt(data []byte, key []byte) ([]byte, error) {
|
||
//创建实例
|
||
block, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
//获取块的大小
|
||
blockSize := block.BlockSize()
|
||
//使用cbc
|
||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||
//初始化解密数据接收切片
|
||
crypted := make([]byte, len(data))
|
||
//执行解密
|
||
blockMode.CryptBlocks(crypted, data)
|
||
//去除填充
|
||
crypted, err = pkcs7UnPadding(crypted)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return crypted, nil
|
||
}
|
||
|
||
// aes加密 后 再base64
|
||
func AesEncryptBase64(data []byte, key []byte) (string, error) {
|
||
res, err := AesEncrypt(data, key)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return base64.StdEncoding.EncodeToString(res), nil
|
||
}
|
||
|
||
// base64解码后再 aes解码
|
||
func AesDecryptBase64(data string, key []byte) ([]byte, error) {
|
||
dataByte, err := base64.StdEncoding.DecodeString(data)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return AesDecrypt(dataByte, key)
|
||
}
|
||
|
||
// pkcs7Padding 填充
|
||
func pkcs7Padding(data []byte, blockSize int) []byte {
|
||
//判断缺少几位长度。最少1,最多 blockSize
|
||
padding := blockSize - len(data)%blockSize
|
||
//补足位数。把切片[]byte{byte(padding)}复制padding个
|
||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||
return append(data, padText...)
|
||
}
|
||
|
||
// pkcs7UnPadding 填充的反向操作
|
||
func pkcs7UnPadding(data []byte) ([]byte, error) {
|
||
length := len(data)
|
||
if length == 0 {
|
||
return nil, errors.New("加密字符串错误!")
|
||
}
|
||
//获取填充的个数
|
||
unPadding := int(data[length-1])
|
||
// todo fix: slice bounds out of range
|
||
if unPadding > length {
|
||
return nil, errors.New("解密字符串时去除填充个数超出字符串长度")
|
||
}
|
||
return data[:(length - unPadding)], nil
|
||
}
|