Skip to content

Commit 601fd9a

Browse files
committed
Add EncodingUtils and DigestUtils
1 parent 596b5ef commit 601fd9a

File tree

4 files changed

+215
-1
lines changed

4 files changed

+215
-1
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package me.ycdev.android.lib.common.utils;
2+
3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.IOException;
6+
import java.io.InputStream;
7+
import java.io.UnsupportedEncodingException;
8+
import java.security.MessageDigest;
9+
import java.security.NoSuchAlgorithmException;
10+
11+
import static me.ycdev.android.lib.common.utils.EncodingUtils.encodeWithHex;
12+
13+
@SuppressWarnings({"WeakerAccess", "unused"})
14+
public class DigestUtils {
15+
public static String md5(final String text)
16+
throws NoSuchAlgorithmException, UnsupportedEncodingException {
17+
return hash(text, "MD5");
18+
}
19+
20+
public static String md5(byte[] data) throws NoSuchAlgorithmException {
21+
return hash(data, "MD5");
22+
}
23+
24+
public static String sha1(String text)
25+
throws NoSuchAlgorithmException, UnsupportedEncodingException {
26+
return hash(text, "SHA-1");
27+
}
28+
29+
public static String sha1(byte[] data)
30+
throws NoSuchAlgorithmException {
31+
return hash(data, "SHA-1");
32+
}
33+
34+
public static String hash(String text, String algorithm)
35+
throws NoSuchAlgorithmException, UnsupportedEncodingException {
36+
return hash(text.getBytes("UTF-8"), algorithm);
37+
}
38+
39+
public static String hash(byte[] data, String algorithm)
40+
throws NoSuchAlgorithmException {
41+
MessageDigest digest = MessageDigest.getInstance(algorithm);
42+
digest.update(data);
43+
byte messageDigest[] = digest.digest();
44+
return encodeWithHex(messageDigest, false);
45+
}
46+
47+
/**
48+
* The caller should close the stream.
49+
*/
50+
public static String md5(final InputStream stream)
51+
throws NoSuchAlgorithmException, IOException {
52+
if (stream == null) {
53+
throw new IllegalArgumentException("Invalid input stream!");
54+
}
55+
byte[] buffer = new byte[1024];
56+
MessageDigest complete = MessageDigest.getInstance("MD5");
57+
int numRead;
58+
do {
59+
numRead = stream.read(buffer);
60+
if (numRead > 0) {
61+
complete.update(buffer, 0, numRead);
62+
}
63+
} while (numRead != -1);
64+
byte[] digest = complete.digest();
65+
return encodeWithHex(digest, false);
66+
}
67+
68+
public static String md5(final File file) throws NoSuchAlgorithmException, IOException {
69+
FileInputStream stream = null;
70+
try {
71+
stream = new FileInputStream(file);
72+
return md5(stream);
73+
} finally {
74+
IoUtils.closeQuietly(stream);
75+
}
76+
}
77+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package me.ycdev.android.lib.common.utils;
2+
3+
import androidx.annotation.NonNull;
4+
5+
@SuppressWarnings({"WeakerAccess", "unused"})
6+
public class EncodingUtils {
7+
private static final char[] HEX_ARRAY_UPPERCASE = {'0', '1', '2', '3', '4', '5', '6',
8+
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
9+
private static final char[] HEX_ARRAY_LOWERCASE = {'0', '1', '2', '3', '4', '5', '6',
10+
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
11+
12+
/**
13+
* Encode the data with HEX (Base16) encoding and with uppercase letters.
14+
*/
15+
public static String encodeWithHex(byte[] bytes) {
16+
return encodeWithHex(bytes, true);
17+
}
18+
19+
public static String encodeWithHex(byte[] bytes, boolean uppercase) {
20+
if (bytes == null) {
21+
return "null";
22+
}
23+
return encodeWithHex(bytes, 0, bytes.length, uppercase);
24+
}
25+
26+
/**
27+
* Encode the data with HEX (Base16) encoding and with uppercase letters.
28+
*/
29+
public static String encodeWithHex(@NonNull byte[] bytes, int startPos, int endPos) {
30+
return encodeWithHex(bytes, startPos, endPos, true);
31+
}
32+
33+
public static String encodeWithHex(@NonNull byte[] bytes, int startPos, int endPos, boolean uppercase) {
34+
if (endPos > bytes.length) {
35+
endPos = bytes.length;
36+
}
37+
final int N = endPos - startPos;
38+
final char[] HEX_ARRAY = uppercase ? HEX_ARRAY_UPPERCASE : HEX_ARRAY_LOWERCASE;
39+
char[] hexChars = new char[N * 2];
40+
for (int i = startPos, j = 0; i < endPos; i++, j += 2) {
41+
int v = bytes[i] & 0xFF;
42+
hexChars[j] = HEX_ARRAY[v >>> 4];
43+
hexChars[j + 1] = HEX_ARRAY[v & 0x0F];
44+
}
45+
return new String(hexChars);
46+
}
47+
48+
public static byte[] fromHexString(@NonNull String hexStr) {
49+
hexStr = hexStr.replace(" ", ""); // support spaces
50+
if (hexStr.length() % 2 != 0) {
51+
throw new IllegalArgumentException("Bad length: " + hexStr);
52+
}
53+
54+
byte[] result = new byte[hexStr.length() / 2];
55+
for (int i = 0; i < result.length; i++) {
56+
int high = fromHexChar(hexStr, i * 2) << 4;
57+
int low = fromHexChar(hexStr, i * 2 + 1);
58+
result[i] = (byte) ((high | low) & 0xFF);
59+
}
60+
return result;
61+
}
62+
63+
private static int fromHexChar(String hexStr, int index) {
64+
char ch = hexStr.charAt(index);
65+
if (ch >= '0' && ch <= '9') {
66+
return ch - '0';
67+
} else if (ch >= 'a' && ch <= 'f') {
68+
return 10 + (ch - 'a');
69+
} else if (ch >= 'A' && ch <= 'F') {
70+
return 10 + (ch - 'A');
71+
} else {
72+
throw new IllegalArgumentException("Not hex string: " + hexStr);
73+
}
74+
}
75+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package me.ycdev.android.lib.common.utils;
2+
3+
import org.junit.Rule;
4+
import org.junit.Test;
5+
import org.junit.rules.ExpectedException;
6+
7+
import static org.hamcrest.CoreMatchers.equalTo;
8+
import static org.hamcrest.CoreMatchers.startsWith;
9+
import static org.hamcrest.MatcherAssert.assertThat;
10+
11+
public class EncodingUtilsTest {
12+
@Rule
13+
public ExpectedException thrownRule = ExpectedException.none();
14+
15+
@Test
16+
public void encodeWithHex() {
17+
byte[] data = new byte[] {0x1a, (byte)0x2b, 0x3c, 0x4d, (byte)0x5c, 0x6d, (byte)0x7e};
18+
String result = EncodingUtils.encodeWithHex(data, 0, data.length);
19+
assertThat(result, equalTo("1A2B3C4D5C6D7E"));
20+
result = EncodingUtils.encodeWithHex(data, 1, 4, true);
21+
assertThat(result, equalTo("2B3C4D"));
22+
result = EncodingUtils.encodeWithHex(data, 3, 20);
23+
assertThat(result, equalTo("4D5C6D7E"));
24+
25+
// lowercase
26+
result = EncodingUtils.encodeWithHex(data, 0, data.length, false);
27+
assertThat(result, equalTo("1a2b3c4d5c6d7e"));
28+
result = EncodingUtils.encodeWithHex(data, 1, 4, false);
29+
assertThat(result, equalTo("2b3c4d"));
30+
result = EncodingUtils.encodeWithHex(data, 3, 20, false);
31+
assertThat(result, equalTo("4d5c6d7e"));
32+
}
33+
34+
@Test
35+
public void test_fromHexString() {
36+
String hexStr = "01020304050607";
37+
String hexStr2 = " 010 20 30 405 060 7 ";
38+
String hexStr3 = "010 203 040 506 07";
39+
byte[] data = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
40+
assertThat(EncodingUtils.fromHexString(hexStr), equalTo(data));
41+
assertThat(EncodingUtils.fromHexString(hexStr2), equalTo(data));
42+
assertThat(EncodingUtils.fromHexString(hexStr3), equalTo(data));
43+
}
44+
45+
@Test
46+
public void test_illegalLength() {
47+
thrownRule.expect(IllegalArgumentException.class);
48+
thrownRule.expectMessage(startsWith("Bad length: 10101"));
49+
50+
String hexStr = "10101";
51+
EncodingUtils.fromHexString(hexStr);
52+
}
53+
54+
@Test
55+
public void test_illegalCharacter() {
56+
thrownRule.expect(IllegalArgumentException.class);
57+
thrownRule.expectMessage(startsWith("Not hex string: 10101X"));
58+
59+
String hexStr = "10101X";
60+
EncodingUtils.fromHexString(hexStr);
61+
}
62+
}

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ buildscript {
1111
}
1212

1313
dependencies {
14-
classpath 'com.android.tools.build:gradle:3.3.0'
14+
classpath 'com.android.tools.build:gradle:3.3.1'
1515
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
1616

1717
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'

0 commit comments

Comments
 (0)