All checks were successful
Sign Plugins / sign (push) Successful in 31s
- 修改 generate-store.sh 脚本添加 Gitea raw 文件基础 URL 配置 - 在 generate-store.sh 中为每个插件添加 download_url 和 signature_url 字段 - 扩展 signtool 工具添加 sign-json 命令用于签名 JSON 配置文件 - 更新 GitHub Actions 工作流添加对 security/*.json 文件的监控 - 新增 sign-security.sh 脚本用于批量签名安全相关 JSON 文件 - 添加 security/keys.json 和 security/revocation.json 模板文件
172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/gotunnel/plugins/tools/signtool/sign"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
printUsage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
switch os.Args[1] {
|
|
case "keygen":
|
|
cmdKeygen()
|
|
case "sign":
|
|
cmdSign(os.Args[2:])
|
|
case "sign-json":
|
|
cmdSignJSON(os.Args[2:])
|
|
default:
|
|
printUsage()
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func printUsage() {
|
|
fmt.Println("GoTunnel Plugin Sign Tool")
|
|
fmt.Println()
|
|
fmt.Println("Usage:")
|
|
fmt.Println(" signtool keygen")
|
|
fmt.Println(" signtool sign -key KEY -name NAME -version VER FILE")
|
|
fmt.Println(" signtool sign-json -key KEY FILE")
|
|
}
|
|
|
|
func cmdKeygen() {
|
|
kp, err := sign.GenerateKeyPair()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "生成密钥失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Println("=== 密钥对已生成 ===")
|
|
fmt.Println()
|
|
fmt.Println("私钥 (请妥善保管):")
|
|
fmt.Println(sign.EncodePrivateKey(kp.PrivateKey))
|
|
fmt.Println()
|
|
fmt.Println("公钥 (内置到客户端):")
|
|
fmt.Println(sign.EncodePublicKey(kp.PublicKey))
|
|
}
|
|
|
|
func cmdSign(args []string) {
|
|
fs := flag.NewFlagSet("sign", flag.ExitOnError)
|
|
keyFile := fs.String("key", "", "私钥文件路径")
|
|
name := fs.String("name", "", "插件名称")
|
|
version := fs.String("version", "", "插件版本")
|
|
keyID := fs.String("keyid", "official-v1", "密钥 ID")
|
|
fs.Parse(args)
|
|
|
|
if *keyFile == "" || *name == "" || *version == "" || fs.NArg() < 1 {
|
|
fmt.Println("用法: signtool sign -key KEY -name NAME -version VER FILE")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 读取私钥
|
|
keyData, err := os.ReadFile(*keyFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取私钥失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
privKey, err := sign.DecodePrivateKey(strings.TrimSpace(string(keyData)))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "解析私钥失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 读取插件文件
|
|
pluginFile := fs.Arg(0)
|
|
source, err := os.ReadFile(pluginFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取文件失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 创建载荷并签名
|
|
payload := sign.CreatePayload(*name, *version, string(source), *keyID)
|
|
signed, err := sign.SignPlugin(privKey, payload)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "签名失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 写入签名文件
|
|
sigData, _ := sign.EncodeSignedPlugin(signed)
|
|
sigFile := pluginFile + ".sig"
|
|
if err := os.WriteFile(sigFile, []byte(sigData), 0644); err != nil {
|
|
fmt.Fprintf(os.Stderr, "写入签名失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("签名成功: %s\n", sigFile)
|
|
fmt.Printf(" 插件: %s v%s\n", *name, *version)
|
|
fmt.Printf(" 哈希: %s\n", payload.SourceHash)
|
|
}
|
|
|
|
// cmdSignJSON 签名 JSON 配置文件(撤销列表、公钥列表等)
|
|
func cmdSignJSON(args []string) {
|
|
fs := flag.NewFlagSet("sign-json", flag.ExitOnError)
|
|
keyFile := fs.String("key", "", "私钥文件路径")
|
|
fs.Parse(args)
|
|
|
|
if *keyFile == "" || fs.NArg() < 1 {
|
|
fmt.Println("用法: signtool sign-json -key KEY FILE")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 读取私钥
|
|
keyData, err := os.ReadFile(*keyFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取私钥失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
privKey, err := sign.DecodePrivateKey(strings.TrimSpace(string(keyData)))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "解析私钥失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 读取 JSON 文件
|
|
jsonFile := fs.Arg(0)
|
|
data, err := os.ReadFile(jsonFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取文件失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 解析 JSON
|
|
var obj map[string]interface{}
|
|
if err := json.Unmarshal(data, &obj); err != nil {
|
|
fmt.Fprintf(os.Stderr, "解析 JSON 失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 移除现有签名字段
|
|
delete(obj, "signature")
|
|
|
|
// 序列化待签名数据
|
|
signData, _ := json.Marshal(obj)
|
|
|
|
// 签名
|
|
signature := sign.SignBase64(privKey, signData)
|
|
|
|
// 添加签名字段
|
|
obj["signature"] = signature
|
|
|
|
// 写回文件
|
|
output, _ := json.MarshalIndent(obj, "", " ")
|
|
if err := os.WriteFile(jsonFile, output, 0644); err != nil {
|
|
fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("签名成功: %s\n", jsonFile)
|
|
}
|