175 lines
4.2 KiB
Go
175 lines
4.2 KiB
Go
package rsa
|
||
|
||
import (
|
||
"context"
|
||
"crypto/rand"
|
||
"crypto/rsa"
|
||
"crypto/x509"
|
||
"encoding/pem"
|
||
"errors"
|
||
"fmt"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"os"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
type rsaClient struct {
|
||
publicKey *rsa.PublicKey
|
||
privateKey *rsa.PrivateKey
|
||
}
|
||
|
||
var (
|
||
instance *rsaClient
|
||
once sync.Once
|
||
)
|
||
|
||
// init 会在包初始化时自动调用,用于加载默认的 RSA 公钥和私钥。
|
||
func init() {
|
||
ctx := context.Background()
|
||
once.Do(func() {
|
||
instance = &rsaClient{}
|
||
err := instance.loadKeys(g.Config().MustGet(ctx, "gamelife.rsaKey").String())
|
||
if err != nil {
|
||
panic("加载 RSA 密钥失败: " + err.Error())
|
||
}
|
||
})
|
||
}
|
||
|
||
// GetRsaClient 返回 RSA 客户端的单例实例。
|
||
//
|
||
// 通常用于执行加解密操作。
|
||
func GetRsaClient() *rsaClient {
|
||
return instance
|
||
}
|
||
|
||
// EncryptWithRsaPublicKey 使用加载的 RSA 公钥对原始数据进行加密。
|
||
//
|
||
// 参数:
|
||
// - plain: 待加密的明文数据。
|
||
//
|
||
// 返回值:
|
||
// - 加密后的密文数据。
|
||
// - 如果加密失败,则返回错误。
|
||
func (c *rsaClient) EncryptWithRsaPublicKey(plain []byte) ([]byte, error) {
|
||
if c.publicKey == nil {
|
||
return nil, errors.New("公钥未加载")
|
||
}
|
||
return rsa.EncryptPKCS1v15(rand.Reader, c.publicKey, plain)
|
||
}
|
||
|
||
// DecryptWithRsaPrivateKey 使用加载的 RSA 私钥对密文数据进行解密。
|
||
//
|
||
// 参数:
|
||
// - cipher: 加密后的密文数据。
|
||
//
|
||
// 返回值:
|
||
// - 解密后的明文数据。
|
||
// - 如果解密失败,则返回错误。
|
||
func (c *rsaClient) DecryptWithRsaPrivateKey(cipher []byte) ([]byte, error) {
|
||
if c.privateKey == nil {
|
||
return nil, errors.New("私钥未加载")
|
||
}
|
||
return rsa.DecryptPKCS1v15(rand.Reader, c.privateKey, cipher)
|
||
}
|
||
|
||
// loadKeys 从指定文件中加载 RSA 公钥和私钥。
|
||
//
|
||
// 参数:
|
||
// - publicKeyPath: 公钥 PEM 文件路径。
|
||
// - privateKeyPath: 私钥 PEM 文件路径。
|
||
//
|
||
// 返回值:
|
||
// - 成功返回 nil,否则返回错误信息。
|
||
func (c *rsaClient) loadKeys(keyFilePath string) error {
|
||
// 读取密钥文件
|
||
keyBytes, err := os.ReadFile(keyFilePath)
|
||
if err != nil {
|
||
return fmt.Errorf("读取密钥文件失败: %w", err)
|
||
}
|
||
|
||
var pubFound, privFound bool
|
||
rest := keyBytes
|
||
|
||
for {
|
||
var block *pem.Block
|
||
block, rest = pem.Decode(rest)
|
||
if block == nil {
|
||
break
|
||
}
|
||
|
||
// 转换为小写以匹配你的密钥格式
|
||
blockTypeLower := strings.ToLower(block.Type)
|
||
|
||
switch blockTypeLower {
|
||
case "rsa public key":
|
||
// 尝试解析 PKCS#1 格式的公钥
|
||
pubKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
|
||
if err != nil {
|
||
// 尝试 PKIX 格式(兼容性处理)
|
||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||
if err != nil {
|
||
return fmt.Errorf("无法解析 RSA 公钥: %w", err)
|
||
}
|
||
var ok bool
|
||
if c.publicKey, ok = pub.(*rsa.PublicKey); !ok {
|
||
return errors.New("解析的公钥不是 RSA 公钥")
|
||
}
|
||
} else {
|
||
c.publicKey = pubKey
|
||
}
|
||
pubFound = true
|
||
|
||
case "rsa private key":
|
||
// 解析 PKCS#1 格式的私钥
|
||
privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
if err != nil {
|
||
return fmt.Errorf("无法解析 RSA 私钥: %w", err)
|
||
}
|
||
c.privateKey = privKey
|
||
privFound = true
|
||
|
||
default:
|
||
// 忽略未知的 PEM 块
|
||
continue
|
||
}
|
||
}
|
||
|
||
if !pubFound {
|
||
return errors.New("未找到有效的 RSA 公钥")
|
||
}
|
||
if !privFound {
|
||
return errors.New("未找到有效的 RSA 私钥")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// EncryptWithPublicKey 使用公钥加密数据
|
||
func (c *rsaClient) EncryptWithPublicKey(plaintext []byte) ([]byte, error) {
|
||
if c.publicKey == nil {
|
||
return nil, errors.New("公钥未加载")
|
||
}
|
||
|
||
// 使用 OAEP 填充进行加密
|
||
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, c.publicKey, plaintext)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("公钥加密失败: %w", err)
|
||
}
|
||
return ciphertext, nil
|
||
}
|
||
|
||
// DecryptWithPrivateKey 使用私钥解密数据
|
||
func (c *rsaClient) DecryptWithPrivateKey(ciphertext []byte) ([]byte, error) {
|
||
if c.privateKey == nil {
|
||
return nil, errors.New("私钥未加载")
|
||
}
|
||
|
||
// 使用 OAEP 填充进行解密
|
||
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, c.privateKey, ciphertext)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("私钥解密失败: %w", err)
|
||
}
|
||
return plaintext, nil
|
||
}
|