Converting Between Byte Arrays and Hexadecimal Strings in Java

1. Overview

In the tutorial, we'll take a look at different ways to convert a byte array to a hexadecimal string, and vice versa.

The record is introduced because the result of encryption and decryption will be a byte array, but my data must be passed through the HTTP protocol.

2. Converting Between Byte and Hexadecimal

First of all, let's take a look at conversion logic between byte and hexadecimal numbers.

2.1 Byte to Hexadecimal

The bytes are 8 bit signed integers in Java. Therefore, we need to convert each 4-bit segment to hex separately and concatenate them. Consequently, we'll get two hexadecimal characters after conversion.

For instance, we can write 45 as 0010 1101 in binary, and the hexadecimal equivalent will be "2d":
0010 = 2 (base 10) = 2 (base 16)
1101 = 13 (base 10) = d (base 16)
 
Therefore: 45 = 0010 1101 = 0x2d
Let's implement the simple logic in Java:
public String byteToHex(byte num) {
    char[] hexDigits = new char[2];
    hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
    hexDigits[1] = Character.forDigit((num & 0xF), 16);
    return new String(hexDigits);
}
Now, let's understand the above code by analyzing each operation. First of all, we created a char array of length 2 to store the output:
char[] hexDigits = new char[2];
Next, we isolated higher order bits by right shifting 4 bits. And then, we applied a mask to isolate lower order 4 bits. Masking is required because negative numbers are internally represented as two's complement of the positive number:
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
Then, we convert the remaining 4 bits to hexadecimal:
hexDigits[1] = Character.forDigit((num & 0xF), 16);
Finally, we create a String object from char array. And then, returned this object as converted hexadecimal array.
Now, let us understand how his will work for a negative byte -4:
hexDigits[0]:
1111 1100 >> 4 = 1111 1111 1111 1111 1111 1111 1111 1111
1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111 = 0xf
 
hexDigits[1]:
1111 1100 & 0xF = 0000 1100 = 0xc
 
Therefore: -4 (base 10) = 1111 1100 (base 2) = fc (base 16)
It's also worth noting that the Character.forDigit() method always returns lowercase characters.

2.2. Hexadecimal to Byte

Now, let's convert a hexadecimal digit to byte. As we know, a byte contains 8 bits. Therefore, we need two hexadecimal digits to create one byte.

First of all, we'll convert each hexadecimal digit into binary equivalent separately.
And then, we need to concatenate the two four bit-segments to get the byte equivalent:
Hexadecimal: 2d
2 = 0010 (base 2)
d = 1101 (base 2)
 
Therefore: 2d = 0010 1101 (base 2) = 45
Now, let's write the operation in Java:
public byte hexToByte(String hexString) {
    int firstDigit = toDigit(hexString.charAt(0));
    int secondDigit = toDigit(hexString.charAt(1));
    return (byte) ((firstDigit << 4) + secondDigit);
}
 
private int toDigit(char hexChar) {
    int digit = Character.digit(hexChar, 16);
    if(digit == -1) {
        throw new IllegalArgumentException(
          "Invalid Hexadecimal Character: "+ hexChar);
    }
    return digit;
}
Let's understand this, one operation at a time.
First of all, we converted hexadecimal characters into integers:
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
Then we left shifted most significant digit by 4 bits. Consequently, the binary representation has zeros at four least significant bits.
Then, we added the least significant digit to it:
return (byte) ((firstDigit << 4) + secondDigit);
Now, let's examine the toDigit() method closely. We are using the Character.digit() method for conversion. If the character value passed to this method is not a valid digit in the specified radix, -1 is returned.
We're validating the return value and throwing an exception if an invalid value was passed.

3. Converting Between Byte Arrays and Hexadecimal Strings

At this point, we know how to convert a byte to the hexadecimal, and vice versa. Let's scale this algorithm and convert byte array to/from hexadecimal String.

3.1. Byte Array to Hexadecimal String

We need to loop through the array and generate hexadecimal pair for each byte:
public String encodeHexString(byte[] byteArray) {
    StringBuffer hexStringBuffer = new StringBuffer();
    for (int i = 0; i < byteArray.length; i++) {
        hexStringBuffer.append(byteToHex(byteArray[i]));
    }
    return hexStringBuffer.toString();
}
As we already know, the output will always be in lowercase.

3.2. Hexadecimal String to Byte Array

First of all, we need to check if the length of the hexadecimal String is an even number. This is because a hexadecimal String with odd length will result in incorrect byte representation.

Now, we'll iterate through the array and convert each hexadecimal pair to a byte:
public byte[] decodeHexString(String hexString) {
    if (hexString.length() % 2 == 1) {
        throw new IllegalArgumentException(
          "Invalid hexadecimal String supplied.");
    }
     
    byte[] bytes = new byte[hexString.length() / 2];
    for (int i = 0; i < hexString.length(); i += 2) {
        bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
    }
    return bytes;
}



留言

這個網誌中的熱門文章

Visual Sudio2019創建MFC ActiveX工程製作IE OCX插件

Little Endian VS Big Endian