001package co.codewizards.cloudstore.core.util;
002
003import java.io.ByteArrayInputStream;
004import java.io.IOException;
005import java.io.InputStream;
006import java.security.MessageDigest;
007import java.security.NoSuchAlgorithmException;
008import java.util.Locale;
009
010import co.codewizards.cloudstore.core.progress.NullProgressMonitor;
011import co.codewizards.cloudstore.core.progress.ProgressMonitor;
012
013public final class HashUtil {
014
015        /**
016         * Specifies usage of the MD5 algorithm in {@link #hash(String, InputStream)}.
017         */
018        public static final String HASH_ALGORITHM_MD5 = "MD5";
019        /**
020         * Specifies usage of the SHA algorithm in {@link #hash(String, InputStream)}.
021         */
022        public static final String HASH_ALGORITHM_SHA = "SHA";
023
024        private HashUtil() { }
025
026        public static String encodeHexStr(byte[] buf)
027        {
028                return encodeHexStr(buf, 0, buf.length);
029        }
030
031        /**
032         * Encode a byte array into a human readable hex string. For each byte,
033         * two hex digits are produced. They are concatenated without any separators.
034         *
035         * @param buf The byte array to translate into human readable text.
036         * @param pos The start position (0-based).
037         * @param len The number of bytes that shall be processed beginning at the position specified by <code>pos</code>.
038         * @return a human readable string like "fa3d70" for a byte array with 3 bytes and these values.
039         * @see #encodeHexStr(byte[])
040         * @see #decodeHexStr(String)
041         */
042        public static String encodeHexStr(byte[] buf, int pos, int len)
043        {
044                 StringBuilder hex = new StringBuilder();
045                 while (len-- > 0) {
046                                byte ch = buf[pos++];
047                                int d = (ch >> 4) & 0xf;
048                                hex.append((char)(d >= 10 ? 'a' - 10 + d : '0' + d));
049                                d = ch & 0xf;
050                                hex.append((char)(d >= 10 ? 'a' - 10 + d : '0' + d));
051                 }
052                 return hex.toString();
053        }
054
055        /**
056         * Decode a string containing two hex digits for each byte.
057         * @param hex The hex encoded string
058         * @return The byte array represented by the given hex string
059         * @see #encodeHexStr(byte[])
060         * @see #encodeHexStr(byte[], int, int)
061         */
062        public static byte[] decodeHexStr(String hex)
063        {
064                if (hex.length() % 2 != 0)
065                        throw new IllegalArgumentException("The hex string must have an even number of characters!");
066
067                byte[] res = new byte[hex.length() / 2];
068
069                int m = 0;
070                for (int i = 0; i < hex.length(); i += 2) {
071                        res[m++] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
072                }
073
074                return res;
075        }
076
077        public static byte[] hash(String algorithm, InputStream in) throws NoSuchAlgorithmException, IOException {
078                return hash(algorithm, in, new NullProgressMonitor());
079        }
080
081        public static byte[] hash(String algorithm, InputStream in, ProgressMonitor monitor) throws NoSuchAlgorithmException, IOException {
082                monitor.beginTask(algorithm, Math.max(1, in.available()));
083                try {
084                        MessageDigest md = MessageDigest.getInstance(algorithm);
085                        byte[] data = new byte[10240];
086                        while (true) {
087                                int bytesRead = in.read(data, 0, data.length);
088                                if (bytesRead < 0) {
089                                        break;
090                                }
091                                if (bytesRead > 0) {
092                                        md.update(data, 0, bytesRead);
093                                        monitor.worked(bytesRead);
094                                }
095                        }
096                        return md.digest();
097                } finally {
098                        monitor.done();
099                }
100        }
101
102        public static String formatEncodedHexStrForHuman(String hex) {
103                if (hex.length() % 2 != 0)
104                        throw new IllegalArgumentException("The hex string must have an even number of characters!");
105
106                hex = hex.toUpperCase(Locale.UK);
107
108                StringBuilder sb = new StringBuilder(hex.length() * 3 / 2);
109                for (int i = 0; i < hex.length(); i += 2) {
110                        if (sb.length() > 0)
111                                sb.append(':');
112
113                        sb.append(hex.substring(i, i + 2));
114                }
115                return sb.toString();
116        }
117
118        public static String sha1ForHuman(byte[] in) {
119                try {
120                        return sha1ForHuman(new ByteArrayInputStream(in));
121                } catch (IOException x) {
122                        throw new RuntimeException(x);
123                }
124        }
125
126        public static String sha1ForHuman(InputStream in) throws IOException {
127                return formatEncodedHexStrForHuman(sha1(in));
128        }
129
130        public static String sha1(String in) {
131                return sha1(in.getBytes(IOUtil.CHARSET_UTF_8));
132        }
133
134        public static String sha1(byte[] in) {
135                try {
136                        return sha1(new ByteArrayInputStream(in));
137                } catch (IOException x) {
138                        throw new RuntimeException(x);
139                }
140        }
141
142        public static String sha1(InputStream in) throws IOException {
143                byte[] hash;
144                try {
145                        hash = hash(HASH_ALGORITHM_SHA, in);
146                } catch (NoSuchAlgorithmException e) {
147                        throw new RuntimeException(e);
148                }
149                return encodeHexStr(hash);
150        }
151
152}