package security import ( "crypto/ed25519" "crypto/rand" "encoding/base64" "encoding/pem" "errors" "fmt" "os" "sync" "golang.org/x/crypto/ssh" "licensing-cotton/internal/logger" ) var ( ed25519Priv ed25519.PrivateKey ed25519Pub ed25519.PublicKey once sync.Once ) // InitEd25519Keys 自动生成或加载 Ed25519 私钥 func InitEd25519Keys(passphrase string) error { var err error once.Do(func() { keyDir := "keys" keyFile := fmt.Sprintf("%s/licensing-key", keyDir) // 检查密钥文件是否存在 if _, e := os.Stat(keyFile); os.IsNotExist(e) { // 密钥不存在,自动生成新的密钥对 pubKey, privKey, e := ed25519.GenerateKey(rand.Reader) if e != nil { err = errors.New("failed to generate ed25519 key pair: " + e.Error()) return } // 创建 keys 目录 if e := os.MkdirAll(keyDir, 0700); e != nil { err = errors.New("failed to create keys directory: " + e.Error()) return } // 保存私钥为 OpenSSH 格式(不加密,便于使用) privateKeyBytes, e := ssh.MarshalPrivateKey(privKey, "") if e != nil { err = errors.New("failed to marshal private key: " + e.Error()) return } privateKeyPEM := pem.EncodeToMemory(privateKeyBytes) if e := os.WriteFile(keyFile, privateKeyPEM, 0600); e != nil { err = errors.New("failed to write private key file: " + e.Error()) return } // 保存公钥为 OpenSSH 格式 publicKeyFile := fmt.Sprintf("%s/licensing-key.pub", keyDir) sshPubKey, e := ssh.NewPublicKey(pubKey) if e != nil { err = errors.New("failed to create ssh public key: " + e.Error()) return } publicKeyBytes := ssh.MarshalAuthorizedKey(sshPubKey) if e := os.WriteFile(publicKeyFile, publicKeyBytes, 0644); e != nil { err = errors.New("failed to write public key file: " + e.Error()) return } ed25519Priv = privKey ed25519Pub = pubKey logger.Info("Ed25519 密钥对已自动生成") return } // 密钥文件存在,加载它 data, e := os.ReadFile(keyFile) if e != nil { err = errors.New("failed to load ed25519 private key file: " + e.Error()) return } // 尝试解析私钥(先尝试无密码,再尝试有密码) var decryptedKey interface{} decryptedKey, e = ssh.ParseRawPrivateKey(data) if e != nil { // 如果无密码解析失败,尝试用密码解析 decryptedKey, e = ssh.ParseRawPrivateKeyWithPassphrase(data, []byte(passphrase)) if e != nil { err = errors.New("failed to decrypt private key: " + e.Error()) return } } // 检查解析出的私钥类型 switch key := decryptedKey.(type) { case ed25519.PrivateKey: ed25519Priv = key ed25519Pub = key.Public().(ed25519.PublicKey) case *ed25519.PrivateKey: ed25519Priv = *key ed25519Pub = (*key).Public().(ed25519.PublicKey) default: err = errors.New("parsed key is not an ed25519 private key, check your key format") return } logger.Info("Ed25519 密钥已从文件加载") }) return err } // GetEd25519PublicKey 获取 Ed25519 公钥 func GetEd25519PublicKey() (ed25519.PublicKey, error) { if ed25519Pub == nil { return nil, errors.New("ed25519 public key not initialized") } return ed25519Pub, nil } // GetEd25519PublicKeyBase64 获取 Base64 编码的公钥 func GetEd25519PublicKeyBase64() (string, error) { pubKey, err := GetEd25519PublicKey() if err != nil { return "", err } return base64.StdEncoding.EncodeToString(pubKey), nil } // GetEd25519PublicKeySSH 获取 SSH 格式的公钥 func GetEd25519PublicKeySSH() (string, error) { pubKey, err := GetEd25519PublicKey() if err != nil { return "", err } sshPubKey, err := ssh.NewPublicKey(pubKey) if err != nil { return "", err } return string(ssh.MarshalAuthorizedKey(sshPubKey)), nil } // Ed25519Sign 进行 Ed25519 签名 func Ed25519Sign(message []byte) ([]byte, error) { if ed25519Priv == nil { return nil, errors.New("private key not initialized") } return ed25519.Sign(ed25519Priv, message), nil }