licensing-cotton/internal/security/ed22519.go
2025-11-01 15:19:24 +08:00

151 lines
3.9 KiB
Go

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
}