encoding/decoding 字节在 Java 中如何工作?

How does encoding/decoding bytes work in Java?

小背景:我正在做 cryptopals 挑战,我完成了 https://cryptopals.com/sets/1/challenges/1 但我意识到我没有学到我认为应该学习(或编码)的东西。

我正在为 Hex 和 Base64 使用 Apache Commons 编解码器库 encoding/decoding。目标是解码十六进制字符串并将其重新编码为 Base64。页面底部的 "hint" 表示 "Always operate on raw bytes, never on encoded strings. Only use hex and base64 for pretty-printing."

这是我的答案...

private static Hex forHex = new Hex();
private static Base64 forBase64 = new Base64();

public static  byte[] hexDecode(String hex) throws DecoderException {
    byte[] rawBytes = forHex.decode(hex.getBytes());
    return rawBytes;
}
public static byte[] encodeB64(byte[] bytes) {
    byte[] base64Bytes = forBase64.encode(bytes);
    return base64Bytes;
}

public static void main(String[] args) throws DecoderException {

String hex = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";


//decode hex String to byte[]
byte[] myHexDecoded = hexDecode(hex);
String myHexDecodedString = new String(myHexDecoded);

//Lyrics from Queen's "Under Pressure"
System.out.println(myHexDecodedString);

//encode myHexDecoded to Base64 encoded byte[]
byte[] myHexEncoded = encodeB64(myHexDecoded);
String myB64String = new String(myHexEncoded);

//"pretty printing" of base64
System.out.println(myB64String);

}

...但我觉得我作弊了。我没有学习如何解码编码为十六进制的字节,也没有学习如何将 "pure" 字节编码为 Base64,我只是学习了如何使用库为我做一些事情。

如果我在 Java 中获取一个字符串然后获取它的字节,我将如何将这些字节编码为十六进制?例如,下面的代码片段将 "Hello"(这是可读的英文)转换为每个字符的字节值:

String s = "Hello";
char[] sChar = s.toCharArray();
byte[] sByte = new byte[sChar.length]
for(int i = 0; i < sChar.length; i++) {
    sByte[i] = (byte) sChar[i];
    System.out.println("sByte[" + i + "] = " +sByte[i]);
}

产生 sByte[0] = 72,sByte[1] = 101,sByte[2] = 108,sByte[3] = 108,sByte[4] = 111

让我们以 'o' 为例 - 我猜它的十进制版本是 111 - 我是否只使用它的十进制版本并将其更改为十六进制版本?

如果是这样,要解码,我是不是每次只取十六进制字符串 2 中的字符,将它们分解为十进制值,然后转换为 ASCII?它会一直是 ASCII 吗?

to decode, do I just take the the characters in the hex String 2 at a time, decompose them to decimal values, then convert to ASCII? Will it always be ASCII?

没有。你一次取字符 2,将字符 '0' 转换为数值 0,将字符 '1' 转换为数值 1,...,字符 'a'(或 'A' , 取决于你要支持的编码) 到数值 10, ..., 字符 'f' 或 'F' 到数值 15.

然后将第一个数值乘以 16,然后将其与第二个数值相加以获得字节的无符号整数值。然后将该无符号整数值转换为有符号字节。

ASCII 与此算法无关。

要看看它在实践中是如何完成的,因为commons-codec是开源的,你可以看看它的实现。