Main Tutorials

Java – How to convert byte arrays to Hex

Java byte[] to hex

This article shows you a few ways to convert byte arrays or byte[] to a hexadecimal (base 16 or hex) string representative.

  1. String.format
  2. Integer.toHexString
  3. Apache Commons Codec – commons-codec
  4. Spring Security Crypto – spring-security-crypto
  5. Bitwise shifting and masking. (educational purposes)

Note
Both Apache Commons-Codec and Spring Security Crypto modules are using the similar 5. Bitwise shifting and masking techniques to convert byte arrays to a hex string, please study the source code below, it is useful for educational purposes.

1. String.format %02x

This String.format is the easiest and obvious way to convert a byte arrays into a hex, %02x for lower case hex, %02X upper case hex.

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)));

    }
}

Output

Terminal

  61

2. Integer.toHexString

This Integer.toHexString(int i) accepts an int as argument and returns a hex string. The key is convert the byte to an int and mask with a 0xff to prevent sign extension.

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)));

    }
}

Output

Terminal

  61

3. Apache Commons Codec

We can use Hex.encodeHex to convert byte[] to a hex string, or Hex.decodeHex to convert a hex string to 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 Crypto

In Spring Security, we can use Hex.encode to convert byte[] to a hex string.

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. Bitwise shifting and masking.

The below source code is from the Spring Security Crypto module, and the Apache Commons Codes uses similar techniques to convert byte arrays to hex strings, with some minor changes like differing variable name or length calculation, the core ideas are the same.

5.1 Hex encode.

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;
  }

The hard part is to understand the following two statements.


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

  HEX[(0x0F & aByte)];

5.1.1 HEX[(0xF0 & aByte) >>> 4] (first half hex)

For example, a character a, binary is 0110 0001, after bitwise AND a 0xF0, it becomes 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

Logical right shift 4 bits 0110 0000 >>> 4, it becomes 0000 0110. Read this Java >> and >>> bitwise shift operator.


0110 0000 |

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

0000 0110           # result, the first half hex

Convert this binary 0000 0110 to decimal, it is a 6, look at the variable static final char[] HEX, the value of the index 6 is 6, and the first half of the hex is 6.

5.1.2 HEX[(0x0F & aByte)] (second half hex)
The same character a, binary is 0110 0001, bitwise AND a 0x0F.


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

0000 0001           # result 0000 0001

Convert this binary 0000 00001 to decimal, it is 1, look at the variable static final char[] HEX again, the value of the index 1 is 1, and the second half of the hex is 1.

We combine the first half and second half of the hex, which is 6 + 1, it becomes 61. For the character a, the hex is 61.

5.2 Hex decode.

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;
	}

}

For example, the same hex 61, character a.

The Character.digit is the JDK APIs, convert a char to a base 16 or hex and returns an int.


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

Note
The msb and lsb variable names are a bit weird, and I think the author refers to the first 4 bits and last 4 bits.

In Java, int 6, the binary is 0000 0110; for int 1 the binary is 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

The final binary is 0110 0001.

This code snippet converts a binary string to a string, for binary 0110 0001, the string is 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

Thanks for reading.

References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
5 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Max Tardiveau
1 year ago

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

This function is unnecessary, you can just do:
String s = Hex.encodeHexString(bytes);

Joost de Folter
3 years ago

Nice article, however method 2 lacks padding – use this instead (this also removes the need to convert from byte to integer):
String.format(“%02x”, b)

rico
4 years ago

if (hex.length() == 1) sb.append(‘0’);
I don’t understand why need append a ‘0’