In a previous post, MD5 in Java, I made reference to a Java class I wrote that converts between hexadecimal strings and the buffer or String that they represent, but I didn't show the code. I've gotten a lot of hits here from searches on variations of "buffer to hex", so I figured I should present the code because it's something people are evidently looking for. It's simple and obvious if you're used to basic bit manipulation in C, but I suppose that stuff can be confusing at first.
The short answer is, here is the complete source code. The long answer is to explain how it works.
One other note: I have read that SHA is preferred to MD5 because it is harder to crack. In the MD5 examples I provided in the previous post mentioned above, all you need to do to use SHA instead of MD5 is to put "SHA" in place of "MD5" in the call to MessageDigest.getInstance().
The API
We define four functions: to convert in each direction, and using either a String or a byte array for the data.
public class HexString ...
public static String stringToHex(String s) ...
public static String bufferToHex(byte buffer[]) ...
public static String bufferToHex(byte buffer[], int startOffset, int length) ...
public static String hexToString(String hexString) throws NumberFormatException ...
public static byte[] hexToBuffer(String hexString) throws NumberFormatException ...
Well, as you can see there are actually five APIs, because you may want to operate on a subset of a buffer, so I have defined two versions of bufferToHex().
The Implementation
Now let's look at how this is implemented. It's simple, but if you are not familiar with C bit manipulation it may be useful to learn.
Most of the code is just setting up to call a couple of core functions discussed below. So these first few functions are nearly trivial.
stringToHex
This function simply gets the string's bytes and calls the API that converts a byte array to hex.
public static String stringToHex(String s)
{
byte[] stringBytes = s.getBytes();
return HexString.bufferToHex(stringBytes);
}
bufferToHex (simple version)
This function simply calls the more flexible version of the API, telling it to use the entire byte array.
public static String bufferToHex(byte buffer[])
{
return HexString.bufferToHex(buffer, 0, buffer.length);
}
bufferToHex (flexible version)
This function simply iterates over the desired range of bytes, and appends each byte's hexadecimal representation to the return string. The conversion from byte to hex is done by appendHexPair(), which we'll get to shortly. It contains the bit manipulation in one direction.
public static String bufferToHex(byte buffer[], int startOffset, int length)
{
StringBuffer hexString = new StringBuffer(2 * length);
int endOffset = startOffset + length;
for (int i = startOffset; i < endOffset; i++)
HexString.appendHexPair(buffer[i], hexString);
return hexString.toString();
}
hexToString
This function simply calls the API that converts a hex string to a byte array, and then returns a String created from that byte array.
public static String hexToString(String hexString) throws NumberFormatException
{
byte[] bytes = HexString.hexToBuffer(hexString);
return new String(bytes);
}
hexToBuffer
Well now finally we get to some real code. The APIs above are really trivial and are most call-throughs to this one or just provide alternate convenient APIs. Here is where we actually have to chew through a string containing hexadecimal digits and build the byte array that the hex digits represent. But it's easy and just requires understanding of C bit manipulation.
First, we create the buffer that we'll return. We know its size: half the number of hex digits, taking into consideration the possibility that we might get an odd-length string, which implies a leading zero.
Then we simply loop over the hex digits, alternating between treating each one as a "high nibble" or a "low nibble". A nibble is 4 bits out of an 8-bit byte. (Half a bite, get it?) The high nibble is the most significant 4 bits of the byte. The low nibble is the least significant 4 bits of the byte. When we process a high nibble, we store it temporarily. When we process a low nibble, we combine it with the high nibble to produce the byte value, and append that to the byte array.
Combining a high nibble and low nibble into a byte is simply a matter of bit manipulation. A high nibble just needs to be bit-shifted left by 4 bits to get its value as part of a byte. Then the low nibble can simply be added (or bit-or'ed, it's the same thing) to the high nibble to get the resulting byte.
To convert a hexadecimal digit into a nibble, we just subtract an appropriate char value from the digit. Values for characters '0' through '9' are obtained by subtracting '0'. Values for characters 'A' through 'F' are obtained by subtracting 'A' and adding 0x0A. Values for characters 'a' through 'f' are obtained by subtracting 'a' and adding 0x0A. Any other characters are invalid, in which case we throw a NumberFormatException.
public static byte[] hexToBuffer(String hexString) throws NumberFormatException
{
int length = hexString.length();
byte[] buffer = new byte[(length + 1) / 2];
boolean evenByte = true;
byte nextByte = 0;
int bufferOffset = 0;
// If given an odd-length input string, there is an implicit
// leading '0' that is not being given to us in the string.
// In that case, act as if we had processed a '0' first.
// It's sufficient to set evenByte to false, and leave nextChar
// as zero which is what it would be if we handled a '0'.
if ((length % 2) == 1)
evenByte = false;
for (int i = 0; i < length; i++)
{
char c = hexString.charAt(i);
int nibble; // A "nibble" is 4 bits: a decimal 0..15
if ((c >= '0') && (c <= '9'))
nibble = c - '0';
else if ((c >= 'A') && (c <= 'F'))
nibble = c - 'A' + 0x0A;
else if ((c >= 'a') && (c <= 'f'))
nibble = c - 'a' + 0x0A;
else
throw new NumberFormatException("Invalid hex digit '" + c + "'.");
if (evenByte)
{
nextByte = (byte) (nibble << 4);
}
else
{
nextByte += (byte) nibble;
buffer[bufferOffset++] = nextByte;
}
evenByte = ! evenByte;
}
return buffer;
}
appendHexPair
This function does the bit manipulation in the reverse direction: generating a pair of hexadecimal digits from a byte. We obtain the high nibble by shifting the byte value right by 4 bits. We obtain the low nibble by masking off the most significant 4 bits. We then grab the appropriate char value from a static array of characters indexed by nibble value. This is a little cleaner than doing char-based addition to accomplish the same thing.
private static void appendHexPair(byte b, StringBuffer hexString)
{
char highNibble = kHexChars[(b & 0xF0) >> 4];
char lowNibble = kHexChars[b & 0x0F];
hexString.append(highNibble);
hexString.append(lowNibble);
}
private static final char kHexChars[] =
{ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
So there you have it.
[98] Google: "HexString.bufferToHex"
[21] Google: "java byte to hex"
[19] Google: "java byte to hex"
[17] http://reinet.co.uk/viewtopic.php?id=70399
[16] http://webferret.search.com/click?wf,java+hexadecimal,,www.b...
[11] Google: "HextoString Java"
[11] Google: "java code for converting hexadecimal to bytes "
[10] Google: "java byte to hex"
[8] Google: "java byte to hex"
[8] Google: "java byte hex"