606 lines
19 KiB
Go
606 lines
19 KiB
Go
package service
|
||
|
||
import (
|
||
"carrotskin/internal/model"
|
||
"carrotskin/internal/repository"
|
||
"carrotskin/pkg/redis"
|
||
"context"
|
||
"crypto"
|
||
"crypto/rand"
|
||
"crypto/rsa"
|
||
"crypto/sha1"
|
||
"crypto/x509"
|
||
"encoding/base64"
|
||
"encoding/binary"
|
||
"encoding/pem"
|
||
"fmt"
|
||
"go.uber.org/zap"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// 常量定义
|
||
const (
|
||
// RSA密钥长度
|
||
RSAKeySize = 4096
|
||
|
||
// Redis密钥名称
|
||
PrivateKeyRedisKey = "private_key"
|
||
PublicKeyRedisKey = "public_key"
|
||
|
||
// 密钥过期时间
|
||
KeyExpirationTime = time.Hour * 24 * 7
|
||
|
||
// 证书相关
|
||
CertificateRefreshInterval = time.Hour * 24 // 证书刷新时间间隔
|
||
CertificateExpirationPeriod = time.Hour * 24 * 7 // 证书过期时间
|
||
)
|
||
|
||
// PlayerCertificate 表示玩家证书信息
|
||
type PlayerCertificate struct {
|
||
ExpiresAt string `json:"expiresAt"`
|
||
RefreshedAfter string `json:"refreshedAfter"`
|
||
PublicKeySignature string `json:"publicKeySignature,omitempty"`
|
||
PublicKeySignatureV2 string `json:"publicKeySignatureV2,omitempty"`
|
||
KeyPair struct {
|
||
PrivateKey string `json:"privateKey"`
|
||
PublicKey string `json:"publicKey"`
|
||
} `json:"keyPair"`
|
||
}
|
||
// SignatureService 保留结构体以保持向后兼容,但推荐使用函数式版本
|
||
type SignatureService struct {
|
||
logger *zap.Logger
|
||
redisClient *redis.Client
|
||
}
|
||
|
||
func NewSignatureService(logger *zap.Logger, redisClient *redis.Client) *SignatureService {
|
||
return &SignatureService{
|
||
logger: logger,
|
||
redisClient: redisClient,
|
||
}
|
||
}
|
||
|
||
// SignStringWithSHA1withRSA 使用SHA1withRSA签名字符串并返回Base64编码的签名(函数式版本)
|
||
func SignStringWithSHA1withRSA(logger *zap.Logger, redisClient *redis.Client, data string) (string, error) {
|
||
if data == "" {
|
||
return "", fmt.Errorf("签名数据不能为空")
|
||
}
|
||
|
||
// 获取私钥
|
||
privateKey, err := DecodePrivateKeyFromPEM(logger, redisClient)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 解码私钥失败: ", zap.Error(err))
|
||
return "", fmt.Errorf("解码私钥失败: %w", err)
|
||
}
|
||
|
||
// 计算SHA1哈希
|
||
hashed := sha1.Sum([]byte(data))
|
||
|
||
// 使用RSA-PKCS1v15算法签名
|
||
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, hashed[:])
|
||
if err != nil {
|
||
logger.Error("[ERROR] RSA签名失败: ", zap.Error(err))
|
||
return "", fmt.Errorf("RSA签名失败: %w", err)
|
||
}
|
||
|
||
// Base64编码签名
|
||
encodedSignature := base64.StdEncoding.EncodeToString(signature)
|
||
|
||
logger.Info("[INFO] 成功使用SHA1withRSA生成签名,", zap.Any("数据长度:", len(data)))
|
||
return encodedSignature, nil
|
||
}
|
||
|
||
// SignStringWithSHA1withRSAService 使用SHA1withRSA签名字符串并返回Base64编码的签名(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) SignStringWithSHA1withRSA(data string) (string, error) {
|
||
return SignStringWithSHA1withRSA(s.logger, s.redisClient, data)
|
||
}
|
||
|
||
// DecodePrivateKeyFromPEM 从Redis获取并解码PEM格式的私钥(函数式版本)
|
||
func DecodePrivateKeyFromPEM(logger *zap.Logger, redisClient *redis.Client) (*rsa.PrivateKey, error) {
|
||
// 从Redis获取私钥
|
||
privateKeyString, err := GetPrivateKeyFromRedis(logger, redisClient)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("从Redis获取私钥失败: %w", err)
|
||
}
|
||
|
||
// 解码PEM格式
|
||
privateKeyBlock, rest := pem.Decode([]byte(privateKeyString))
|
||
if privateKeyBlock == nil || len(rest) > 0 {
|
||
logger.Error("[ERROR] 无效的PEM格式私钥")
|
||
return nil, fmt.Errorf("无效的PEM格式私钥")
|
||
}
|
||
|
||
// 解析PKCS1格式的私钥
|
||
privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 解析私钥失败: ", zap.Error(err))
|
||
return nil, fmt.Errorf("解析私钥失败: %w", err)
|
||
}
|
||
|
||
return privateKey, nil
|
||
}
|
||
|
||
// GetPrivateKeyFromRedis 从Redis获取私钥(PEM格式)(函数式版本)
|
||
func GetPrivateKeyFromRedis(logger *zap.Logger, redisClient *redis.Client) (string, error) {
|
||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
|
||
defer cancel()
|
||
|
||
pemBytes, err := redisClient.GetBytes(ctx, PrivateKeyRedisKey)
|
||
if err != nil {
|
||
logger.Info("[INFO] 从Redis获取私钥失败,尝试生成新的密钥对: ", zap.Error(err))
|
||
|
||
// 生成新的密钥对
|
||
err = GenerateRSAKeyPair(logger, redisClient)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 生成RSA密钥对失败: ", zap.Error(err))
|
||
return "", fmt.Errorf("生成RSA密钥对失败: %w", err)
|
||
}
|
||
|
||
// 递归获取生成的密钥
|
||
return GetPrivateKeyFromRedis(logger, redisClient)
|
||
}
|
||
|
||
return string(pemBytes), nil
|
||
}
|
||
|
||
// DecodePrivateKeyFromPEMService 从Redis获取并解码PEM格式的私钥(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) DecodePrivateKeyFromPEM() (*rsa.PrivateKey, error) {
|
||
return DecodePrivateKeyFromPEM(s.logger, s.redisClient)
|
||
}
|
||
|
||
// GetPrivateKeyFromRedisService 从Redis获取私钥(PEM格式)(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) GetPrivateKeyFromRedis() (string, error) {
|
||
return GetPrivateKeyFromRedis(s.logger, s.redisClient)
|
||
}
|
||
|
||
// GenerateRSAKeyPair 生成新的RSA密钥对(函数式版本)
|
||
func GenerateRSAKeyPair(logger *zap.Logger, redisClient *redis.Client) error {
|
||
logger.Info("[INFO] 开始生成RSA密钥对", zap.Int("keySize", RSAKeySize))
|
||
|
||
// 生成私钥
|
||
privateKey, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 生成RSA私钥失败: ", zap.Error(err))
|
||
return fmt.Errorf("生成RSA私钥失败: %w", err)
|
||
}
|
||
|
||
// 编码私钥为PEM格式
|
||
pemPrivateKey, err := EncodePrivateKeyToPEM(privateKey)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 编码RSA私钥失败: ", zap.Error(err))
|
||
return fmt.Errorf("编码RSA私钥失败: %w", err)
|
||
}
|
||
|
||
// 获取公钥并编码为PEM格式
|
||
pubKey := privateKey.PublicKey
|
||
pemPublicKey, err := EncodePublicKeyToPEM(logger, &pubKey)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 编码RSA公钥失败: ", zap.Error(err))
|
||
return fmt.Errorf("编码RSA公钥失败: %w", err)
|
||
}
|
||
|
||
// 保存密钥对到Redis
|
||
return SaveKeyPairToRedis(logger, redisClient, string(pemPrivateKey), string(pemPublicKey))
|
||
}
|
||
|
||
// GenerateRSAKeyPairService 生成新的RSA密钥对(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) GenerateRSAKeyPair() error {
|
||
return GenerateRSAKeyPair(s.logger, s.redisClient)
|
||
}
|
||
|
||
// EncodePrivateKeyToPEM 将私钥编码为PEM格式(函数式版本)
|
||
func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey, keyType ...string) ([]byte, error) {
|
||
if privateKey == nil {
|
||
return nil, fmt.Errorf("私钥不能为空")
|
||
}
|
||
|
||
// 默认使用 "PRIVATE KEY" 类型
|
||
pemType := "PRIVATE KEY"
|
||
|
||
// 如果指定了类型参数且为 "RSA",则使用 "RSA PRIVATE KEY"
|
||
if len(keyType) > 0 && keyType[0] == "RSA" {
|
||
pemType = "RSA PRIVATE KEY"
|
||
}
|
||
|
||
// 将私钥转换为PKCS1格式
|
||
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
||
|
||
// 编码为PEM格式
|
||
pemBlock := &pem.Block{
|
||
Type: pemType,
|
||
Bytes: privateKeyBytes,
|
||
}
|
||
|
||
return pem.EncodeToMemory(pemBlock), nil
|
||
}
|
||
|
||
// EncodePublicKeyToPEM 将公钥编码为PEM格式(函数式版本)
|
||
func EncodePublicKeyToPEM(logger *zap.Logger, publicKey *rsa.PublicKey, keyType ...string) ([]byte, error) {
|
||
if publicKey == nil {
|
||
return nil, fmt.Errorf("公钥不能为空")
|
||
}
|
||
|
||
// 默认使用 "PUBLIC KEY" 类型
|
||
pemType := "PUBLIC KEY"
|
||
var publicKeyBytes []byte
|
||
var err error
|
||
|
||
// 如果指定了类型参数且为 "RSA",则使用 "RSA PUBLIC KEY"
|
||
if len(keyType) > 0 && keyType[0] == "RSA" {
|
||
pemType = "RSA PUBLIC KEY"
|
||
publicKeyBytes = x509.MarshalPKCS1PublicKey(publicKey)
|
||
} else {
|
||
// 默认将公钥转换为PKIX格式
|
||
publicKeyBytes, err = x509.MarshalPKIXPublicKey(publicKey)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 序列化公钥失败: ", zap.Error(err))
|
||
return nil, fmt.Errorf("序列化公钥失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 编码为PEM格式
|
||
pemBlock := &pem.Block{
|
||
Type: pemType,
|
||
Bytes: publicKeyBytes,
|
||
}
|
||
|
||
return pem.EncodeToMemory(pemBlock), nil
|
||
}
|
||
|
||
// SaveKeyPairToRedis 将RSA密钥对保存到Redis(函数式版本)
|
||
func SaveKeyPairToRedis(logger *zap.Logger, redisClient *redis.Client, privateKey, publicKey string) error {
|
||
// 创建上下文并设置超时
|
||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
|
||
defer cancel()
|
||
|
||
// 使用事务确保两个操作的原子性
|
||
tx := redisClient.TxPipeline()
|
||
|
||
tx.Set(ctx, PrivateKeyRedisKey, privateKey, KeyExpirationTime)
|
||
tx.Set(ctx, PublicKeyRedisKey, publicKey, KeyExpirationTime)
|
||
|
||
// 执行事务
|
||
_, err := tx.Exec(ctx)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 保存RSA密钥对到Redis失败: ", zap.Error(err))
|
||
return fmt.Errorf("保存RSA密钥对到Redis失败: %w", err)
|
||
}
|
||
|
||
logger.Info("[INFO] 成功保存RSA密钥对到Redis")
|
||
return nil
|
||
}
|
||
|
||
// EncodePrivateKeyToPEMService 将私钥编码为PEM格式(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey, keyType ...string) ([]byte, error) {
|
||
return EncodePrivateKeyToPEM(privateKey, keyType...)
|
||
}
|
||
|
||
// EncodePublicKeyToPEMService 将公钥编码为PEM格式(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) EncodePublicKeyToPEM(publicKey *rsa.PublicKey, keyType ...string) ([]byte, error) {
|
||
return EncodePublicKeyToPEM(s.logger, publicKey, keyType...)
|
||
}
|
||
|
||
// SaveKeyPairToRedisService 将RSA密钥对保存到Redis(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) SaveKeyPairToRedis(privateKey, publicKey string) error {
|
||
return SaveKeyPairToRedis(s.logger, s.redisClient, privateKey, publicKey)
|
||
}
|
||
|
||
// GetPublicKeyFromRedisFunc 从Redis获取公钥(PEM格式,函数式版本)
|
||
func GetPublicKeyFromRedisFunc(logger *zap.Logger, redisClient *redis.Client) (string, error) {
|
||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
|
||
defer cancel()
|
||
|
||
pemBytes, err := redisClient.GetBytes(ctx, PublicKeyRedisKey)
|
||
if err != nil {
|
||
logger.Info("[INFO] 从Redis获取公钥失败,尝试生成新的密钥对: ", zap.Error(err))
|
||
|
||
// 生成新的密钥对
|
||
err = GenerateRSAKeyPair(logger, redisClient)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 生成RSA密钥对失败: ", zap.Error(err))
|
||
return "", fmt.Errorf("生成RSA密钥对失败: %w", err)
|
||
}
|
||
|
||
// 递归获取生成的密钥
|
||
return GetPublicKeyFromRedisFunc(logger, redisClient)
|
||
}
|
||
|
||
// 检查获取到的公钥是否为空(key不存在时GetBytes返回nil, nil)
|
||
if len(pemBytes) == 0 {
|
||
logger.Info("[INFO] Redis中公钥为空,尝试生成新的密钥对")
|
||
// 生成新的密钥对
|
||
err = GenerateRSAKeyPair(logger, redisClient)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 生成RSA密钥对失败: ", zap.Error(err))
|
||
return "", fmt.Errorf("生成RSA密钥对失败: %w", err)
|
||
}
|
||
// 递归获取生成的密钥
|
||
return GetPublicKeyFromRedisFunc(logger, redisClient)
|
||
}
|
||
|
||
return string(pemBytes), nil
|
||
}
|
||
|
||
// GetPublicKeyFromRedis 从Redis获取公钥(PEM格式,结构体方法版本)
|
||
func (s *SignatureService) GetPublicKeyFromRedis() (string, error) {
|
||
return GetPublicKeyFromRedisFunc(s.logger, s.redisClient)
|
||
}
|
||
|
||
|
||
// GeneratePlayerCertificate 生成玩家证书(函数式版本)
|
||
func GeneratePlayerCertificate(db *gorm.DB, logger *zap.Logger, redisClient *redis.Client, uuid string) (*PlayerCertificate, error) {
|
||
if uuid == "" {
|
||
return nil, fmt.Errorf("UUID不能为空")
|
||
}
|
||
logger.Info("[INFO] 开始生成玩家证书,用户UUID: %s",
|
||
zap.String("uuid", uuid),
|
||
)
|
||
|
||
keyPair, err := repository.GetProfileKeyPair(uuid)
|
||
if err != nil {
|
||
logger.Info("[INFO] 获取用户密钥对失败,将创建新密钥对: %v",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
)
|
||
keyPair = nil
|
||
}
|
||
|
||
// 如果没有找到密钥对或密钥对已过期,创建一个新的
|
||
now := time.Now().UTC()
|
||
if keyPair == nil || keyPair.Refresh.Before(now) || keyPair.PrivateKey == "" || keyPair.PublicKey == "" {
|
||
logger.Info("[INFO] 为用户创建新的密钥对: %s",
|
||
zap.String("uuid", uuid),
|
||
)
|
||
keyPair, err = NewKeyPair(logger)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 生成玩家证书密钥对失败: %v",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
)
|
||
return nil, fmt.Errorf("生成玩家证书密钥对失败: %w", err)
|
||
}
|
||
// 保存密钥对到数据库
|
||
err = repository.UpdateProfileKeyPair(uuid, keyPair)
|
||
if err != nil {
|
||
// 日志修改:logger → s.logger,zap结构化字段
|
||
logger.Warn("[WARN] 更新用户密钥对失败: %v",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
)
|
||
// 继续执行,即使保存失败
|
||
}
|
||
}
|
||
|
||
// 计算expiresAt的毫秒时间戳
|
||
expiresAtMillis := keyPair.Expiration.UnixMilli()
|
||
|
||
// 准备签名
|
||
publicKeySignature := ""
|
||
publicKeySignatureV2 := ""
|
||
|
||
// 获取服务器私钥用于签名
|
||
serverPrivateKey, err := DecodePrivateKeyFromPEM(logger, redisClient)
|
||
if err != nil {
|
||
// 日志修改:logger → s.logger,zap结构化字段
|
||
logger.Error("[ERROR] 获取服务器私钥失败: %v",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
)
|
||
return nil, fmt.Errorf("获取服务器私钥失败: %w", err)
|
||
}
|
||
|
||
// 提取公钥DER编码
|
||
pubPEMBlock, _ := pem.Decode([]byte(keyPair.PublicKey))
|
||
if pubPEMBlock == nil {
|
||
// 日志修改:logger → s.logger,zap结构化字段
|
||
logger.Error("[ERROR] 解码公钥PEM失败",
|
||
zap.String("uuid", uuid),
|
||
zap.String("publicKey", keyPair.PublicKey),
|
||
)
|
||
return nil, fmt.Errorf("解码公钥PEM失败")
|
||
}
|
||
pubDER := pubPEMBlock.Bytes
|
||
|
||
// 准备publicKeySignature(用于MC 1.19)
|
||
// Base64编码公钥,不包含换行
|
||
pubBase64 := strings.ReplaceAll(base64.StdEncoding.EncodeToString(pubDER), "\n", "")
|
||
|
||
// 按76字符一行进行包装
|
||
pubBase64Wrapped := WrapString(pubBase64, 76)
|
||
|
||
// 放入PEM格式
|
||
pubMojangPEM := "-----BEGIN RSA PUBLIC KEY-----\n" +
|
||
pubBase64Wrapped +
|
||
"\n-----END RSA PUBLIC KEY-----\n"
|
||
|
||
// 签名数据: expiresAt毫秒时间戳 + 公钥PEM格式
|
||
signedData := []byte(fmt.Sprintf("%d%s", expiresAtMillis, pubMojangPEM))
|
||
|
||
// 计算SHA1哈希并签名
|
||
hash1 := sha1.Sum(signedData)
|
||
signature, err := rsa.SignPKCS1v15(rand.Reader, serverPrivateKey, crypto.SHA1, hash1[:])
|
||
if err != nil {
|
||
logger.Error("[ERROR] 签名失败: %v",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
zap.Int64("expiresAtMillis", expiresAtMillis),
|
||
)
|
||
return nil, fmt.Errorf("签名失败: %w", err)
|
||
}
|
||
publicKeySignature = base64.StdEncoding.EncodeToString(signature)
|
||
|
||
// 准备publicKeySignatureV2(用于MC 1.19.1+)
|
||
var uuidBytes []byte
|
||
|
||
// 如果提供了UUID,则使用它
|
||
// 移除UUID中的连字符
|
||
uuidStr := strings.ReplaceAll(uuid, "-", "")
|
||
|
||
// 将UUID转换为字节数组(16字节)
|
||
if len(uuidStr) < 32 {
|
||
logger.Warn("[WARN] UUID长度不足32字符,使用空UUID: %s",
|
||
zap.String("uuid", uuid),
|
||
zap.String("processedUuidStr", uuidStr),
|
||
)
|
||
uuidBytes = make([]byte, 16)
|
||
} else {
|
||
// 解析UUID字符串为字节
|
||
uuidBytes = make([]byte, 16)
|
||
parseErr := error(nil)
|
||
for i := 0; i < 16; i++ {
|
||
// 每两个字符转换为一个字节
|
||
byteStr := uuidStr[i*2 : i*2+2]
|
||
byteVal, err := strconv.ParseUint(byteStr, 16, 8)
|
||
if err != nil {
|
||
parseErr = err
|
||
logger.Error("[ERROR] 解析UUID字节失败: %v, byteStr: %s",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
zap.String("byteStr", byteStr),
|
||
zap.Int("index", i),
|
||
)
|
||
uuidBytes = make([]byte, 16) // 出错时使用空UUID
|
||
break
|
||
}
|
||
uuidBytes[i] = byte(byteVal)
|
||
}
|
||
if parseErr != nil {
|
||
return nil, fmt.Errorf("解析UUID字节失败: %w", parseErr)
|
||
}
|
||
}
|
||
|
||
// 准备签名数据:UUID + expiresAt时间戳 + DER编码的公钥
|
||
signedDataV2 := make([]byte, 0, 24+len(pubDER)) // 预分配缓冲区
|
||
|
||
// 添加UUID(16字节)
|
||
signedDataV2 = append(signedDataV2, uuidBytes...)
|
||
|
||
// 添加expiresAt毫秒时间戳(8字节,大端序)
|
||
expiresAtBytes := make([]byte, 8)
|
||
binary.BigEndian.PutUint64(expiresAtBytes, uint64(expiresAtMillis))
|
||
signedDataV2 = append(signedDataV2, expiresAtBytes...)
|
||
|
||
// 添加DER编码的公钥
|
||
signedDataV2 = append(signedDataV2, pubDER...)
|
||
|
||
// 计算SHA1哈希并签名
|
||
hash2 := sha1.Sum(signedDataV2)
|
||
signatureV2, err := rsa.SignPKCS1v15(rand.Reader, serverPrivateKey, crypto.SHA1, hash2[:])
|
||
if err != nil {
|
||
logger.Error("[ERROR] 签名V2失败: %v",
|
||
zap.Error(err),
|
||
zap.String("uuid", uuid),
|
||
zap.Int64("expiresAtMillis", expiresAtMillis),
|
||
)
|
||
return nil, fmt.Errorf("签名V2失败: %w", err)
|
||
}
|
||
publicKeySignatureV2 = base64.StdEncoding.EncodeToString(signatureV2)
|
||
|
||
// 创建玩家证书结构
|
||
certificate := &PlayerCertificate{
|
||
KeyPair: struct {
|
||
PrivateKey string `json:"privateKey"`
|
||
PublicKey string `json:"publicKey"`
|
||
}{
|
||
PrivateKey: keyPair.PrivateKey,
|
||
PublicKey: keyPair.PublicKey,
|
||
},
|
||
PublicKeySignature: publicKeySignature,
|
||
PublicKeySignatureV2: publicKeySignatureV2,
|
||
ExpiresAt: keyPair.Expiration.Format(time.RFC3339Nano),
|
||
RefreshedAfter: keyPair.Refresh.Format(time.RFC3339Nano),
|
||
}
|
||
|
||
logger.Info("[INFO] 成功生成玩家证书,过期时间: %s",
|
||
zap.String("uuid", uuid),
|
||
zap.String("expiresAt", certificate.ExpiresAt),
|
||
zap.String("refreshedAfter", certificate.RefreshedAfter),
|
||
)
|
||
return certificate, nil
|
||
}
|
||
|
||
// GeneratePlayerCertificateService 生成玩家证书(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) GeneratePlayerCertificate(uuid string) (*PlayerCertificate, error) {
|
||
return GeneratePlayerCertificate(nil, s.logger, s.redisClient, uuid) // TODO: 需要传入db参数
|
||
}
|
||
|
||
// NewKeyPair 生成新的密钥对(函数式版本)
|
||
func NewKeyPair(logger *zap.Logger) (*model.KeyPair, error) {
|
||
// 生成新的RSA密钥对(用于玩家证书)
|
||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048) // 对玩家证书使用更小的密钥以提高性能
|
||
if err != nil {
|
||
logger.Error("[ERROR] 生成玩家证书私钥失败: %v",
|
||
zap.Error(err),
|
||
)
|
||
return nil, fmt.Errorf("生成玩家证书私钥失败: %w", err)
|
||
}
|
||
|
||
// 获取DER编码的密钥
|
||
keyDER, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 编码私钥为PKCS8格式失败: %v",
|
||
zap.Error(err),
|
||
)
|
||
return nil, fmt.Errorf("编码私钥为PKCS8格式失败: %w", err)
|
||
}
|
||
|
||
pubDER, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
|
||
if err != nil {
|
||
logger.Error("[ERROR] 编码公钥为PKIX格式失败: %v",
|
||
zap.Error(err),
|
||
)
|
||
return nil, fmt.Errorf("编码公钥为PKIX格式失败: %w", err)
|
||
}
|
||
|
||
// 将密钥编码为PEM格式
|
||
keyPEM := pem.EncodeToMemory(&pem.Block{
|
||
Type: "RSA PRIVATE KEY",
|
||
Bytes: keyDER,
|
||
})
|
||
|
||
pubPEM := pem.EncodeToMemory(&pem.Block{
|
||
Type: "RSA PUBLIC KEY",
|
||
Bytes: pubDER,
|
||
})
|
||
|
||
// 创建证书过期和刷新时间
|
||
now := time.Now().UTC()
|
||
expiresAtTime := now.Add(CertificateExpirationPeriod)
|
||
refreshedAfter := now.Add(CertificateRefreshInterval)
|
||
keyPair := &model.KeyPair{
|
||
Expiration: expiresAtTime,
|
||
PrivateKey: string(keyPEM),
|
||
PublicKey: string(pubPEM),
|
||
Refresh: refreshedAfter,
|
||
}
|
||
return keyPair, nil
|
||
}
|
||
|
||
// WrapString 将字符串按指定宽度进行换行(函数式版本)
|
||
func WrapString(str string, width int) string {
|
||
if width <= 0 {
|
||
return str
|
||
}
|
||
|
||
var b strings.Builder
|
||
for i := 0; i < len(str); i += width {
|
||
end := i + width
|
||
if end > len(str) {
|
||
end = len(str)
|
||
}
|
||
b.WriteString(str[i:end])
|
||
if end < len(str) {
|
||
b.WriteString("\n")
|
||
}
|
||
}
|
||
return b.String()
|
||
}
|
||
|
||
// NewKeyPairService 生成新的密钥对(结构体方法版本,保持向后兼容)
|
||
func (s *SignatureService) NewKeyPair() (*model.KeyPair, error) {
|
||
return NewKeyPair(s.logger)
|
||
}
|