chisel 是一个轻量级 Go 包,用于在编译后的二进制文件中嵌入和提取配置数据。通过字节级替换技术,支持在不重新编译的情况下为不同客户端生成定制化二进制文件。
适用场景:
| 特性 | 说明 |
|---|---|
| 泛型支持 | 完全类型安全,支持任意配置结构体 |
| 统一模式 | 默认内置占位符,支持自定义占位符与大小 |
| 签名防篡改 | Ed25519 签名验证(默认开启) |
| 混淆保护 | AES-CTR 混淆,降低明文可读性(默认开启) |
| 完整性校验 | CRC32 快速完整性校验 |
| 压缩支持 | gzip 压缩(可选) |
| 零依赖 | 仅依赖 Go 标准库,无第三方依赖 |
| 可配置 | 支持自定义占位符、块大小、混淆与签名器 |
| 流式接口 | 提供流式 API(当前实现会读取全量输入) |
注意:混淆仅用于降低可读性,不提供真正保密性。
警告:默认签名密钥与混淆密钥为内置示例值,仅用于演示与测试。 生产环境务必使用
GenerateEd25519KeyPair与GenerateObfuscationKey生成并替换为自有密钥。
go get cnb.cool/zishuo/chisel
注:需要 Go 1.23 或更高版本(支持泛型)
chisel 采用单包主入口设计。
import "cnb.cool/zishuo/chisel"
推荐按这三层心智使用:
| 层级 | 适用场景 | 推荐 API |
|---|---|---|
| 直接函数 | 90% 日常场景 | chisel.Extract / chisel.Generate |
| 可复用对象 | 批量处理、长期复用 | chisel.NewExtractor / chisel.NewGenerator |
| 高级选项 | 自定义占位符、压缩、签名与混淆 | chisel.With* |
原则只有一句:能直接调用函数,就不要先创建对象。
客户端代码:
package main
import (
"context"
"log"
"cnb.cool/zishuo/chisel"
)
// Config 配置结构
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
Secure bool `json:"secure"`
}
func main() {
// 默认使用根包统一入口
cfg, err := chisel.Extract[Config](context.Background())
if err != nil {
log.Fatal(err)
}
protocol := "http"
if cfg.Secure {
protocol = "https"
}
log.Printf("✅ 服务器: %s://%s:%d", protocol, cfg.Host, cfg.Port)
}
服务端代码:
package main
import (
"context"
"log"
"cnb.cool/zishuo/chisel"
)
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
Secure bool `json:"secure"`
}
func main() {
// 创建配置
cfg := Config{
Host: "api.example.com",
Port: 443,
Secure: true,
}
// 生成配置化的客户端二进制
err := chisel.Generate(context.Background(), "client.template", "client", cfg)
if err != nil {
log.Fatal(err)
}
log.Println("✅ 客户端生成成功!")
}
使用步骤:
client.templateclient 可执行文件client,自动读取配置服务端可直接使用根包入口,或显式构造可复用的 Generator。
// 一次性生成(推荐)
err := chisel.Generate(ctx, templatePath, outputPath, config)
if err != nil {
log.Fatal(err)
}
// 创建可复用生成器
gen, err := chisel.NewGenerator[ConfigType](
chisel.WithMaxSize(64 * 1024), // 自定义块大小
chisel.WithCompressor(compressor), // 启用压缩
chisel.WithPlaceholder(placeholder),// 自定义占位符
chisel.WithSigner(signer), // 自定义签名器
chisel.WithObfuscator(obfuscator), // 自定义混淆器
)
if err != nil {
log.Fatal(err)
}
// 流式处理大文件
err = gen.GenerateStream(ctx, inputReader, outputWriter, config)
说明:
Generate 会尽量保留模板文件的权限位,并通过临时文件 + rename 降低输出文件被部分写入的风险Generate / GenerateStream 会在关键阶段检查 context.Context,支持取消客户端可直接使用根包入口,或显式构造可复用的 Extractor。
// 直接提取(推荐)
cfg, err := chisel.Extract[ConfigType](context.Background())
if err != nil {
log.Fatal(err)
}
// 创建可复用提取器
ext, err := chisel.NewExtractor[ConfigType]()
if err != nil {
log.Fatal(err)
}
// 传入自定义占位符(字符串)
cfg, err := ext.ExtractFrom(context.Background(), userPlaceholder)
// 从任意 Reader 中扫描首个合法配置块
cfg, err := ext.ExtractFromReader(context.Background(), reader, chisel.PlaceholderSize())
说明:
ExtractFromReader 会扫描输入流中的首个合法配置块,而不是命中第一个 MagicMarker 就立即返回context.Context,在取消后尽早停止错误处理:
cfg, err := ext.Extract(ctx)
if err != nil {
switch err {
case chisel.ErrNoConfig:
log.Println("配置未被注入(占位符未替换)")
case chisel.ErrInvalidMagic:
log.Println("魔术标记无效")
case chisel.ErrSignatureMismatch:
log.Println("签名校验失败,配置可能被篡改")
default:
log.Fatalf("提取失败: %v", err)
}
}
// 生成 Ed25519 密钥对
pub, priv, err := chisel.GenerateEd25519KeyPair()
// 生成混淆密钥
obfKey, err := chisel.GenerateObfuscationKey(32)
// 生成占位符字节
placeholder, err := chisel.GeneratePlaceholder(chisel.MagicMarker, 64*1024)
gzip 压缩可显著减少配置大小,尤其是配置中包含重复数据时。
// 服务端:启用 gzip 压缩
gen, err := chisel.NewGenerator[Config](
chisel.WithCompressor(chisel.NewGZipCompressor()),
)
if err != nil {
log.Fatal(err)
}
err := gen.Generate(ctx, template, output, cfg)
// 客户端:自动检测并解压(无需额外配置)
ext, err := chisel.NewExtractor[Config]()
if err != nil {
log.Fatal(err)
}
cfg, err := ext.Extract(ctx) // 自动解压(如果配置被压缩)
处理大文件时使用流式接口:
注意:当前实现会将输入读入内存以查找占位符,适用于大多数场景。
templateFile, err := os.Open("client.template")
if err != nil {
log.Fatal(err)
}
defer templateFile.Close()
outputFile, err := os.Create("client")
if err != nil {
log.Fatal(err)
}
defer outputFile.Close()
gen, err := chisel.NewGenerator[Config]()
if err != nil {
log.Fatal(err)
}
err = gen.GenerateStream(ctx, templateFile, outputFile, cfg)
if err != nil {
log.Fatal(err)
}
根据需要调整配置块大小:
// 增加块大小以容纳更大的配置
gen, err := chisel.NewGenerator[Config](
chisel.WithMaxSize(64 * 1024),
)
if err != nil {
log.Fatal(err)
}
// 减小块大小以节省空间
gen, err := chisel.NewGenerator[Config](
chisel.WithMaxSize(16 * 1024),
)
if err != nil {
log.Fatal(err)
}
placeholder, err := chisel.GeneratePlaceholder(chisel.MagicMarker, 128*1024)
if err != nil {
log.Fatal(err)
}
// 生成器指定占位符
gen, err := chisel.NewGenerator[Config](
chisel.WithPlaceholder(placeholder),
)
if err != nil {
log.Fatal(err)
}
// 客户端提取时传入相同占位符
ext, err := chisel.NewExtractor[Config]()
if err != nil {
log.Fatal(err)
}
cfg, err := ext.ExtractFromBytes(ctx, placeholder)
说明:
GeneratePlaceholder 返回 []byte,因为占位符本质上是二进制块ExtractFromBytesExtractFrom(string) 更适合源码内静态字符串占位符(如 const / var)编译时: ┌─────────────────────┐ │ 客户端源代码 │ │ + 占位符(32KB) │ └──────────┬──────────┘ │ go build ▼ ┌─────────────────────┐ │ 客户端二进制 │ │ - 包含占位符 │ └─────────────────────┘ 生成时: ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ 配置结构体 │──────▶│ 服务端生成器 │──────▶│ 配置化客户端 │ └──────────────┘ │ - 序列化配置 │ │ - 占位符被替换 │ │ - 压缩(可选) │ │ - 配置已注入 │ │ - 混淆 │ └──────────────────┘ │ - 签名 │ └──────────────────┘ 运行时: ┌─────────────────────┐ │ 配置化客户端 │ └──────────┬──────────┘ │ 启动 ▼ ┌─────────────────────┐ │ 提取器 │ │ - 查找配置块 │ │ - 验证签名 │ │ - 反混淆 │ │ - 反序列化 │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ 配置对象 │ │ (立即可用) │ └─────────────────────┘
┌─────────────────────────────────────────────────────────────┐ │ 配置块(32KB,可配置) │ ├─────────────────────────────────────────────────────────────┤ │ Header(20 字节) │ │ ├─ Magic (8B):BINCFG\x00\x02 │ │ ├─ Length (4B):数据块长度 │ │ ├─ Checksum (4B):CRC32 校验和 │ │ └─ Flags (4B):压缩/混淆/签名标记 │ ├─────────────────────────────────────────────────────────────┤ │ 数据块(可变长度,<= 块大小 - 20) │ │ ├─ 签名 (64B):Ed25519 签名 │ │ └─ 混淆数据: │ │ ├─ IV (16B):AES-CTR IV │ │ └─ JSON 数据(可选压缩) │ ├─────────────────────────────────────────────────────────────┤ │ 填充(0xFF,填充至块大小) │ └─────────────────────────────────────────────────────────────┘
| 特性 | 实现 | 说明 |
|---|---|---|
| 签名 | Ed25519 | 防篡改校验(默认) |
| 混淆 | AES-CTR | 降低明文可读性(默认) |
| 校验和 | CRC32 | 快速检测损坏 |
混淆不提供真正保密性,仅用于降低明文可读性。
| 操作 | 时间 | 大小 |
|---|---|---|
| Header 序列化 | < 1μs | 20 字节 |
| 配置序列化 (JSON) | < 100μs | 取决于配置 |
| 混淆 (AES-CTR) | < 100μs | 小配置 |
| gzip 压缩 | < 10ms | 1MB 数据 |
| 完整生成流程 | < 50ms | 典型场景 |
| 完整提取流程 | < 50ms | 典型场景 |
客户端模板 (包含 32KB 占位符):~4.05MB - Go 二进制基础大小:~4.0MB - 占位符占用:32KB
✅ 当前主包覆盖率: 75.7%
核心模块: ✅ config.go (Header, Flags) ✅ crypto.go (Ed25519, AES-CTR) ✅ compress.go (gzip) ✅ placeholder.go (32KB) ✅ generator_api.go ✅ extractor_api.go
可以。默认开启 Ed25519 签名校验,任何修改都会导致验证失败。
混淆只能降低明文可读性,不能提供真正保密性。
// 自定义更大的占位符
placeholder, _ := chisel.GeneratePlaceholder(chisel.MagicMarker, 128*1024)
// 使用自定义占位符
gen, err := chisel.NewGenerator[Config](
chisel.WithPlaceholder(placeholder),
)
if err != nil {
log.Fatal(err)
}
欢迎提交 Issue 和 Pull Request!
MIT License - 详见 LICENSE 文件
版本: v0.2.0 最后更新: 2025-10-29