001package com.identityworksllc.iiq.common;
002
003import sailpoint.tools.Base64;
004import sailpoint.tools.GeneralException;
005
006import java.security.MessageDigest;
007import java.security.NoSuchAlgorithmException;
008import java.util.function.Function;
009
010/**
011 * Utilities to return hexadecimal hashes of strings
012 */
013public class HashUtilities {
014    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
015
016    /**
017     * Transforms the input byte array to Base64. This is intended as an input to
018     * {@link #hash(String, String, Function)}, as the encoder.
019     *
020     * @param bytes The input bytes
021     * @return A base64 string
022     */
023    public static String bytesToBase64(byte[] bytes) {
024        return Base64.encodeBytes(bytes);
025    }
026
027    /**
028     * Transforms the given byte array to hexadecimal. This is intended as an input to
029     * {@link #hash(String, String, Function)}, as the encoder.
030     *
031     * @param bytes The input byte array
032     * @return The output as a hexadecimal string
033     */
034    public static String bytesToHex(byte[] bytes) {
035        char[] hexChars = new char[bytes.length * 2];
036        for (int j = 0; j < bytes.length; j++) {
037            int v = bytes[j] & 0xFF;
038            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
039            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
040        }
041        return new String(hexChars);
042    }
043
044    /**
045     * Performs a hash of the given input with the given hash function. This implementation
046     * takes care of the javax.crypto initialization for you.
047     *
048     * @param hashFunction The hash function to use
049     * @param input The input string to hash
050     * @return The hashed value as a hexadecimal string
051     * @throws GeneralException if any cryptographic failures occur
052     */
053    public static String hash(String hashFunction, String input) throws GeneralException {
054        return hash(hashFunction, input, HashUtilities::bytesToHex);
055    }
056
057    /**
058     * Performs a hash of the given input with the given hash function. This method allows you
059     * to replace the encoding implementation if you don't wish to receive your output as a
060     * hexadecimal string.
061     *
062     * @param hashFunction The hash function to use
063     * @param input The input string to hash
064     * @param encoder A function to encode the byte array into a String if you wish to replace the hexadecimal implementation
065     * @return The hashed value as a hexadecimal string
066     * @throws GeneralException if any cryptographic failures occur
067     */
068    public static String hash(String hashFunction, String input, Function<byte[], String> encoder) throws GeneralException {
069        try {
070            MessageDigest md = MessageDigest.getInstance(hashFunction);
071            md.update(input.getBytes());
072            byte[] digest = md.digest();
073            return encoder.apply(digest);
074        } catch(NoSuchAlgorithmException e) {
075            throw new GeneralException(e);
076        }
077    }
078
079    /**
080     * Hashes the input using the MD5 algorithm and returns it as a hex string
081     * @param input The input string
082     * @return The MD5 hash of the input string in hex
083     * @throws GeneralException if any cryptographic failures occur
084     */
085    public static String md5(String input) throws GeneralException {
086        return hash("MD5", input);
087    }
088
089    /**
090     * Hashes the input using the SHA-256 algorithm and returns it as a hex string
091     * @param input The input string
092     * @return The SHA-256 hash of the input string in hex
093     * @throws GeneralException if any cryptographic failures occur
094     */
095    public static String sha256(String input) throws GeneralException {
096        return hash("SHA-256", input);
097    }
098
099    /**
100     * Private utility constructor
101     */
102    private HashUtilities() {
103
104    }
105}