001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.codec;
020
021/**
022 * <a href="http://en.wikipedia.org/wiki/Hexadecimal">Hexadecimal</a> encoder and decoder.
023 * <p/>
024 * This class was borrowed from Apache Commons Codec SVN repository (rev. {@code 560660}) with modifications
025 * to enable Hex conversion without a full dependency on Commons Codec.  We didn't want to reinvent the wheel of
026 * great work they've done, but also didn't want to force every Shiro user to depend on the commons-codec.jar
027 * <p/>
028 * As per the Apache 2.0 license, the original copyright notice and all author and copyright information have
029 * remained in tact.
030 *
031 * @see <a href="http://en.wikipedia.org/wiki/Hexadecimal">Wikipedia: Hexadecimal</a>
032 * @since 0.9
033 */
034public class Hex {
035
036    /**
037     * Used to build output as Hex
038     */
039    private static final char[] DIGITS = {
040            '0', '1', '2', '3', '4', '5', '6', '7',
041            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
042    };
043
044    /**
045     * Encodes the specifed byte array to a character array and then returns that character array
046     * as a String.
047     *
048     * @param bytes the byte array to Hex-encode.
049     * @return A String representation of the resultant hex-encoded char array.
050     */
051    public static String encodeToString(byte[] bytes) {
052        char[] encodedChars = encode(bytes);
053        return new String(encodedChars);
054    }
055
056    /**
057     * Converts an array of bytes into an array of characters representing the hexidecimal values of each byte in order.
058     * The returned array will be double the length of the passed array, as it takes two characters to represent any
059     * given byte.
060     *
061     * @param data byte[] to convert to Hex characters
062     * @return A char[] containing hexidecimal characters
063     */
064    public static char[] encode(byte[] data) {
065
066        int l = data.length;
067
068        char[] out = new char[l << 1];
069
070        // two characters form the hex value.
071        for (int i = 0, j = 0; i < l; i++) {
072            out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
073            out[j++] = DIGITS[0x0F & data[i]];
074        }
075
076        return out;
077    }
078
079    /**
080     * Converts an array of character bytes representing hexidecimal values into an
081     * array of bytes of those same values. The returned array will be half the
082     * length of the passed array, as it takes two characters to represent any
083     * given byte. An exception is thrown if the passed char array has an odd
084     * number of elements.
085     *
086     * @param array An array of character bytes containing hexidecimal digits
087     * @return A byte array containing binary data decoded from
088     *         the supplied byte array (representing characters).
089     * @throws IllegalArgumentException Thrown if an odd number of characters is supplied
090     *                                  to this function
091     * @see #decode(char[])
092     */
093    public static byte[] decode(byte[] array) throws IllegalArgumentException {
094        String s = CodecSupport.toString(array);
095        return decode(s);
096    }
097
098    /**
099     * Converts the specified Hex-encoded String into a raw byte array.  This is a
100     * convenience method that merely delegates to {@link #decode(char[])} using the
101     * argument's hex.toCharArray() value.
102     *
103     * @param hex a Hex-encoded String.
104     * @return A byte array containing binary data decoded from the supplied String's char array.
105     */
106    public static byte[] decode(String hex) {
107        return decode(hex.toCharArray());
108    }
109
110    /**
111     * Converts an array of characters representing hexidecimal values into an
112     * array of bytes of those same values. The returned array will be half the
113     * length of the passed array, as it takes two characters to represent any
114     * given byte. An exception is thrown if the passed char array has an odd
115     * number of elements.
116     *
117     * @param data An array of characters containing hexidecimal digits
118     * @return A byte array containing binary data decoded from
119     *         the supplied char array.
120     * @throws IllegalArgumentException if an odd number or illegal of characters
121     *                                  is supplied
122     */
123    public static byte[] decode(char[] data) throws IllegalArgumentException {
124
125        int len = data.length;
126
127        if ((len & 0x01) != 0) {
128            throw new IllegalArgumentException("Odd number of characters.");
129        }
130
131        byte[] out = new byte[len >> 1];
132
133        // two characters form the hex value.
134        for (int i = 0, j = 0; j < len; i++) {
135            int f = toDigit(data[j], j) << 4;
136            j++;
137            f = f | toDigit(data[j], j);
138            j++;
139            out[i] = (byte) (f & 0xFF);
140        }
141
142        return out;
143    }
144
145    /**
146     * Converts a hexadecimal character to an integer.
147     *
148     * @param ch    A character to convert to an integer digit
149     * @param index The index of the character in the source
150     * @return An integer
151     * @throws IllegalArgumentException if ch is an illegal hex character
152     */
153    protected static int toDigit(char ch, int index) throws IllegalArgumentException {
154        int digit = Character.digit(ch, 16);
155        if (digit == -1) {
156            throw new IllegalArgumentException("Illegal hexadecimal charcter " + ch + " at index " + index);
157        }
158        return digit;
159    }
160
161
162}