001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.codec.binary; 019 020import java.nio.ByteBuffer; 021import java.nio.charset.Charset; 022import java.nio.charset.StandardCharsets; 023 024import org.apache.commons.codec.BinaryDecoder; 025import org.apache.commons.codec.BinaryEncoder; 026import org.apache.commons.codec.CharEncoding; 027import org.apache.commons.codec.DecoderException; 028import org.apache.commons.codec.EncoderException; 029 030/** 031 * Converts hexadecimal Strings. The Charset used for certain operation can be set, the default is set in 032 * {@link #DEFAULT_CHARSET_NAME} 033 * 034 * <p> 035 * Decoding accepts only the ASCII hexadecimal characters {@code 0-9}, {@code A-F}, and {@code a-f}. Non-ASCII Unicode digits and fullwidth letters 036 * are rejected. 037 * </p> 038 * 039 * <p> 040 * This class is thread-safe. 041 * </p> 042 * 043 * @since 1.1 044 */ 045public class Hex implements BinaryEncoder, BinaryDecoder { 046 047 /** 048 * Default charset is {@link StandardCharsets#UTF_8}. 049 * 050 * @since 1.7 051 */ 052 public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; 053 054 /** 055 * Default charset name is {@link CharEncoding#UTF_8}. 056 * 057 * @since 1.4 058 */ 059 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 060 061 /** 062 * Used to build output as hex. 063 */ 064 private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 065 066 /** 067 * Used to build output as hex. 068 */ 069 private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 070 071 /** 072 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 073 * returned array will be half the length of the passed array, as it takes two characters to represent any given 074 * byte. An exception is thrown if the passed char array has an odd number of elements. 075 * 076 * @param data An array of characters containing hexadecimal digits. 077 * @return A byte array containing binary data decoded from the supplied char array. 078 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied. 079 */ 080 public static byte[] decodeHex(final char[] data) throws DecoderException { 081 final byte[] out = new byte[data.length >> 1]; 082 decodeHex(data, out, 0); 083 return out; 084 } 085 086 /** 087 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 088 * returned array will be half the length of the passed array, as it takes two characters to represent any given 089 * byte. An exception is thrown if the passed char array has an odd number of elements. 090 * 091 * @param data An array of characters containing hexadecimal digits. 092 * @param out A byte array to contain the binary data decoded from the supplied char array. 093 * @param outOffset The position within {@code out} to start writing the decoded bytes. 094 * @return The number of bytes written to {@code out}. 095 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied. 096 * @since 1.15 097 */ 098 public static int decodeHex(final char[] data, final byte[] out, final int outOffset) throws DecoderException { 099 final int len = data.length; 100 if ((len & 1) != 0) { 101 throw new DecoderException("Odd number of characters %,d.", len); 102 } 103 final int outLen = len >> 1; 104 if (out.length - outOffset < outLen) { 105 throw new DecoderException("Output array is not large enough to accommodate decoded data."); 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 |= toDigit(data[j], j); 112 j++; 113 out[i] = (byte) (f & 0xFF); 114 } 115 return outLen; 116 } 117 118 /** 119 * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array 120 * will be half the length of the passed String, as it takes two characters to represent any given byte. An 121 * exception is thrown if the passed String has an odd number of elements. 122 * 123 * @param data A String containing hexadecimal digits. 124 * @return A byte array containing binary data decoded from the supplied char array. 125 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied. 126 * @since 1.11 127 */ 128 public static byte[] decodeHex(final String data) throws DecoderException { 129 return decodeHex(data.toCharArray()); 130 } 131 132 /** 133 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 134 * The returned array will be double the length of the passed array, as it takes two characters to represent any 135 * given byte. 136 * 137 * @param data A byte[] to convert to hexadecimal characters. 138 * @return A char[] containing lower-case hexadecimal characters. 139 */ 140 public static char[] encodeHex(final byte[] data) { 141 return encodeHex(data, true); 142 } 143 144 /** 145 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 146 * The returned array will be double the length of the passed array, as it takes two characters to represent any 147 * given byte. 148 * 149 * @param data A byte[] to convert to Hex characters. 150 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase. 151 * @return A char[] containing hexadecimal characters in the selected case. 152 * @since 1.4 153 */ 154 public static char[] encodeHex(final byte[] data, final boolean toLowerCase) { 155 return encodeHex(data, toAlphabet(toLowerCase)); 156 } 157 158 /** 159 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 160 * The returned array will be double the length of the passed array, as it takes two characters to represent any 161 * given byte. 162 * 163 * @param data A byte[] to convert to hexadecimal characters. 164 * @param toDigits The output alphabet (must contain at least 16 chars). 165 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 166 * upper- or lower-case hex. 167 * @since 1.4 168 */ 169 protected static char[] encodeHex(final byte[] data, final char[] toDigits) { 170 final int dataLength = data.length; 171 return encodeHex(data, 0, dataLength, toDigits, new char[dataLength << 1], 0); 172 } 173 174 /** 175 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 176 * 177 * @param data A byte[] to convert to hexadecimal characters. 178 * @param dataOffset The position in {@code data} to start encoding from. 179 * @param dataLen The number of bytes from {@code dataOffset} to encode. 180 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase. 181 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 182 * upper- or lower-case hex. 183 * @since 1.15 184 */ 185 public static char[] encodeHex(final byte[] data, final int dataOffset, final int dataLen, final boolean toLowerCase) { 186 return encodeHex(data, dataOffset, dataLen, toAlphabet(toLowerCase), new char[dataLen << 1], 0); 187 } 188 189 /** 190 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 191 * 192 * @param data A byte[] to convert to hexadecimal characters. 193 * @param dataOffset The position in {@code data} to start encoding from. 194 * @param dataLen The number of bytes from {@code dataOffset} to encode. 195 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase. 196 * @param out A char[] which will hold the resultant appropriate characters from the alphabet. 197 * @param outOffset The position within {@code out} at which to start writing the encoded characters. 198 * @since 1.15 199 */ 200 public static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, final boolean toLowerCase, final char[] out, final int outOffset) { 201 encodeHex(data, dataOffset, dataLen, toAlphabet(toLowerCase), out, outOffset); 202 } 203 204 /** 205 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 206 * 207 * @param data A byte[] to convert to hexadecimal characters. 208 * @param dataOffset The position in {@code data} to start encoding from. 209 * @param dataLen The number of bytes from {@code dataOffset} to encode. 210 * @param toDigits The output alphabet (must contain at least 16 chars). 211 * @param out A char[] which will hold the resultant appropriate characters from the alphabet. 212 * @param outOffset The position within {@code out} at which to start writing the encoded characters. 213 * @return The given {@code out}. 214 */ 215 private static char[] encodeHex(final byte[] data, final int dataOffset, final int dataLen, final char[] toDigits, final char[] out, final int outOffset) { 216 // two characters form the hex value. 217 for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) { 218 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 219 out[j++] = toDigits[0x0F & data[i]]; 220 } 221 return out; 222 } 223 224 /** 225 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 226 * returned array will be double the length of the passed array, as it takes two characters to represent any given 227 * byte. 228 * 229 * <p> 230 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 231 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 232 * </p> 233 * 234 * @param data A byte buffer to convert to hexadecimal characters. 235 * @return A char[] containing lower-case hexadecimal characters. 236 * @since 1.11 237 */ 238 public static char[] encodeHex(final ByteBuffer data) { 239 return encodeHex(data, true); 240 } 241 242 /** 243 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 244 * returned array will be double the length of the passed array, as it takes two characters to represent any given 245 * byte. 246 * 247 * <p> 248 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 249 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 250 * </p> 251 * 252 * @param data A byte buffer to convert to hexadecimal characters. 253 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase. 254 * @return A char[] containing hexadecimal characters in the selected case. 255 * @since 1.11 256 */ 257 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) { 258 return encodeHex(data, toAlphabet(toLowerCase)); 259 } 260 261 /** 262 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 263 * returned array will be double the length of the passed array, as it takes two characters to represent any given 264 * byte. 265 * 266 * <p> 267 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 268 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 269 * </p> 270 * 271 * @param byteBuffer A byte buffer to convert to hexadecimal 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 hexadecimal 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 hexadecimal 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> 311 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 312 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 313 * </p> 314 * 315 * @param data A byte buffer to convert to hexadecimal characters. 316 * @return A String containing lower-case hexadecimal characters. 317 * @since 1.11 318 */ 319 public static String encodeHexString(final ByteBuffer data) { 320 return new String(encodeHex(data)); 321 } 322 323 /** 324 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 325 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 326 * 327 * <p> 328 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 329 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 330 * </p> 331 * 332 * @param data A byte buffer to convert to hexadecimal characters. 333 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase. 334 * @return A String containing lower-case hexadecimal characters. 335 * @since 1.11 336 */ 337 public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) { 338 return new String(encodeHex(data, toLowerCase)); 339 } 340 341 /** 342 * Converts a boolean to an alphabet. 343 * 344 * @param toLowerCase true for lowercase, false for uppercase. 345 * @return An alphabet. 346 */ 347 private static char[] toAlphabet(final boolean toLowerCase) { 348 return toLowerCase ? DIGITS_LOWER : DIGITS_UPPER; 349 } 350 351 /** 352 * Convert the byte buffer to a byte array. All bytes identified by 353 * {@link ByteBuffer#remaining()} will be used. 354 * 355 * @param byteBuffer The byte buffer. 356 * @return The byte[]. 357 */ 358 private static byte[] toByteArray(final ByteBuffer byteBuffer) { 359 final int remaining = byteBuffer.remaining(); 360 // Use the underlying buffer if possible 361 if (byteBuffer.hasArray()) { 362 final byte[] byteArray = byteBuffer.array(); 363 if (remaining == byteArray.length) { 364 byteBuffer.position(remaining); 365 return byteArray; 366 } 367 } 368 // Copy the bytes 369 final byte[] byteArray = new byte[remaining]; 370 byteBuffer.get(byteArray); 371 return byteArray; 372 } 373 374 /** 375 * Converts a hexadecimal character to an integer. 376 * 377 * <p> 378 * Only the ASCII characters {@code '0'} to {@code '9'}, {@code 'A'} to {@code 'F'} and {@code 'a'} to {@code 'f'} are accepted. Other Unicode digits, 379 * such as fullwidth or Arabic-Indic digits, are rejected even though {@link Character#digit(char, int)} would accept them. These alternate spellings 380 * can bypass textual blocklists or replay caches that compare hexadecimal strings without decoding or normalizing them first. 381 * </p> 382 * 383 * @param ch A character to convert to an integer digit. 384 * @param index The index of the character in the source. 385 * @return An integer. 386 * @throws DecoderException Thrown if ch is an illegal hexadecimal character. 387 */ 388 protected static int toDigit(final char ch, final int index) throws DecoderException { 389 final int digit; 390 if (ch >= '0' && ch <= '9') { 391 digit = ch - '0'; 392 } else if (ch >= 'A' && ch <= 'F') { 393 digit = ch - 'A' + 10; 394 } else if (ch >= 'a' && ch <= 'f') { 395 digit = ch - 'a' + 10; 396 } else { 397 digit = -1; 398 } 399 if (digit == -1) { 400 throw new DecoderException("Illegal hexadecimal character 0x%02X at index %,d.", ch & 0xFFFF, index); 401 } 402 return digit; 403 } 404 405 private final Charset charset; 406 407 /** 408 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET} 409 */ 410 public Hex() { 411 // use default encoding 412 this.charset = DEFAULT_CHARSET; 413 } 414 415 /** 416 * Creates a new codec with the given Charset. 417 * 418 * @param charset The charset. 419 * @since 1.7 420 */ 421 public Hex(final Charset charset) { 422 this.charset = charset; 423 } 424 425 /** 426 * Creates a new codec with the given charset name. 427 * 428 * @param charsetName The charset name. 429 * @throws java.nio.charset.UnsupportedCharsetException Thrown if the named charset is unavailable. 430 * @since 1.4 431 * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable 432 */ 433 public Hex(final String charsetName) { 434 this(Charset.forName(charsetName)); 435 } 436 437 /** 438 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. 439 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 440 * byte. An exception is thrown if the passed char array has an odd number of elements. 441 * 442 * @param array An array of character bytes containing hexadecimal digits. 443 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 444 * @throws DecoderException Thrown if an odd number of characters is supplied to this function. 445 * @see #decodeHex(char[]) 446 */ 447 @Override 448 public byte[] decode(final byte[] array) throws DecoderException { 449 return decodeHex(new String(array, getCharset()).toCharArray()); 450 } 451 452 /** 453 * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values. 454 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 455 * byte. An exception is thrown if the passed char array has an odd number of elements. 456 * 457 * <p> 458 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 459 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 460 * </p> 461 * 462 * @param buffer An array of character bytes containing hexadecimal digits. 463 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 464 * @throws DecoderException Thrown if an odd number of characters is supplied to this function. 465 * @see #decodeHex(char[]) 466 * @since 1.11 467 */ 468 public byte[] decode(final ByteBuffer buffer) throws DecoderException { 469 return decodeHex(new String(toByteArray(buffer), getCharset()).toCharArray()); 470 } 471 472 /** 473 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those 474 * same values. The returned array will be half the length of the passed String or array, as it takes two characters 475 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. 476 * 477 * @param object A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits. 478 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 479 * @throws DecoderException Thrown if an odd number of characters is supplied to this function or the object is not 480 * a String or char[]. 481 * @see #decodeHex(char[]) 482 */ 483 @Override 484 public Object decode(final Object object) throws DecoderException { 485 if (object instanceof String) { 486 return decode(((String) object).toCharArray()); 487 } 488 if (object instanceof byte[]) { 489 return decode((byte[]) object); 490 } 491 if (object instanceof ByteBuffer) { 492 return decode((ByteBuffer) object); 493 } 494 try { 495 return decodeHex((char[]) object); 496 } catch (final ClassCastException e) { 497 throw new DecoderException(e.getMessage(), e); 498 } 499 } 500 501 /** 502 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each 503 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 504 * represent any given byte. 505 * <p> 506 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 507 * {@link #getCharset()}. 508 * </p> 509 * 510 * @param array A byte[] to convert to hexadecimal characters. 511 * @return A byte[] containing the bytes of the lower-case hexadecimal characters. 512 * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid. 513 * @see #encodeHex(byte[]) 514 */ 515 @Override 516 public byte[] encode(final byte[] array) { 517 return encodeHexString(array).getBytes(getCharset()); 518 } 519 520 /** 521 * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each byte 522 * in order. The returned array will be double the length of the passed array, as it takes two characters to 523 * represent any given byte. 524 * 525 * <p> 526 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 527 * {@link #getCharset()}. 528 * </p> 529 * 530 * <p> 531 * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 532 * the value {@link ByteBuffer#remaining() remaining()} will be zero. 533 * </p> 534 * 535 * @param array A byte buffer to convert to hexadecimal characters. 536 * @return A byte[] containing the bytes of the lower-case hexadecimal characters. 537 * @see #encodeHex(byte[]) 538 * @since 1.11 539 */ 540 public byte[] encode(final ByteBuffer array) { 541 return encodeHexString(array).getBytes(getCharset()); 542 } 543 544 /** 545 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each 546 * byte in order. The returned array will be double the length of the passed String or array, as it takes two 547 * characters to represent any given byte. 548 * <p> 549 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by 550 * {@link #getCharset()}. 551 * </p> 552 * 553 * @param object A String, ByteBuffer, or byte[] to convert to hexadecimal characters. 554 * @return A char[] containing lower-case hexadecimal characters. 555 * @throws EncoderException Thrown if the given object is not a String or byte[]. 556 * @see #encodeHex(byte[]) 557 */ 558 @Override 559 public Object encode(final Object object) throws EncoderException { 560 final byte[] byteArray; 561 if (object instanceof String) { 562 byteArray = ((String) object).getBytes(getCharset()); 563 } else if (object instanceof ByteBuffer) { 564 byteArray = toByteArray((ByteBuffer) object); 565 } else { 566 try { 567 byteArray = (byte[]) object; 568 } catch (final ClassCastException e) { 569 throw new EncoderException(e.getMessage(), e); 570 } 571 } 572 return encodeHex(byteArray); 573 } 574 575 /** 576 * Gets the charset. 577 * 578 * @return The charset. 579 * @since 1.7 580 */ 581 public Charset getCharset() { 582 return this.charset; 583 } 584 585 /** 586 * Gets the charset name. 587 * 588 * @return The charset name. 589 * @since 1.4 590 */ 591 public String getCharsetName() { 592 return this.charset.name(); 593 } 594 595 /** 596 * Returns a string representation of the object, which includes the charset name. 597 * 598 * @return A string representation of the object. 599 */ 600 @Override 601 public String toString() { 602 return super.toString() + "[charsetName=" + this.charset + "]"; 603 } 604}