SHA哈希函数输出为负数

5

我正在尝试实现DSA签名算法,但有一个问题困扰着我。我正在使用java.securityMessageDigest类,以下是代码:

MessageDigest md;
md = MessageDigest.getInstance("SHA-1");
md.update(text.getBytes());
return new BigInteger(md.digest());

文本是一个随机的字符串对象。问题在于这段代码给我负数的哈希值,而算法不接受负数。我做错了什么吗?谢谢。

顺便说一下,我也尝试过实现DSA而不使用BigIntegers,这是可能的吗?我没有找到小于1024和160的L和N值,所以我不知道应该取什么值和使用什么哈希函数。非常感谢回答这些问题。


2
那么你期望什么?BigInteger只是一个带有方法的byte[]。MessageDigest和BigInteger的外部byte[]表示没有接触点。 - bestsss
@bestsss,如何将摘要转换为BigInteger?对于我问题中的P.S.部分,您有什么想法吗?感谢您的回答。 - Egor
为什么你要重新实现DSA?通常情况下,你应该使用系统提供的功能;重新实现加密是危险的,因为它可能会导致微妙的安全漏洞。 - crazyscot
@crazyscot 这是我在大学的作业,老师告诉我不要使用内置类。 - Egor
如果您严格需要一个正(或零)的BigInteger,请调用abs()。在这种情况下,您会失去一位,或者您可以将绝对最小值添加到结果中以确保它始终为正数。我只是看不出您的问题所在:需要某些随机序列,然后进行哈希处理,然后用作整数的算法的目的是什么?如果您只需要以某种方式表示哈希值,则有比BigInteger更好的选项。 - bestsss
我不支持以上任何评论。你代码的唯一问题在于应该使用new BigInteger(1, md.digest())。 - class stacker
4个回答

3
MessageDigest md;
md = MessageDigest.getInstance("SHA-1");
md.update(text.getBytes());
return new BigInteger(1, md.digest()); // use this 1 to tell it is positive.

然后,您可以使用以下方式将哈希转换为字符串:
String hash = biginteger.toString(16);

然后可以选择性地在前面添加前导零。
String zeros = String.format("%032d", 0);
hash = zeros.substring(hash.length()) + hash;

1
首先,没有MessageDigest.toString(base)方法,这也是第一个问题的原因。我想这就是为什么会有这个问题。上面的第二段代码应该使用BigInteger.toString(16)。其次,没有必要填充零。即使是最糟糕的摘要函数也不会返回一个值为零的四个连续位的哈希值。 - class stacker
是的,你说得对,我已经修复了。至于填充方面,这也是我写“最终”的原因。 - Martijn Courteaux
@MartijnCourteaux 对于我评论的第二部分,我很抱歉。哈希确实可以生成八个连续的零。我以为不行,但被证明是错误的。 - class stacker
@ClassStacker:哦,等等。Eventually的意思是“最终”。我本来想写“可选”的。 - Martijn Courteaux

3

你为什么感到惊讶?MessageDigest#digest()返回均匀分布的160位数据。它们通常表示为十六进制字符串,但如果将它们转换为整数,则最高有效位指定符号。请查看以下代码:

System.out.println(new BigInteger(new byte[]{(byte) 255}));  //-1

1
你正在将返回给 BigInteger 构造函数 的字节传递。虽然类型匹配,但我不确定你想在这里实现什么目标。从 BigInteger JavaDoc 中可以看到:

翻译一个包含 二进制补码表示的 BigInteger 的字节数组


0

不要重复造轮子,特别是在加密方面 -- 使用 java.security.Signature 或更高级别的库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接