我知道这听起来是个宽泛的问题,但我可以用一个例子缩小范围。我在Java是个新手。对于我的一个“学习”项目,我想创建一个内部的MD5文件- hasher供我们使用。我一开始非常简单,尝试散列一个字符串,然后再转到一个文件。我创建了一个名为MD5Hasher.java的文件,并编写了以下内容:
import java.security.*;
import java.io.*;
public class MD5Hasher{
public static void main(String[] args){
String myString = "Hello, World!";
byte[] myBA = myString.getBytes();
MessageDigest myMD;
try{
myMD = MessageDigest.getInstance("MD5");
myMD.update(myBA);
byte[] newBA = myMD.digest();
String output = newBA.toString();
System.out.println("The Answer Is: " + output);
} catch(NoSuchAlgorithmException nsae){
// print error here
}
}
}我访问了java.sun.com来查看java.security的javadocs,以了解如何使用MessageDigest类。在阅读完之后,我知道我必须使用"getInstance“方法来获得一个可用的MessageDigest对象。Javadoc接着说:“数据是通过它使用更新方法处理的。”因此,我查看了update方法,确定我需要使用给它一个字符串字节数组的方法,所以我添加了这个部分。Javadoc接着说:“一旦所有要更新的数据都被更新了,就应该调用其中一个摘要方法来完成哈希计算。”我再次查看了这些方法,并看到该摘要返回了一个字节数组,因此我添加了这个部分。然后,我在新字节数组上使用了"toString“方法来获取可以打印的字符串。但是,当我编译和运行代码时,输出的所有内容如下:
答案是:[B@4cb162d5
我在StackOverflow上做了一些调查,并在这里发现了一些信息:
这给出了以下例子:
String plaintext = 'your text here';
MessageDigest m = MessageDigest.getInstance("MD5");
m.reset();
m.update(plaintext.getBytes());
byte[] digest = m.digest();
BigInteger bigInt = new BigInteger(1,digest);
String hashtext = bigInt.toString(16);
// Now we need to zero pad it if you actually want the full 32 chars.
while(hashtext.length() < 32 ){
hashtext = "0"+hashtext;
}这似乎是我唯一可能错过的部分是"BigInteger“部分,但我不确定。
所以,在这一切之后,我想我要问的是,你怎么知道如何使用"BigInteger“部分?我错误地认为newBA对象上的“newBA”方法会将其转换为可读的输出,但我显然错了。一个人怎么知道用Java走哪条路呢?我有C的背景,所以这个看起来很奇怪。对于如何在谷歌上搜索如何一直做一些事情,我如何才能变得更好而不必“欺骗”,有什么建议吗?
谢谢大家抽出时间阅读。:-)
发布于 2010-07-13 15:31:41
你实际上已经成功地理解了这条信息。您只是不知道如何正确地表示已找到的摘要值。您拥有的是一个字节数组。这有点难读,字节数组的toString产生的[B@somewhere根本没用。
BigInteger将其作为将字节数组格式化为单个数字的工具。
你所做的是:
而while循环将该值前缀为0-字符,以获得32的宽度。我可能会为此使用String.format,但是无论什么东西浮在你的船上:)
发布于 2010-07-13 15:29:17
在这种特殊情况下,关键是您需要认识到,字节不是“人类可读的”,而是字符。因此,您需要以某种格式将字节转换为字符。对于散列之类的任意字节,通常使用十六进制作为“人类可读的”格式。然后将每个字节转换为一个2字符的十六进制字符串,然后将它们连接在一起。
这与你使用的语言无关。你只需要理解/意识到它是如何以一种语言不可知论的方式“在罩下”工作的。您必须了解您拥有什么(字节数组)和需要什么(一个六边形)。编程语言只是实现预期结果的工具。您只需搜索“功能需求”以及您希望使用的编程语言来实现该需求。例如"在java中将字节数组转换为十六进制字符串“。
尽管如此,您发现的代码示例是错误的。实际上,您应该确定循环中的每个字节,并测试它是否小于0x10,然后将其填充为零,而不是仅根据结果字符串的长度填充零(这不一定是由于第一个字节小于0x10!)。
StringBuilder hex = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
if ((b & 0xff) < 0x10) hex.append("0");
hex.append(Integer.toHexString(b & 0xff));
}
String hexString = hex.toString();更新,根据对@extraneon答案的注释,使用new BigInteger(byte[])也是错误的解决方案。这不会解除字节的签名。Java中的字节(所有原语数字)都是有符号的。他们有一个负值范围。Java中的byte从-128到127,而您希望有一个从0到255的范围,以获得一个正确的十六进制。你只需要移除这个标志就可以让他们不签名了。上面示例中的& 0xff正是这样做的。
从new BigInteger(bytes).toString(16)获得的十六进制与生成MD5生成器的所有其他六边形的结果不兼容。每当您在MD5摘要中有一个负字节时,它们就会有所不同。
发布于 2010-07-13 15:17:04
只要你(最终)理解你把什么拷贝粘贴到你的应用程序:-),谷歌的答案就可以了。
总的来说,我建议从一本好的Java入门书或网络教程开始。有关更多提示,请参见这些线程:
https://stackoverflow.com/questions/3238548
复制相似问题