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     */
019    package 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     */
034    public 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    }