You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go生成DSA签名后Java公钥验证返回false问题求解

问题根源
  • DSA签名规范的哈希前置要求:Go的crypto/dsa库的dsa.Sign方法要求用户提前对待签名数据做哈希处理,默认要求传入符合DSA参数位数的哈希值(通常为160位的SHA1哈希结果,对应20字节)。你直接传入[]byte{1}作为待签名数据,相当于跳过了标准哈希步骤,和Java端DSA实现的默认逻辑不匹配。
  • Java端不同DSA算法标识的逻辑差异:
    • DSA是SHA1withDSA的别名,会自动对输入的原始数据做SHA1哈希后再验证签名,和Go端直接签名1字节原始数据的输入内容不一致,验证必然失败。
    • NONEwithDSA要求输入的待验证数据必须是已经完成哈希的20字节固定长度值,你直接传入1字节的[]byte{1}不符合长度要求,因此抛出异常。
    • 使用SHA1withDSA时,Java端会自动将[]byte{1}计算一次SHA1得到20字节哈希值再验签,和Go端直接签名的1字节数据完全不匹配,因此返回false。
  • 次要兼容问题:小概率存在Go端生成的R、S值ASN1编码和Java端要求的DER格式有细微差异(比如整数前导0的处理规则不同),但该问题在当前场景下不是核心矛盾。
解决方案

方案1(推荐):修改Go端签名逻辑对齐DSA标准规范

符合DSA的通用安全设计,也能兼容绝大多数语言的默认DSA实现:

  1. Go端签名前先对原始数据做SHA1哈希,再传入dsa.Sign方法:
import (
    "crypto/dsa"
    "crypto/sha1"
)
// 原始待签名数据
rawData := []byte{1}
// 先计算SHA1哈希得到20字节结果
hashed := sha1.Sum(rawData)
// 再用DSA私钥对哈希结果签名
r, s, err := dsa.Sign(rand.Reader, privateKey, hashed[:])
// 后续ASN1编码、转十六进制的逻辑保持不变
  1. Java端直接使用Signature.getInstance("SHA1withDSA")初始化验证器,传入原始[]byte{1}即可验证通过。

方案2(临时兼容):修改Java端验签逻辑对齐现有Go端签名

如果无法修改Go端已上线的签名逻辑,可调整Java端处理逻辑适配:

  1. 把待验证的1字节原始数据补前导0到20字节长度,适配NONEwithDSA的输入要求:
// 原始待验证数据
byte[] rawData = new byte[]{1};
// 补前导0到20字节
byte[] paddedData = new byte[20];
System.arraycopy(rawData, 0, paddedData, 20 - rawData.length, rawData.length);
// 初始化NONEwithDSA验证器
Signature dsaVerify = Signature.getInstance("NONEwithDSA");
dsaVerify.initVerify(publicKey);
dsaVerify.update(paddedData);
// 传入十六进制解码后的ASN1格式签名即可验证
boolean verifyResult = dsaVerify.verify(signatureBytes);

注意:方案2不符合DSA签名的安全规范,仅作为临时兼容方案使用,生产环境优先选择方案1

内容的提问来源于stack exchange,提问作者TBC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 23:39:02