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