/* * HexString.java * * */ package org.smartly.commons.cryptograph; /** * Helper that converts between hexadecimal strings * and the buffer or String that they represent. */ public class HexString { /** * Creates a new instance of HexString */ private HexString() { } /** * Returns a string containing the hexadecimal representation of the * input string. Each byte in the input string is converted to a * two-digit hexadecimal value. Thus the returned string is twice the * length of the input string. The output hex characters are upper * case. The conversion assumes the default charset; making the * charset explicit could be accomplished by just adding a parameter * here and passing it through to getBytes(). * * @return the hex string version of the input string * @param s a string to convert to hex */ public static String stringToHex(String s) { byte[] stringBytes = s.getBytes(); return HexString.bufferToHex(stringBytes); } /** * Returns a string containing the hexadecimal representation of the * input byte array. Each byte in the input array is converted to a * two-digit hexadecimal value. Thus the returned string is twice the * length of the input byte array. The output hex characters are upper case. * * @return the hex string version of the input buffer * @param buffer a buffer to convert to hex */ public static String bufferToHex(byte buffer[]) { return HexString.bufferToHex(buffer, 0, buffer.length); } /** * Returns a string containing the hexadecimal representation of the * input byte array. Each byte in the input array is converted to a * two-digit hexadecimal value. Thus the returned string is twice the * length of the specified amount of the input byte array. The output * hex characters are upper case. * * @return the hex string version of the input buffer * @param buffer a buffer to convert to hex * @param startOffset the offset of the first byte in the buffer to process * @param length the number of bytes in the buffer to process */ 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(); } /** * Returns a string built from the byte values represented by the input * hexadecimal string. That is, each pair of hexadecimal characters in * the input string is decoded into the byte value that they represent, * and that byte value is appended to a string which is ultimately * returned. This function doesn't care whether the hexadecimal characters * are upper or lower case, and it also can handle odd-length hex strings * by assuming a leading zero digit. If any character in the input string is * not a valid hexadecimal digit, it throws a NumberFormatException, in * keeping with the behavior of Java functions like Integer.parseInt(). The * conversion assumes the default charset; making the charset explicit could * be accomplished by just adding a parameter here and passing it through to * the String constructor. * * @return a String built from the bytes indicated by the input string * @throws NumberFormatException * @param hexString a string of hexadecimal characters */ public static String hexToString(String hexString) throws NumberFormatException { byte[] bytes = HexString.hexToBuffer(hexString); return new String(bytes); } /** * Returns a byte array built from the byte values represented by the input * hexadecimal string. That is, each pair of hexadecimal characters in * the input string is decoded into the byte value that they represent, * and that byte value is appended to a byte array which is ultimately * returned. This function doesn't care whether the hexadecimal characters * are upper or lower case, and it also can handle odd-length hex strings * by assuming a leading zero digit. If any character in the input string is not * a valid hexadecimal digit, it throws a NumberFormatException, in keeping * with the behavior of Java functions like Integer.parseInt(). * * @return a byte array built from the bytes indicated by the input string * @throws NumberFormatException * @param hexString a string of hexadecimal characters */ 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; } // ------------------------------------------------------------------------ // p r i v a t e // ------------------------------------------------------------------------ /** * Appends a hexadecimal representation of a particular char value * to a string buffer. That is, two hexadecimal digits are appended * to the string. * * @param b a byte whose hex representation is to be obtained * @param hexString the string to append the hex digits to */ 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'}; }