淘先锋技术网

首页 1 2 3 4 5 6 7
Java字节[]到十六进制

本文介绍了几种将字节数组或byte[]转换为十六进制 (基数16或十六进制)字符串表示形式的方法。

  1. String.format
  2. Integer.toHexString
  3. Apache Commons编解码器– commons-codec
  4. Spring Security加密– spring-security-crypto
  5. 按位移位和屏蔽。 (教育目的)

注意
Apache Commons-Codec和Spring Security Crypto模块都使用类似的5. Bitwise shifting and masking技术将字节数组转换为十六进制字符串,请研究下面的源代码,这对于教育目的很有用。

1. String.format%02x

String.format是将字节数组转换为十六进制的最简单且显而易见的方法, %02x用于小写十六进制, %02X大写十六进制。

ByteToHexExample1.java
package com.mkyong.crypto.bytes;

import java.nio.charset.StandardCharsets;

public class ByteToHexExample1 {

    public static String hex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte aByte : bytes) {
            result.append(String.format("%02x", aByte));
            // upper case
            // result.append(String.format("%02X", aByte));
        }
        return result.toString();
    }

    public static void main(String[] args) {

        String input = "a";
        System.out.println(hex(input.getBytes(StandardCharsets.UTF_8)));

    }
}

输出量

Terminal
61

2. Integer.toHexString

Integer.toHexString(int i)接受一个int作为参数,并返回一个十六进制字符串。 密钥是将byte转换为int和带有0xff掩码以防止符号扩展

ByteToHexExample2.java
package com.mkyong.crypto.bytes;

import java.nio.charset.StandardCharsets;

public class ByteToHexExample2 {

    public static String hex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte aByte : bytes) {
            int decimal = (int) aByte & 0xff;               // bytes widen to int, need mask, prevent sign extension
                                                            // get last 8 bits
            String hex = Integer.toHexString(decimal);
            if (hex.length() % 2 == 1) {                    // if half hex, pad with zero, e.g \t
                hex = "0" + hex;
            }
            result.append(hex);
        }
        return result.toString();
    }

    public static void main(String[] args) {

        String input = "a";
        System.out.println(hex(input.getBytes(StandardCharsets.UTF_8)));

    }
}

输出量

Terminal
61

3. Apache Commons编解码器

我们可以使用Hex.encodeHexbyte[]转换为十六进制字符串,或使用Hex.decodeHex将十六进制字符串转换为byte[]

ByteToHexExample3
package com.mkyong.crypto.bytes;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import java.nio.charset.StandardCharsets;

public class ByteToHexExample3 {

    public static String hex(byte[] bytes) {
        char[] result = Hex.encodeHex(bytes);
        return new String(result);
    }

    public static String unhex(String hex) throws DecoderException {
        return new String(Hex.decodeHex(hex));
    }

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

        String input = "a";

        String hex = hex(input.getBytes(StandardCharsets.UTF_8));
        System.out.println(hex);    // 61

        String unhex = unhex(hex);
        System.out.println(unhex);  // a

    }
}

Maven。

pom.xml
<dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.14</version>
  </dependency>

4. Spring Security加密

在Spring Security中,我们可以使用Hex.encodebyte[]转换为十六进制字符串。

ByteToHexExample4.java
package com.mkyong.crypto.bytes;

import org.springframework.security.crypto.codec.Hex;

import java.nio.charset.StandardCharsets;

public class ByteToHexExample4 {

    public static void main(String[] args) {

        String input = "a";

        char[] encode = Hex.encode(input.getBytes(StandardCharsets.UTF_8));
        String hex = new String(encode);
        System.out.println(hex);                    // 61

        byte[] decode = Hex.decode(hex);
        System.out.println(new String(decode));     // a

    }
}

Maven。

pom.xml
<dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-crypto</artifactId>
      <version>5.3.2.RELEASE</version>
  </dependency>

5.按位移位和屏蔽。

下面的源代码来自Spring Security Crypto模块, Apache Commons Codes使用类似的技术将字节数组转换为十六进制字符串,但有一些小的变化,例如不同的变量名或长度计算,其核心思想是相同的。

5.1十六进制编码。

Hex.java
package org.springframework.security.crypto.codec;

  //...
  private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'a', 'b', 'c', 'd', 'e', 'f' };

  public static char[] encode(byte[] bytes) {
    final int nBytes = bytes.length;
    char[] result = new char[2 * nBytes];         //  1 hex contains two chars
                                                  //  hex = [0-f][0-f], e.g 0f or ff

    int j = 0;
    for (byte aByte : bytes) {                    // loop byte by byte

                                                  // 0xF0 = FFFF 0000
      result[j++] = HEX[(0xF0 & aByte) >>> 4];    // get the top 4 bits, first half hex char

                                                  // 0x0F = 0000 FFFF
      result[j++] = HEX[(0x0F & aByte)];          // get the bottom 4 bits, second half hex char

                                                  // combine first and second half, we get a complete hex
    }

    return result;
  }

困难的部分是要理解以下两个语句。

HEX[(0xF0 & aByte) >>> 4];

  HEX[(0x0F & aByte)];

5.1.1 HEX[(0xF0 & aByte) >>> 4] (十六进制的前半部分)

例如,一个二进制字符a0110 0001bitwise AND 0xF0 ,它变为0110 0000

0110 0001           # 1 hex = 2 chars [0-f][0-f]
                    # In this case, hex = [0110][0001]

0110 0001           # 0110 = first half hex, 0001 = second half hex
&
FFFF 0000           # 0xF0 = FFFF 0000 , bitwise AND operator.

0110 0000           # result 0110 0000

逻辑右移4位0110 0000 >>> 4 ,它变为0000 0110 。 阅读此Java >>和>>>按位移位运算符

0110 0000 |

???? 0110 | 0000    # >>> 4
0000 0110 | 0000    # >>> 4 (logical right shift, zero extension)

0000 0110           # result, the first half hex

将此二进制数0000 0110转换为十进制,它是6 ,查看变量static final char[] HEX ,索引6的值是6,十六进制的前半部分是6。

5.1.2 HEX[(0x0F & aByte)] (后半十六进制)
相同的字符a二进制为0110 0001bitwise AND 0x0F

0110 0001           # 0110 = first half hex, 0001 = second half hex
&                   # bitwise AND operator.
0000 FFFF           # 0x0F = 0000 FFFF

0000 0001           # result 0000 0001

将此二进制数0000 00001转换为十进制,为1 ,再次查看变量static final char[] HEX ,索引1的值为1,十六进制的后半部分为1。

我们将十六进制的上半部分和下半部分相结合,即6 + 1,即为61 。 对于字符a ,十六进制为61

5.2十六进制解码。

Hex.java
package org.springframework.security.crypto.codec;

  //...

	private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'a', 'b', 'c', 'd', 'e', 'f' };

	public static byte[] decode(CharSequence s) {
		int nChars = s.length();

		if (nChars % 2 != 0) {                                             
			throw new IllegalArgumentException(
					"Hex-encoded string must have an even number of characters");
		}

		byte[] result = new byte[nChars / 2];                                  // 1 hex = 2 char

		for (int i = 0; i < nChars; i += 2) {                                  // step 2, 1 hex = 2 char
			int msb = Character.digit(s.charAt(i), 16);                         // char -> hex, base16
			int lsb = Character.digit(s.charAt(i + 1), 16);

			if (msb < 0 || lsb < 0) {
				throw new IllegalArgumentException(
					"Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
			}
			result[i / 2] = (byte) ((msb << 4) | lsb);
		}
		return result;
	}

}

例如,相同的十六进制61字符a

Character.digit是JDK API,将char转换为16或16进制并返回int

int msb = Character.digit(s.charAt(i), 16);           // msb = 6
int lsb = Character.digit(s.charAt(i + 1), 16);       // lsb = 1

注意
msblsb变量名称有些奇怪,我认为作者指的是前4位和后4位。

在Java int 6 ,二进制值为0000 0110 ; 对于int 1二进制是0000 0001

(byte) ((msb << 4) | lsb);  // in this example, msb = 6, lsb = 1
# (byte) ((msb << 4) | lsb);

     | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0110 |    # msb, 6 is an int, 32-bit
                                                          # (byte) ((msb << 4) | lsb);
                                                          # (byte) ((6 << 4) | 1);
# <<-- 4
0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0110 ???? |    # 6 << 4

0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0110 0000 |    # left shift, ignore sign, zero extension.

     | 0000 0000 | 0000 0000 | 0000 0000 | 0110 0000 |    # final msb

     # bitwise | operator, bitwise inclusive OR

     | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 |    # lsb = 1

     | 0000 0000 | 0000 0000 | 0000 0000 | 0110 0001 |    # msb | lsb = 0110 0001

     | 0110 0001 |                                        # (byte) (msb|lsb) , down cast from int to byte 8-bit

最终的二进制文件是0110 0001

此代码段将二进制字符串转换为字符串,对于二进制0110 0001 ,该字符串为a

int charCode = Integer.parseInt("01100001", 2);
  System.out.println(charCode);                       // 97, look ascii table
  String str = Character.toString((char) charCode);
  System.out.println(str);                            // output = a

谢谢阅读。

参考文献