1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.codec.binary; 19 20 import java.nio.ByteBuffer; 21 import java.nio.charset.Charset; 22 23 import org.apache.commons.codec.BinaryDecoder; 24 import org.apache.commons.codec.BinaryEncoder; 25 import org.apache.commons.codec.CharEncoding; 26 import org.apache.commons.codec.Charsets; 27 import org.apache.commons.codec.DecoderException; 28 import org.apache.commons.codec.EncoderException; 29 30 /** 31 * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in 32 * {@link #DEFAULT_CHARSET_NAME} 33 * 34 * This class is thread-safe. 35 * 36 * @since 1.1 37 * @version $Id: Hex.java 1811344 2017-10-06 15:19:57Z ggregory $ 38 */ 39 public class Hex implements BinaryEncoder, BinaryDecoder { 40 41 /** 42 * Default charset is {@link Charsets#UTF_8} 43 * 44 * @since 1.7 45 */ 46 public static final Charset DEFAULT_CHARSET = Charsets.UTF_8; 47 48 /** 49 * Default charset name is {@link CharEncoding#UTF_8} 50 * 51 * @since 1.4 52 */ 53 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 54 55 /** 56 * Used to build output as Hex 57 */ 58 private static final char[] DIGITS_LOWER = 59 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 60 61 /** 62 * Used to build output as Hex 63 */ 64 private static final char[] DIGITS_UPPER = 65 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 66 67 /** 68 * Converts a String representing hexadecimal values into an array of bytes of those same values. The 69 * returned array will be half the length of the passed String, as it takes two characters to represent any given 70 * byte. An exception is thrown if the passed String has an odd number of elements. 71 * 72 * @param data 73 * A String containing hexadecimal digits 74 * @return A byte array containing binary data decoded from the supplied char array. 75 * @throws DecoderException 76 * Thrown if an odd number or illegal of characters is supplied 77 * @since 1.11 78 */ 79 public static byte[] decodeHex(final String data) throws DecoderException { 80 return decodeHex(data.toCharArray()); 81 } 82 83 /** 84 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 85 * returned array will be half the length of the passed array, as it takes two characters to represent any given 86 * byte. An exception is thrown if the passed char array has an odd number of elements. 87 * 88 * @param data 89 * An array of characters containing hexadecimal digits 90 * @return A byte array containing binary data decoded from the supplied char array. 91 * @throws DecoderException 92 * Thrown if an odd number or illegal of characters is supplied 93 */ 94 public static byte[] decodeHex(final char[] data) throws DecoderException { 95 96 final int len = data.length; 97 98 if ((len & 0x01) != 0) { 99 throw new DecoderException("Odd number of characters."); 100 } 101 102 final byte[] out = new byte[len >> 1]; 103 104 // two characters form the hex value. 105 for (int i = 0, j = 0; j < len; i++) { 106 int f = toDigit(data[j], j) << 4; 107 j++; 108 f = f | toDigit(data[j], j); 109 j++; 110 out[i] = (byte) (f & 0xFF); 111 } 112 113 return out; 114 } 115 116 /** 117 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 118 * The returned array will be double the length of the passed array, as it takes two characters to represent any 119 * given byte. 120 * 121 * @param data 122 * a byte[] to convert to Hex characters 123 * @return A char[] containing lower-case hexadecimal characters 124 */ 125 public static char[] encodeHex(final byte[] data) { 126 return encodeHex(data, true); 127 } 128 129 /** 130 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. 131 * The returned array will be double the length of the passed array, as it takes two characters to represent any 132 * given byte. 133 * 134 * @param data 135 * a byte buffer to convert to Hex characters 136 * @return A char[] containing lower-case hexadecimal characters 137 * @since 1.11 138 */ 139 public static char[] encodeHex(final ByteBuffer data) { 140 return encodeHex(data, true); 141 } 142 143 /** 144 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 145 * The returned array will be double the length of the passed array, as it takes two characters to represent any 146 * given byte. 147 * 148 * @param data 149 * a byte[] to convert to Hex characters 150 * @param toLowerCase 151 * <code>true</code> converts to lowercase, <code>false</code> to uppercase 152 * @return A char[] containing hexadecimal characters in the selected case 153 * @since 1.4 154 */ 155 public static char[] encodeHex(final byte[] data, final boolean toLowerCase) { 156 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 157 } 158 159 /** 160 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. 161 * The returned array will be double the length of the passed array, as it takes two characters to represent any 162 * given byte. 163 * 164 * @param data 165 * a byte buffer to convert to Hex characters 166 * @param toLowerCase 167 * <code>true</code> converts to lowercase, <code>false</code> to uppercase 168 * @return A char[] containing hexadecimal characters in the selected case 169 * @since 1.11 170 */ 171 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) { 172 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 173 } 174 175 /** 176 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 177 * The returned array will be double the length of the passed array, as it takes two characters to represent any 178 * given byte. 179 * 180 * @param data 181 * a byte[] to convert to Hex characters 182 * @param toDigits 183 * the output alphabet (must contain at least 16 chars) 184 * @return A char[] containing the appropriate characters from the alphabet 185 * For best results, this should be either upper- or lower-case hex. 186 * @since 1.4 187 */ 188 protected static char[] encodeHex(final byte[] data, final char[] toDigits) { 189 final int l = data.length; 190 final char[] out = new char[l << 1]; 191 // two characters form the hex value. 192 for (int i = 0, j = 0; i < l; i++) { 193 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 194 out[j++] = toDigits[0x0F & data[i]]; 195 } 196 return out; 197 } 198 199 /** 200 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. 201 * The returned array will be double the length of the passed array, as it takes two characters to represent any 202 * given byte. 203 * 204 * @param data 205 * a byte buffer to convert to Hex characters 206 * @param toDigits 207 * the output alphabet (must be at least 16 characters) 208 * @return A char[] containing the appropriate characters from the alphabet 209 * For best results, this should be either upper- or lower-case hex. 210 * @since 1.11 211 */ 212 protected static char[] encodeHex(final ByteBuffer data, final char[] toDigits) { 213 return encodeHex(data.array(), toDigits); 214 } 215 216 /** 217 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 218 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 219 * 220 * @param data 221 * a byte[] to convert to Hex characters 222 * @return A String containing lower-case hexadecimal characters 223 * @since 1.4 224 */ 225 public static String encodeHexString(final byte[] data) { 226 return new String(encodeHex(data)); 227 } 228 229 /** 230 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 231 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 232 * 233 * @param data 234 * a byte[] to convert to Hex characters 235 * @param toLowerCase 236 * <code>true</code> converts to lowercase, <code>false</code> to uppercase 237 * @return A String containing lower-case hexadecimal characters 238 * @since 1.11 239 */ 240 public static String encodeHexString(final byte[] data, final boolean toLowerCase) { 241 return new String(encodeHex(data, toLowerCase)); 242 } 243 244 /** 245 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 246 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 247 * 248 * @param data 249 * a byte buffer to convert to Hex characters 250 * @return A String containing lower-case hexadecimal characters 251 * @since 1.11 252 */ 253 public static String encodeHexString(final ByteBuffer data) { 254 return new String(encodeHex(data)); 255 } 256 257 /** 258 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 259 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 260 * 261 * @param data 262 * a byte buffer to convert to Hex characters 263 * @param toLowerCase 264 * <code>true</code> converts to lowercase, <code>false</code> to uppercase 265 * @return A String containing lower-case hexadecimal characters 266 * @since 1.11 267 */ 268 public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) { 269 return new String(encodeHex(data, toLowerCase)); 270 } 271 272 /** 273 * Converts a hexadecimal character to an integer. 274 * 275 * @param ch 276 * A character to convert to an integer digit 277 * @param index 278 * The index of the character in the source 279 * @return An integer 280 * @throws DecoderException 281 * Thrown if ch is an illegal hex character 282 */ 283 protected static int toDigit(final char ch, final int index) throws DecoderException { 284 final int digit = Character.digit(ch, 16); 285 if (digit == -1) { 286 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); 287 } 288 return digit; 289 } 290 291 private final Charset charset; 292 293 /** 294 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET} 295 */ 296 public Hex() { 297 // use default encoding 298 this.charset = DEFAULT_CHARSET; 299 } 300 301 /** 302 * Creates a new codec with the given Charset. 303 * 304 * @param charset 305 * the charset. 306 * @since 1.7 307 */ 308 public Hex(final Charset charset) { 309 this.charset = charset; 310 } 311 312 /** 313 * Creates a new codec with the given charset name. 314 * 315 * @param charsetName 316 * the charset name. 317 * @throws java.nio.charset.UnsupportedCharsetException 318 * If the named charset is unavailable 319 * @since 1.4 320 * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable 321 */ 322 public Hex(final String charsetName) { 323 this(Charset.forName(charsetName)); 324 } 325 326 /** 327 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. 328 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 329 * byte. An exception is thrown if the passed char array has an odd number of elements. 330 * 331 * @param array 332 * An array of character bytes containing hexadecimal digits 333 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 334 * @throws DecoderException 335 * Thrown if an odd number of characters is supplied to this function 336 * @see #decodeHex(char[]) 337 */ 338 @Override 339 public byte[] decode(final byte[] array) throws DecoderException { 340 return decodeHex(new String(array, getCharset()).toCharArray()); 341 } 342 343 /** 344 * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values. 345 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 346 * byte. An exception is thrown if the passed char array has an odd number of elements. 347 * 348 * @param buffer 349 * An array of character bytes containing hexadecimal digits 350 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 351 * @throws DecoderException 352 * Thrown if an odd number of characters is supplied to this function 353 * @see #decodeHex(char[]) 354 * @since 1.11 355 */ 356 public byte[] decode(final ByteBuffer buffer) throws DecoderException { 357 return decodeHex(new String(buffer.array(), getCharset()).toCharArray()); 358 } 359 360 /** 361 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those 362 * same values. The returned array will be half the length of the passed String or array, as it takes two characters 363 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. 364 * 365 * @param object 366 * A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits 367 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 368 * @throws DecoderException 369 * Thrown if an odd number of characters is supplied to this function or the object is not a String or 370 * char[] 371 * @see #decodeHex(char[]) 372 */ 373 @Override 374 public Object decode(final Object object) throws DecoderException { 375 if (object instanceof String) { 376 return decode(((String) object).toCharArray()); 377 } else if (object instanceof byte[]) { 378 return decode((byte[]) object); 379 } else if (object instanceof ByteBuffer) { 380 return decode((ByteBuffer) object); 381 } else { 382 try { 383 return decodeHex((char[]) object); 384 } catch (final ClassCastException e) { 385 throw new DecoderException(e.getMessage(), e); 386 } 387 } 388 } 389 390 /** 391 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each 392 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 393 * represent any given byte. 394 * <p> 395 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 396 * {@link #getCharset()}. 397 * </p> 398 * 399 * @param array 400 * a byte[] to convert to Hex characters 401 * @return A byte[] containing the bytes of the lower-case hexadecimal characters 402 * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid. 403 * @see #encodeHex(byte[]) 404 */ 405 @Override 406 public byte[] encode(final byte[] array) { 407 return encodeHexString(array).getBytes(this.getCharset()); 408 } 409 410 /** 411 * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each 412 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 413 * represent any given byte. 414 * <p> 415 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 416 * {@link #getCharset()}. 417 * </p> 418 * 419 * @param array 420 * a byte buffer to convert to Hex characters 421 * @return A byte[] containing the bytes of the lower-case hexadecimal characters 422 * @see #encodeHex(byte[]) 423 * @since 1.11 424 */ 425 public byte[] encode(final ByteBuffer array) { 426 return encodeHexString(array).getBytes(this.getCharset()); 427 } 428 429 /** 430 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each 431 * byte in order. The returned array will be double the length of the passed String or array, as it takes two 432 * characters to represent any given byte. 433 * <p> 434 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by 435 * {@link #getCharset()}. 436 * </p> 437 * 438 * @param object 439 * a String, ByteBuffer, or byte[] to convert to Hex characters 440 * @return A char[] containing lower-case hexadecimal characters 441 * @throws EncoderException 442 * Thrown if the given object is not a String or byte[] 443 * @see #encodeHex(byte[]) 444 */ 445 @Override 446 public Object encode(final Object object) throws EncoderException { 447 byte[] byteArray; 448 if (object instanceof String) { 449 byteArray = ((String) object).getBytes(this.getCharset()); 450 } else if (object instanceof ByteBuffer) { 451 byteArray = ((ByteBuffer) object).array(); 452 } else { 453 try { 454 byteArray = (byte[]) object; 455 } catch (final ClassCastException e) { 456 throw new EncoderException(e.getMessage(), e); 457 } 458 } 459 return encodeHex(byteArray); 460 } 461 462 /** 463 * Gets the charset. 464 * 465 * @return the charset. 466 * @since 1.7 467 */ 468 public Charset getCharset() { 469 return this.charset; 470 } 471 472 /** 473 * Gets the charset name. 474 * 475 * @return the charset name. 476 * @since 1.4 477 */ 478 public String getCharsetName() { 479 return this.charset.name(); 480 } 481 482 /** 483 * Returns a string representation of the object, which includes the charset name. 484 * 485 * @return a string representation of the object. 486 */ 487 @Override 488 public String toString() { 489 return super.toString() + "[charsetName=" + this.charset + "]"; 490 } 491 }