一个人如何学习Java?(将字节数组转换为十六进制字符串

Bri*_*ian 5 java md5

我知道这听起来像一个广泛的问题,但我可以用一个例子来缩小范围.我在Java上非常新.对于我的一个"学习"项目,我想创建一个内部MD5文件,供我们使用.我开始非常简单,尝试散列字符串,然后再转到文件.我创建了一个名为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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我访问了java.sun.com以查看java.security的javadoc以了解如何使用MessageDigest类.阅读后我知道我必须使用"getInstance"方法来获取我可以使用的可用MessageDigest对象.Javadoc继续说"使用更新方法通过它处理数据." 所以我查看了更新方法并确定我需要使用我给它提供字符串的字节数组的那个,所以我添加了那个部分.Javadoc接着说:"一旦更新了所有要更新的数据,就应该调用其中一个摘要方法来完成哈希计算." 我再次看了一下方法,看到摘要返回了一个字节数组,所以我添加了那个部分.然后我在新的字节数组上使用"toString"方法来获取我可以打印的字符串.然而,

答案是:[B @ 4cb162d5

我在StackOverflow上做了一些调查,并在此处找到了一些信息:

如何生成MD5哈希?

给出了以下示例:

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;
}
Run Code Online (Sandbox Code Playgroud)

似乎我可能缺少的唯一部分是"BigInteger"部分,但我不确定.

所以,经过这一切,我想我要问的是,你怎么知道使用"BigInteger"部分?我错误地认为我的newBA对象上的"toString"方法会将其转换为可读输出,但显然我错了.一个人怎么会知道用Java走哪条路?我有一个C背景,所以这个Java的东西看起来很奇怪.关于我怎样才能变得更好的建议,而不必通过谷歌搜索"欺骗"如何一直做某事?

谢谢大家抽空阅读.:-)

Bal*_*usC 6

在这种特殊情况下的关键是你需要意识到字节不是"人类可读的",而是字符.因此,您需要将字节转换为特定格式的字符.对于像哈希这样的任意字节,通常使用十六进制作为"人类可读"格式.然后将每个字节转换为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();
Run Code Online (Sandbox Code Playgroud)

按照@extraneon答案的评论更新,使用new BigInteger(byte[])也是错误的解决方案.这不会取消字节.Java中的字节(如所有原始数字)都是签名的.它们的范围为负.在byte从Java的范围-128127,而你想有一个范围0,以255获得正确的十六进制串.你基本上只需要删除标志,使它们无符号.的& 0xff在上面的例子正是如此.

从中获得的hexstring new BigInteger(bytes).toString(16)与生成世界上所知的MD5生成器的所有其他hexstring的结果不兼容.只要MD5摘要中有负字节,它们就会不同.


ext*_*eon 2

您实际上已经成功地消化了该消息。您只是不知道如何正确呈现找到的摘要值。你拥有的是一个字节数组。这有点难以阅读,并且字节数组的 toString 产生的结果[B@somewhere根本没有用。

BigInteger 作为一种工具将字节数组格式化为单个数字。

你所做的是:

  • 构造一个具有正确值的 BigInteger (在这种情况下,该值恰好以字节数组的形式编码 - 您的摘要
  • 指示 BigInteger 对象返回该数字的字符串表示形式(例如,纯文本、可读文本),基数为 16(例如十六进制)

while 循环在该值前加上 0 个字符作为前缀,以获得 32 的宽度。我可能会使用 String.format 来实现这一点,但无论如何都可以让你的船漂浮:)