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 import java.nio.charset.StandardCharsets; 23 24 import org.apache.commons.codec.BinaryDecoder; 25 import org.apache.commons.codec.BinaryEncoder; 26 import org.apache.commons.codec.CharEncoding; 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 */ 38 public class Hex implements BinaryEncoder, BinaryDecoder { 39 40 /** 41 * Default charset is {@link StandardCharsets#UTF_8}. 42 * 43 * @since 1.7 44 */ 45 public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; 46 47 /** 48 * Default charset name is {@link CharEncoding#UTF_8}. 49 * 50 * @since 1.4 51 */ 52 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 53 54 /** 55 * Used to build output as hex. 56 */ 57 private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 58 'e', 'f' }; 59 60 /** 61 * Used to build output as hex. 62 */ 63 private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 64 'E', 'F' }; 65 66 private final Charset charset; 67 68 /** 69 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 70 * returned array will be half the length of the passed array, as it takes two characters to represent any given 71 * byte. An exception is thrown if the passed char array has an odd number of elements. 72 * 73 * @param data An array of characters containing hexadecimal digits 74 * @return A byte array containing binary data decoded from the supplied char array. 75 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied 76 */ 77 public static byte[] decodeHex(final char[] data) throws DecoderException { 78 final byte[] out = new byte[data.length >> 1]; 79 decodeHex(data, out, 0); 80 return out; 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 An array of characters containing hexadecimal digits 89 * @param out A byte array to contain the binary data decoded from the supplied char array. 90 * @param outOffset The position within {@code out} to start writing the decoded bytes. 91 * @return the number of bytes written to {@code out}. 92 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied 93 * @since 1.15 94 */ 95 public static int decodeHex(final char[] data, final byte[] out, final int outOffset) throws DecoderException { 96 final int len = data.length; 97 98 if ((len & 0x01) != 0) { 99 throw new DecoderException("Odd number of characters."); 100 } 101 102 final int outLen = len >> 1; 103 if (out.length - outOffset < outLen) { 104 throw new DecoderException("Output array is not large enough to accommodate decoded data."); 105 } 106 107 // two characters form the hex value. 108 for (int i = outOffset, j = 0; j < len; i++) { 109 int f = toDigit(data[j], j) << 4; 110 j++; 111 f = f | toDigit(data[j], j); 112 j++; 113 out[i] = (byte) (f & 0xFF); 114 } 115 116 return outLen; 117 } 118 119 /** 120 * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array 121 * will be half the length of the passed String, as it takes two characters to represent any given byte. An 122 * exception is thrown if the passed String has an odd number of elements. 123 * 124 * @param data A String containing hexadecimal digits 125 * @return A byte array containing binary data decoded from the supplied char array. 126 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied 127 * @since 1.11 128 */ 129 public static byte[] decodeHex(final String data) throws DecoderException { 130 return decodeHex(data.toCharArray()); 131 } 132 133 /** 134 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 135 * The returned array will be double the length of the passed array, as it takes two characters to represent any 136 * given byte. 137 * 138 * @param data a byte[] to convert to hex characters 139 * @return A char[] containing lower-case hexadecimal characters 140 */ 141 public static char[] encodeHex(final byte[] data) { 142 return encodeHex(data, true); 143 } 144 145 /** 146 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 147 * The returned array will be double the length of the passed array, as it takes two characters to represent any 148 * given byte. 149 * 150 * @param data a byte[] to convert to Hex characters 151 * @param toLowerCase {@code true} converts to lowercase, {@code false} 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 an array of bytes 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 a byte[] to convert to hex characters 165 * @param toDigits the output alphabet (must contain at least 16 chars) 166 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 167 * upper- or lower-case hex. 168 * @since 1.4 169 */ 170 protected static char[] encodeHex(final byte[] data, final char[] toDigits) { 171 final int dataLength = data.length; 172 final char[] out = new char[dataLength << 1]; 173 encodeHex(data, 0, dataLength, toDigits, out, 0); 174 return out; 175 } 176 177 /** 178 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 179 * 180 * @param data a byte[] to convert to hex characters 181 * @param dataOffset the position in {@code data} to start encoding from 182 * @param dataLen the number of bytes from {@code dataOffset} to encode 183 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 184 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 185 * upper- or lower-case hex. 186 * @since 1.15 187 */ 188 public static char[] encodeHex(final byte[] data, final int dataOffset, final int dataLen, 189 final boolean toLowerCase) { 190 final char[] out = new char[dataLen << 1]; 191 encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, 0); 192 return out; 193 } 194 195 /** 196 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 197 * 198 * @param data a byte[] to convert to hex characters 199 * @param dataOffset the position in {@code data} to start encoding from 200 * @param dataLen the number of bytes from {@code dataOffset} to encode 201 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 202 * @param out a char[] which will hold the resultant appropriate characters from the alphabet. 203 * @param outOffset the position within {@code out} at which to start writing the encoded characters. 204 * @since 1.15 205 */ 206 public static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, 207 final boolean toLowerCase, final char[] out, final int outOffset) { 208 encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, outOffset); 209 } 210 211 /** 212 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 213 * 214 * @param data a byte[] to convert to hex characters 215 * @param dataOffset the position in {@code data} to start encoding from 216 * @param dataLen the number of bytes from {@code dataOffset} to encode 217 * @param toDigits the output alphabet (must contain at least 16 chars) 218 * @param out a char[] which will hold the resultant appropriate characters from the alphabet. 219 * @param outOffset the position within {@code out} at which to start writing the encoded characters. 220 */ 221 private static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, final char[] toDigits, 222 final char[] out, final int outOffset) { 223 // two characters form the hex value. 224 for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) { 225 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 226 out[j++] = toDigits[0x0F & data[i]]; 227 } 228 } 229 230 /** 231 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 232 * returned array will be double the length of the passed array, as it takes two characters to represent any given 233 * byte. 234 * 235 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 236 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 237 * 238 * @param data a byte buffer to convert to hex characters 239 * @return A char[] containing lower-case hexadecimal characters 240 * @since 1.11 241 */ 242 public static char[] encodeHex(final ByteBuffer data) { 243 return encodeHex(data, true); 244 } 245 246 /** 247 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 248 * returned array will be double the length of the passed array, as it takes two characters to represent any given 249 * byte. 250 * 251 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 252 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 253 * 254 * @param data a byte buffer to convert to hex characters 255 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 256 * @return A char[] containing hexadecimal characters in the selected case 257 * @since 1.11 258 */ 259 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) { 260 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 261 } 262 263 /** 264 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 265 * returned array will be double the length of the passed array, as it takes two characters to represent any given 266 * byte. 267 * 268 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 269 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 270 * 271 * @param byteBuffer a byte buffer to convert to hex characters 272 * @param toDigits the output alphabet (must be at least 16 characters) 273 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 274 * upper- or lower-case hex. 275 * @since 1.11 276 */ 277 protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) { 278 return encodeHex(toByteArray(byteBuffer), toDigits); 279 } 280 281 /** 282 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 283 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 284 * 285 * @param data a byte[] to convert to hex characters 286 * @return A String containing lower-case hexadecimal characters 287 * @since 1.4 288 */ 289 public static String encodeHexString(final byte[] data) { 290 return new String(encodeHex(data)); 291 } 292 293 /** 294 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 295 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 296 * 297 * @param data a byte[] to convert to hex characters 298 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 299 * @return A String containing lower-case hexadecimal characters 300 * @since 1.11 301 */ 302 public static String encodeHexString(final byte[] data, final boolean toLowerCase) { 303 return new String(encodeHex(data, toLowerCase)); 304 } 305 306 /** 307 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 308 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 309 * 310 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 311 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 312 * 313 * @param data a byte buffer to convert to hex characters 314 * @return A String containing lower-case hexadecimal characters 315 * @since 1.11 316 */ 317 public static String encodeHexString(final ByteBuffer data) { 318 return new String(encodeHex(data)); 319 } 320 321 /** 322 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 323 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 324 * 325 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 326 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 327 * 328 * @param data a byte buffer to convert to hex characters 329 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 330 * @return A String containing lower-case hexadecimal characters 331 * @since 1.11 332 */ 333 public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) { 334 return new String(encodeHex(data, toLowerCase)); 335 } 336 337 /** 338 * Convert the byte buffer to a byte array. All bytes identified by 339 * {@link ByteBuffer#remaining()} will be used. 340 * 341 * @param byteBuffer the byte buffer 342 * @return the byte[] 343 */ 344 private static byte[] toByteArray(final ByteBuffer byteBuffer) { 345 final int remaining = byteBuffer.remaining(); 346 // Use the underlying buffer if possible 347 if (byteBuffer.hasArray()) { 348 final byte[] byteArray = byteBuffer.array(); 349 if (remaining == byteArray.length) { 350 byteBuffer.position(remaining); 351 return byteArray; 352 } 353 } 354 // Copy the bytes 355 final byte[] byteArray = new byte[remaining]; 356 byteBuffer.get(byteArray); 357 return byteArray; 358 } 359 360 /** 361 * Converts a hexadecimal character to an integer. 362 * 363 * @param ch A character to convert to an integer digit 364 * @param index The index of the character in the source 365 * @return An integer 366 * @throws DecoderException Thrown if ch is an illegal hex character 367 */ 368 protected static int toDigit(final char ch, final int index) throws DecoderException { 369 final int digit = Character.digit(ch, 16); 370 if (digit == -1) { 371 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); 372 } 373 return digit; 374 } 375 376 /** 377 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET} 378 */ 379 public Hex() { 380 // use default encoding 381 this.charset = DEFAULT_CHARSET; 382 } 383 384 /** 385 * Creates a new codec with the given Charset. 386 * 387 * @param charset the charset. 388 * @since 1.7 389 */ 390 public Hex(final Charset charset) { 391 this.charset = charset; 392 } 393 394 /** 395 * Creates a new codec with the given charset name. 396 * 397 * @param charsetName the charset name. 398 * @throws java.nio.charset.UnsupportedCharsetException If the named charset is unavailable 399 * @since 1.4 400 * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable 401 */ 402 public Hex(final String charsetName) { 403 this(Charset.forName(charsetName)); 404 } 405 406 /** 407 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. 408 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 409 * byte. An exception is thrown if the passed char array has an odd number of elements. 410 * 411 * @param array An array of character bytes containing hexadecimal digits 412 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 413 * @throws DecoderException Thrown if an odd number of characters is supplied to this function 414 * @see #decodeHex(char[]) 415 */ 416 @Override 417 public byte[] decode(final byte[] array) throws DecoderException { 418 return decodeHex(new String(array, getCharset()).toCharArray()); 419 } 420 421 /** 422 * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values. 423 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 424 * byte. An exception is thrown if the passed char array has an odd number of elements. 425 * 426 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 427 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 428 * 429 * @param buffer An array of character bytes containing hexadecimal digits 430 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 431 * @throws DecoderException Thrown if an odd number of characters is supplied to this function 432 * @see #decodeHex(char[]) 433 * @since 1.11 434 */ 435 public byte[] decode(final ByteBuffer buffer) throws DecoderException { 436 return decodeHex(new String(toByteArray(buffer), getCharset()).toCharArray()); 437 } 438 439 /** 440 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those 441 * same values. The returned array will be half the length of the passed String or array, as it takes two characters 442 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. 443 * 444 * @param object A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits 445 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 446 * @throws DecoderException Thrown if an odd number of characters is supplied to this function or the object is not 447 * a String or char[] 448 * @see #decodeHex(char[]) 449 */ 450 @Override 451 public Object decode(final Object object) throws DecoderException { 452 if (object instanceof String) { 453 return decode(((String) object).toCharArray()); 454 } 455 if (object instanceof byte[]) { 456 return decode((byte[]) object); 457 } 458 if (object instanceof ByteBuffer) { 459 return decode((ByteBuffer) object); 460 } 461 try { 462 return decodeHex((char[]) object); 463 } catch (final ClassCastException e) { 464 throw new DecoderException(e.getMessage(), e); 465 } 466 } 467 468 /** 469 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each 470 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 471 * represent any given byte. 472 * <p> 473 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 474 * {@link #getCharset()}. 475 * </p> 476 * 477 * @param array a byte[] to convert to hex characters 478 * @return A byte[] containing the bytes of the lower-case hexadecimal characters 479 * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid. 480 * @see #encodeHex(byte[]) 481 */ 482 @Override 483 public byte[] encode(final byte[] array) { 484 return encodeHexString(array).getBytes(this.getCharset()); 485 } 486 487 /** 488 * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each byte 489 * in order. The returned array will be double the length of the passed array, as it takes two characters to 490 * represent any given byte. 491 * 492 * <p>The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 493 * {@link #getCharset()}.</p> 494 * 495 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 496 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 497 * 498 * @param array a byte buffer to convert to hex characters 499 * @return A byte[] containing the bytes of the lower-case hexadecimal characters 500 * @see #encodeHex(byte[]) 501 * @since 1.11 502 */ 503 public byte[] encode(final ByteBuffer array) { 504 return encodeHexString(array).getBytes(this.getCharset()); 505 } 506 507 /** 508 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each 509 * byte in order. The returned array will be double the length of the passed String or array, as it takes two 510 * characters to represent any given byte. 511 * <p> 512 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by 513 * {@link #getCharset()}. 514 * </p> 515 * 516 * @param object a String, ByteBuffer, or byte[] to convert to hex characters 517 * @return A char[] containing lower-case hexadecimal characters 518 * @throws EncoderException Thrown if the given object is not a String or byte[] 519 * @see #encodeHex(byte[]) 520 */ 521 @Override 522 public Object encode(final Object object) throws EncoderException { 523 final byte[] byteArray; 524 if (object instanceof String) { 525 byteArray = ((String) object).getBytes(this.getCharset()); 526 } else if (object instanceof ByteBuffer) { 527 byteArray = toByteArray((ByteBuffer) object); 528 } else { 529 try { 530 byteArray = (byte[]) object; 531 } catch (final ClassCastException e) { 532 throw new EncoderException(e.getMessage(), e); 533 } 534 } 535 return encodeHex(byteArray); 536 } 537 538 /** 539 * Gets the charset. 540 * 541 * @return the charset. 542 * @since 1.7 543 */ 544 public Charset getCharset() { 545 return this.charset; 546 } 547 548 /** 549 * Gets the charset name. 550 * 551 * @return the charset name. 552 * @since 1.4 553 */ 554 public String getCharsetName() { 555 return this.charset.name(); 556 } 557 558 /** 559 * Returns a string representation of the object, which includes the charset name. 560 * 561 * @return a string representation of the object. 562 */ 563 @Override 564 public String toString() { 565 return super.toString() + "[charsetName=" + this.charset + "]"; 566 } 567 }