151 lines
3.9 KiB
Go
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
|
|
}
|