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" "strconv" "strings" "time" "go.uber.org/zap" ) // 常量定义 const ( KeySize = 4096 ExpirationDays = 90 RefreshDays = 60 PublicKeyRedisKey = "yggdrasil:public_key" PrivateKeyRedisKey = "yggdrasil:private_key" KeyExpirationRedisKey = "yggdrasil:key_expiration" RedisTTL = 0 // 永不过期,由应用程序管理过期时间 ) // signatureService 签名服务实现 type signatureService struct { profileRepo repository.ProfileRepository redis *redis.Client logger *zap.Logger } // NewSignatureService 创建SignatureService实例 func NewSignatureService( profileRepo repository.ProfileRepository, redisClient *redis.Client, logger *zap.Logger, ) *signatureService { return &signatureService{ profileRepo: profileRepo, redis: redisClient, logger: logger, } } // NewKeyPair 生成新的RSA密钥对 func (s *signatureService) NewKeyPair() (*model.KeyPair, error) { privateKey, err := rsa.GenerateKey(rand.Reader, KeySize) if err != nil { return nil, fmt.Errorf("生成RSA密钥对失败: %w", err) } // 获取公钥 publicKey := &privateKey.PublicKey // PEM编码私钥 privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) privateKeyPEM := pem.EncodeToMemory(&pem.Block{ Type: "RSA PRIVATE KEY", Bytes: privateKeyBytes, }) // PEM编码公钥 publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) if err != nil { return nil, fmt.Errorf("编码公钥失败: %w", err) } publicKeyPEM := pem.EncodeToMemory(&pem.Block{ Type: "PUBLIC KEY", Bytes: publicKeyBytes, }) // 计算时间 now := time.Now().UTC() expiration := now.AddDate(0, 0, ExpirationDays) refresh := now.AddDate(0, 0, RefreshDays) // 获取Yggdrasil根密钥并签名公钥 yggPublicKey, yggPrivateKey, err := s.GetOrCreateYggdrasilKeyPair() if err != nil { return nil, fmt.Errorf("获取Yggdrasil根密钥失败: %w", err) } // 构造签名消息 expiresAtMillis := expiration.UnixMilli() message := []byte(string(publicKeyPEM) + strconv.FormatInt(expiresAtMillis, 10)) // 使用SHA1withRSA签名 hashed := sha1.Sum(message) signature, err := rsa.SignPKCS1v15(rand.Reader, yggPrivateKey, crypto.SHA1, hashed[:]) if err != nil { return nil, fmt.Errorf("签名失败: %w", err) } publicKeySignature := base64.StdEncoding.EncodeToString(signature) // 构造V2签名消息(DER编码) publicKeyDER, err := x509.MarshalPKIXPublicKey(publicKey) if err != nil { return nil, fmt.Errorf("DER编码公钥失败: %w", err) } // V2签名:timestamp (8 bytes, big endian) + publicKey (DER) messageV2 := make([]byte, 8+len(publicKeyDER)) binary.BigEndian.PutUint64(messageV2[0:8], uint64(expiresAtMillis)) copy(messageV2[8:], publicKeyDER) hashedV2 := sha1.Sum(messageV2) signatureV2, err := rsa.SignPKCS1v15(rand.Reader, yggPrivateKey, crypto.SHA1, hashedV2[:]) if err != nil { return nil, fmt.Errorf("V2签名失败: %w", err) } publicKeySignatureV2 := base64.StdEncoding.EncodeToString(signatureV2) return &model.KeyPair{ PrivateKey: string(privateKeyPEM), PublicKey: string(publicKeyPEM), PublicKeySignature: publicKeySignature, PublicKeySignatureV2: publicKeySignatureV2, YggdrasilPublicKey: yggPublicKey, Expiration: expiration, Refresh: refresh, }, nil } // GetOrCreateYggdrasilKeyPair 获取或创建Yggdrasil根密钥对 func (s *signatureService) GetOrCreateYggdrasilKeyPair() (string, *rsa.PrivateKey, error) { ctx := context.Background() // 尝试从Redis获取密钥 publicKeyPEM, err := s.redis.Get(ctx, PublicKeyRedisKey) if err == nil && publicKeyPEM != "" { privateKeyPEM, err := s.redis.Get(ctx, PrivateKeyRedisKey) if err == nil && privateKeyPEM != "" { // 检查密钥是否过期 expStr, err := s.redis.Get(ctx, KeyExpirationRedisKey) if err == nil && expStr != "" { expTime, err := time.Parse(time.RFC3339, expStr) if err == nil && time.Now().Before(expTime) { // 密钥有效,解析私钥 block, _ := pem.Decode([]byte(privateKeyPEM)) if block != nil { privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err == nil { s.logger.Info("从Redis加载Yggdrasil根密钥") return publicKeyPEM, privateKey, nil } } } } } } // 生成新的根密钥对 s.logger.Info("生成新的Yggdrasil根密钥对") privateKey, err := rsa.GenerateKey(rand.Reader, KeySize) if err != nil { return "", nil, fmt.Errorf("生成RSA密钥失败: %w", err) } // PEM编码私钥 privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) privateKeyPEM := string(pem.EncodeToMemory(&pem.Block{ Type: "RSA PRIVATE KEY", Bytes: privateKeyBytes, })) // PEM编码公钥 publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) if err != nil { return "", nil, fmt.Errorf("编码公钥失败: %w", err) } publicKeyPEM = string(pem.EncodeToMemory(&pem.Block{ Type: "PUBLIC KEY", Bytes: publicKeyBytes, })) // 计算过期时间(90天) expiration := time.Now().AddDate(0, 0, ExpirationDays) // 保存到Redis if err := s.redis.Set(ctx, PublicKeyRedisKey, publicKeyPEM, RedisTTL); err != nil { s.logger.Warn("保存公钥到Redis失败", zap.Error(err)) } if err := s.redis.Set(ctx, PrivateKeyRedisKey, privateKeyPEM, RedisTTL); err != nil { s.logger.Warn("保存私钥到Redis失败", zap.Error(err)) } if err := s.redis.Set(ctx, KeyExpirationRedisKey, expiration.Format(time.RFC3339), RedisTTL); err != nil { s.logger.Warn("保存密钥过期时间到Redis失败", zap.Error(err)) } return publicKeyPEM, privateKey, nil } // GetPublicKeyFromRedis 从Redis获取公钥 func (s *signatureService) GetPublicKeyFromRedis() (string, error) { ctx := context.Background() publicKey, err := s.redis.Get(ctx, PublicKeyRedisKey) if err != nil { return "", fmt.Errorf("从Redis获取公钥失败: %w", err) } if publicKey == "" { // 如果Redis中没有,创建新的密钥对 publicKey, _, err = s.GetOrCreateYggdrasilKeyPair() if err != nil { return "", fmt.Errorf("创建新密钥对失败: %w", err) } } return publicKey, nil } // SignStringWithSHA1withRSA 使用SHA1withRSA签名字符串 func (s *signatureService) SignStringWithSHA1withRSA(data string) (string, error) { ctx := context.Background() // 从Redis获取私钥 privateKeyPEM, err := s.redis.Get(ctx, PrivateKeyRedisKey) if err != nil || privateKeyPEM == "" { // 如果没有私钥,创建新的密钥对 _, privateKey, err := s.GetOrCreateYggdrasilKeyPair() if err != nil { return "", fmt.Errorf("获取私钥失败: %w", err) } // 使用新生成的私钥签名 hashed := sha1.Sum([]byte(data)) signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, hashed[:]) if err != nil { return "", fmt.Errorf("签名失败: %w", err) } return base64.StdEncoding.EncodeToString(signature), nil } // 解析PEM格式的私钥 block, _ := pem.Decode([]byte(privateKeyPEM)) if block == nil { return "", fmt.Errorf("解析PEM私钥失败") } privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", fmt.Errorf("解析RSA私钥失败: %w", err) } // 签名 hashed := sha1.Sum([]byte(data)) signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, hashed[:]) if err != nil { return "", fmt.Errorf("签名失败: %w", err) } return base64.StdEncoding.EncodeToString(signature), nil } // FormatPublicKey 格式化公钥为单行格式(去除PEM头尾和换行符) func FormatPublicKey(publicKeyPEM string) string { // 移除PEM格式的头尾 lines := strings.Split(publicKeyPEM, "\n") var keyLines []string for _, line := range lines { trimmed := strings.TrimSpace(line) if trimmed != "" && !strings.HasPrefix(trimmed, "-----BEGIN") && !strings.HasPrefix(trimmed, "-----END") { keyLines = append(keyLines, trimmed) } } return strings.Join(keyLines, "") }