chore: 初始化仓库,排除二进制文件和覆盖率文件
This commit is contained in:
605
internal/service/signature_service.go
Normal file
605
internal/service/signature_service.go
Normal file
@@ -0,0 +1,605 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user