feat(backend): Ed25519 license signing utilities (21A)

- util/license_key.go: LicensePayload struct, GenerateEd25519KeyPair,
  IssueLicenseToken (sign), VerifyLicenseToken (verify offline)
- cmd/genkey: one-shot keypair generator with demo sign+verify
- config: add Ed25519PublicKey field (LICENSE_ED25519_PUBLIC_KEY env)

Token format: base64url(header).base64url(payload).base64url(ed25519-sig)
Private key stored in Bitwarden; public key embedded in config/client.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-10 00:28:55 +08:00
parent 18cd2497c1
commit 40a80467ce
3 changed files with 168 additions and 1 deletions
+3 -1
View File
@@ -32,7 +32,8 @@ type JWTConfig struct {
}
type LicenseConfig struct {
HMACSecret string `mapstructure:"hmac_secret"` // 许可证签名密钥
HMACSecret string `mapstructure:"hmac_secret"` // legacy, kept for backward compat
Ed25519PublicKey string `mapstructure:"ed25519_public_key"` // base64 Ed25519 public key for token verification
}
type StorageConfig struct {
@@ -57,6 +58,7 @@ func Load() {
_ = viper.BindEnv("database.dsn", "DATABASE_DSN")
_ = viper.BindEnv("jwt.secret", "JWT_SECRET")
_ = viper.BindEnv("license.hmac_secret", "LICENSE_HMAC_SECRET")
_ = viper.BindEnv("license.ed25519_public_key", "LICENSE_ED25519_PUBLIC_KEY")
_ = viper.BindEnv("storage.upload_dir", "STORAGE_UPLOAD_DIR")
_ = viper.BindEnv("storage.base_url", "STORAGE_BASE_URL")
_ = viper.BindEnv("storage.public_url", "STORAGE_PUBLIC_URL")