Current section
Files
Jump to
Current section
Files
cli.md
# Guomi CLI 使用说明
命令行工具提供 SM2/SM3/SM4 国密算法的直接操作能力。所有算法均为纯 Elixir 实现,无需运行时 OpenSSL 支持。
## 安装
```bash
mix escript.build
```
编译后在项目目录生成 `guomi`(Windows 下为 `guomi.exe`)。
## 全局用法
```bash
guomi <command> [options] [input]
```
### 输入解析规则
- 未提供 `[input]` 时,从标准输入读取。
- `[input]` 为 `-` 或 `--` 时,从标准输入读取。
- 一个或多个普通参数均以空格连接后作为消息内容。
- 使用 `--file <path>` 显式读取文件;`--file -` 从标准输入读取。
- `--file` 与普通位置参数不能同时使用。
- SM2 的 `--message` 优先于上述输入来源。
因此 `guomi sm3 --hex hello` 会直接计算文本 `hello`,不会尝试打开同名文件。
## 命令一览
| 命令 | 说明 |
|------|------|
| `sm3` | 计算 SM3 哈希 |
| `sm4` | SM4 加密/解密 |
| `sm2` | SM2 密钥生成、签名/验签、加密/解密 |
| `version` | 显示版本信息 |
| `help` | 显示帮助信息 |
---
## sm3 — SM3 哈希
### 用法
```
guomi sm3 [options] [input]
```
### 选项
| 选项 | 说明 |
|------|------|
| `--hex` | 十六进制输出(默认输出原始二进制) |
| `--file <path>` | 从文件读取输入;使用 `-` 表示 stdin |
| `--help` | 显示帮助信息 |
### 输入来源
SM3 遵循[全局输入解析规则](#输入解析规则)。
### 示例
```bash
# 从 stdin 读取,输出十六进制哈希
echo -n "hello" | guomi sm3 --hex
#=> becbbfaae6548b8bf0cfcad5a27183cd1be6093b1cceccc303d9c61d0a645268
# 从多个参数读取消息
guomi sm3 --hex hello world
# 从文件读取
guomi sm3 --hex --file document.txt
# 输出原始二进制(默认)
echo -n "hello" | guomi sm3
```
### 参考
SM3("abc") 标准测试向量:
```bash
echo -n "abc" | guomi sm3 --hex
#=> 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0
```
---
## sm4 — SM4 加密/解密
### 用法
```
guomi sm4 [options] [input]
```
### 选项
| 选项 | 说明 |
|------|------|
| `--mode <ecb\|cbc\|ctr>` | 加密模式,默认 `ecb` |
| `--key <hex>` | **必填**。16 字节密钥,十六进制编码 |
| `--iv <hex>` | CBC 模式必填。16 字节初始向量,十六进制编码 |
| `--counter <hex>` | CTR 模式必填。16 字节大端初始计数器,十六进制编码 |
| `--decrypt` | 解密模式(默认加密) |
| `--hex` | 快捷方式:加密时输出 hex,解密时输入为 hex |
| `--input-hex` | 输入视为十六进制文本 |
| `--output-hex` | 输出为十六进制文本 |
| `--padding <pkcs7\|none>` | 填充方式,默认 `pkcs7` |
| `--file <path>` | 从文件读取输入;使用 `-` 表示 stdin |
| `--help` | 显示帮助信息 |
### 安全提示
- **ECB 模式**只适合测试或兼容遗留系统,**不建议用于新数据加密**
- **CBC 模式**必须为每次加密使用**不可预测且不复用**的 16 字节 IV
- 同一密钥下 IV 复用会完全破坏机密性
- ECB/CBC 均不提供消息认证;应用层需要单独保证完整性
- CTR 的同一 key/counter 组合不得复用;CTR 不使用 padding,也不提供消息认证
### 示例
```bash
# ECB 加密,输出原始密文
echo "secret data" | guomi sm4 --key 0123456789abcdef0123456789abcdef
# ECB 加密,输出十六进制密文
echo "secret data" | guomi sm4 --key 0123456789abcdef0123456789abcdef --hex
# ECB 解密(输入为十六进制密文)
guomi sm4 --decrypt --hex --key 0123456789abcdef0123456789abcdef < ciphertext.hex
# CBC 加密
echo "secret data" | guomi sm4 --mode cbc \
--key 0123456789abcdef0123456789abcdef \
--iv 00112233445566778899aabbccddeeff
# CBC 解密
guomi sm4 --decrypt --mode cbc \
--key 0123456789abcdef0123456789abcdef \
--iv 00112233445566778899aabbccddeeff < ciphertext.bin
# --hex 模式解密
guomi sm4 --decrypt --mode cbc \
--key 0123456789abcdef0123456789abcdef \
--iv 00112233445566778899aabbccddeeff \
--hex < ciphertext.hex
# 显式控制 hex 输入/输出
echo -n "736563726574" | guomi sm4 \
--input-hex --output-hex \
--key 0123456789abcdef0123456789abcdef
# 无填充(输入必须是 16 字节对齐)
echo -n "abcdefghijklmnop" | guomi sm4 \
--key 0123456789abcdef0123456789abcdef \
--padding none
# CTR(任意长度;不要复用相同的 key/counter)
echo -n "secret" | guomi sm4 --mode ctr --output-hex \
--key 0123456789abcdef0123456789abcdef \
--counter 00112233445566778899aabbccddeeff
```
---
## sm2 — SM2 密钥生成、签名/验签、加密/解密
### 用法
```
guomi sm2 [options] [input]
```
### 通用选项
| 选项 | 说明 |
|------|------|
| `--generate` | 生成密钥对 |
| `--sign` | 签名 |
| `--verify` | 验签 |
| `--encrypt` | 加密 |
| `--decrypt` | 解密 |
| `--private-key <hex>` | 私钥(32 字节,十六进制) |
| `--public-key <hex>` | 公钥(65 字节,04 前缀 + 32 字节 x + 32 字节 y,十六进制) |
| `--message <msg>` | 待签名/验签/加密的消息文本 |
| `--file <path>` | 从文件读取消息或密文;使用 `-` 表示 stdin |
| `--signature <hex>` | 签名值(64 字节 raw r \|\| s,十六进制,仅验签) |
| `--ciphertext <hex>` | 密文(十六进制,仅解密) |
| `--help` | 显示帮助信息 |
### 兼容性说明
- 签名算法使用 SM3 预哈希 + raw 64 字节 `r || s` 格式,未暴露用户 ID/ZA 参数
- 加密使用 Guomi 内部 `C1 || C2 || C3` 格式(C1=65 字节临时公钥, C3=32 字节 SM3 MAC)。当前实现对超过 32 字节的消息重复 XOR 掩码,会泄露相隔 32 字节的明文 XOR 关系;仅用于兼容和测试,不得用于敏感数据或生产协议
- **不应假定可与 OpenSSL 或其他 SM2 实现互通**
- 标准 ZA 签名和 `C1 || C3 || C2` 加密目前仅通过库 API 提供;CLI 保持旧兼容行为,避免无提示改变已有脚本
### 示例
```bash
# 生成密钥对
guomi sm2 --generate
#=>
# Private Key:
# <32-byte-hex>
# Public Key:
# <65-byte-hex>
# 签名(从 stdin 读取消息)
echo "message to sign" | guomi sm2 --sign --private-key <hex-key>
# 签名(从 --message 参数读取)
guomi sm2 --sign --private-key <hex-key> --message "hello"
# 验签
guomi sm2 --verify \
--public-key <hex-pubkey> \
--signature <hex-signature> \
--message "hello"
# 验签(从文件读取消息)
guomi sm2 --verify \
--public-key <hex-pubkey> \
--signature <hex-signature> \
--file message.txt
# 加密
echo "secret message" | guomi sm2 --encrypt --public-key <hex-pubkey>
# 解密
guomi sm2 --decrypt \
--private-key <hex-privkey> \
--ciphertext <hex-ciphertext>
# 解密(文件内容必须是 hex 文本)
guomi sm2 --decrypt \
--private-key <hex-privkey> \
--file ciphertext.hex
```
---
## 常见问题
### Q: `guomi sm3 --hex` 和 `guomi sm3` 输出有什么区别?
`--hex` 输出 64 字符十六进制小写字符串,末尾换行。不指定时输出原始 32 字节二进制数据,适合管道传递。
### Q: SM4 的 `--hex`、`--input-hex`、`--output-hex` 有什么区别?
| 标志 | 加密时 | 解密时 |
|------|--------|--------|
| `--hex` | 输出 hex | 输入为 hex |
| `--input-hex` | 输入为 hex | 输入为 hex |
| `--output-hex` | 输出 hex | 输出 hex |
| 无 | 输入/输出均为原始二进制 | 输入/输出均为原始二进制 |
### Q: SM2 生成的密钥对格式是什么?
- **私钥**: 32 字节大端整数,十六进制编码(64 字符)
- **公钥**: 65 字节,`0x04` 前缀 + 32 字节 x 坐标 + 32 字节 y 坐标,十六进制编码(130 字符)
### Q: Windows 下 `guomi` 运行报 `SetConsoleMode` 错误怎么办?
这是 OTP 28 在 Windows 上的兼容性问题。在运行前设置环境变量可绕过:
```cmd
set ELIXIR_ERL_OPTIONS=-noinput
guomi version
```
或一次性命令:
```cmd
set ELIXIR_ERL_OPTIONS=-noinput && guomi version
```
### Q: 返回码约定?
| 场景 | 退出码 |
|------|--------|
| 操作成功 | 0 |
| 输入错误/参数缺失 | 1 |
| 验签失败(签名无效) | 1 |
| SM2 不支持 | 1 |
> 当前版本为纯 Elixir 实现,受支持运行时上的 `SM2 不支持` 分支不会在正常操作中出现。
## 格式与兼容性速查
| 数据 | 格式 |
|------|------|
| SM3 摘要 | 32 字节;`--hex` 时为 64 个小写十六进制字符 |
| SM4 key / CBC IV | 16 字节,以 32 个十六进制字符传入 |
| SM2 私钥 | 32 字节大端整数,hex 编码 |
| SM2 公钥 | 65 字节未压缩点 `0x04 || x || y`,hex 编码 |
| SM2 签名 | 64 字节 raw `r || s`,hex 编码 |
| SM2 CLI 密文 | 旧兼容 `C1 || C2 || C3` 格式,hex 编码;标准 `C1 || C3 || C2` 仅通过库 API 提供 |