当使用自定义私钥调用签名接口时,出现以下错误:
{
"error": "签名失败: The multiplicator cannot be negative",
"success": false
}
###错误示例
curl -X POST http://localhost:8888/api/sign \
-H "Content-Type: application/json" \
-d '{
"data": {"version": "1.0"},
"priKey": "AABBCCDD0011223344556677889900AABBCCDD0011223344556677889900AA",
"reqOrgNo": "TEST123456"
}'
# 返回错误
{"error":"签名失败: The multiplicator cannot be negative","success":false}
问题出在 SM2Utils.java 第135行:
// 错误的代码
BigInteger userD = new BigInteger(privateKey);
当使用 new BigInteger(byte[]) 构造器时:
// 假设私钥第一个字节是 0xFA (250)
byte[] key = Hex.decode("FAA43158512CCA4DEBCA3047F4BD6F9E...");
// 错误方式:会被解释为负数
BigInteger wrong = new BigInteger(key);
// wrong < 0 ❌
// 正确方式:明确指定为正数
BigInteger correct = new BigInteger(1, key);
// correct > 0 ✅
在 /Users/light/www/pros/xingfutong-java/src/demo/com/util/sm/SM2Utils.java 第135行:
修改前:
BigInteger userD = new BigInteger(privateKey);
修改后:
BigInteger userD = new BigInteger(1, privateKey); // 1 表示正数符号位
代码已自动修复(参考上述修改)。
cd /Users/light/www/pros/xingfutong-java
# 编译SM2Utils
javac -encoding UTF-8 -d bin -cp "lib/*:bin" \
src/demo/com/util/sm/SM2Utils.java
# 停止旧容器
docker-compose down
# 重新构建镜像
docker-compose build --no-cache
# 启动新容器
docker-compose up -d
或使用一键脚本:
./deploy.sh
java -cp "bin:lib/*" demo.com.GenerateKeyPair
输出示例:
私钥 (64位十六进制):
FAA43158512CCA4DEBCA3047F4BD6F9E3BF1C10685E5FFB80B34B48DA854DCC1
公钥 (130位十六进制):
049DC52B5FFB819C19DB9FA4DC1AF97B754810E9EBCFF85D83BDFAB452C5DD53E8...
curl -X POST http://localhost:8888/api/sign \
-H "Content-Type: application/json" \
-d '{
"data": {
"version": "1.0",
"test": "hello"
},
"priKey": "FAA43158512CCA4DEBCA3047F4BD6F9E3BF1C10685E5FFB80B34B48DA854DCC1",
"reqOrgNo": "MY_ORG"
}'
期望结果(修复后):
{
"success": true,
"signData": "TEST=hello|VERSION=1.0",
"sign": "3044022015A7C08F039EAFAD3330D9B4456BD21B...",
"timestamp": 1761292521531
}
Java的 BigInteger 类有多个构造器:
// 1. 从字节数组构造(有符号)
new BigInteger(byte[] val)
// - 使用二进制补码表示
// - 第一个字节的最高位是符号位
// - 如果最高位=1,则为负数
// 2. 从字节数组构造(指定符号)
new BigInteger(int signum, byte[] magnitude)
// - signum: -1负数, 0零, 1正数
// - magnitude: 绝对值的二进制表示
// - 总是按无符号解释字节数组
SM2私钥是32字节(256位)的随机数:
因此,如果不指定符号位,大约一半的私钥会被错误解释为负数!
项目中的默认私钥:
3164EE0DF2BCA7A12309383E3305DD6563A28DFE53F65BBD60B3A1D7F80AC275
第一个字节是 0x31 (49),小于 128,所以被正确解释为正数。
第一个字节 >= 128(80-FF)的私钥会出错,例如:
FAA43158... (0xFA = 250)AAB43158... (0xAA = 170)80000000... (0x80 = 128)第一个字节 < 128(00-7F)的私钥正常,例如:
31640000... (0x31 = 49)7FFFFFFF... (0x7F = 127)不需要! 修复后,所有有效的SM2私钥都能正常使用。
项目已包含密钥对生成工具 GenerateKeyPair.java。
# 生成1对密钥
java -cp "bin:lib/*" demo.com.GenerateKeyPair
# 生成5对密钥
java -cp "bin:lib/*" demo.com.GenerateKeyPair 5
⚠️ 私钥必须保密!
⚠️ 使用HTTPS!
⚠️ 定期轮换密钥!
| 项目 | 说明 |
|---|---|
| 问题 | 部分私钥签名失败 |
| 原因 | BigInteger构造器未指定正数符号位 |
| 影响 | 约50%的随机私钥受影响 |
| 修复 | 使用 new BigInteger(1, privateKey) |
| 状态 | 已修复 ✅ |
| 测试 | 需重新编译和部署 |
最后更新: 2025-10-24
修复作者: AI Assistant