|
@@ -0,0 +1,548 @@
|
|
|
|
|
+package com.atmob.common.crypto;
|
|
|
|
|
+
|
|
|
|
|
+import android.util.Base64;
|
|
|
|
|
+import android.util.Log;
|
|
|
|
|
+
|
|
|
|
|
+import java.io.InputStream;
|
|
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
|
|
+import java.security.MessageDigest;
|
|
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
|
|
+import java.security.SecureRandom;
|
|
|
|
|
+import java.security.spec.KeySpec;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+
|
|
|
|
|
+import javax.crypto.Cipher;
|
|
|
|
|
+import javax.crypto.SecretKey;
|
|
|
|
|
+import javax.crypto.SecretKeyFactory;
|
|
|
|
|
+import javax.crypto.spec.IvParameterSpec;
|
|
|
|
|
+import javax.crypto.spec.PBEKeySpec;
|
|
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 加密算法相关的工具方法
|
|
|
|
|
+ * Created by benson on 14/12/24.
|
|
|
|
|
+ */
|
|
|
|
|
+public final class CryptoUtils {
|
|
|
|
|
+ public static final String TAG = CryptoUtils.class.getSimpleName();
|
|
|
|
|
+ public static final String ENC_UTF8 = Charsets.ENCODING_UTF_8;
|
|
|
|
|
+
|
|
|
|
|
+ private CryptoUtils() {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static String getRandomString() {
|
|
|
|
|
+ SecureRandom random = new SecureRandom();
|
|
|
|
|
+ return String.valueOf(random.nextLong());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static byte[] getRandomBytes(int size) {
|
|
|
|
|
+ SecureRandom random = new SecureRandom();
|
|
|
|
|
+ byte[] bytes = new byte[size];
|
|
|
|
|
+ random.nextBytes(bytes);
|
|
|
|
|
+ return bytes;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static byte[] getRawBytes(String text) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return text.getBytes(ENC_UTF8);
|
|
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
|
|
+ return text.getBytes();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static String getString(byte[] data) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return new String(data, ENC_UTF8);
|
|
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
|
|
+ return new String(data);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static byte[] base64Decode(String text) {
|
|
|
|
|
+ return Base64.decode(text, Base64.NO_WRAP);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static String base64Encode(byte[] data) {
|
|
|
|
|
+ return Base64.encodeToString(data, Base64.NO_WRAP);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static final class AES {
|
|
|
|
|
+ static final int ITERATION_COUNT_DEFAULT = 100;
|
|
|
|
|
+ static final int KEY_SIZE_DEFAULT = 256;
|
|
|
|
|
+ static final int IV_SIZE_DEFAULT = 16;
|
|
|
|
|
+ static final String KEY_AES_SPEC = "AES/CBC/PKCS7Padding";
|
|
|
|
|
+
|
|
|
|
|
+ public static String encrypt(String text) {
|
|
|
|
|
+ return encrypt(text, getSimplePassword(), getSimpleSalt(),
|
|
|
|
|
+ getSimpleIV());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String decrypt(String text) {
|
|
|
|
|
+ return decrypt(text, getSimplePassword(), getSimpleSalt(),
|
|
|
|
|
+ getSimpleIV());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] encrypt(byte[] data) {
|
|
|
|
|
+ return encrypt(data, getSimplePassword(), getSimpleSalt(),
|
|
|
|
|
+ getSimpleIV(), KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] decrypt(byte[] data) {
|
|
|
|
|
+ return decrypt(data, getSimplePassword(), getSimpleSalt(),
|
|
|
|
|
+ getSimpleIV(), KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String encrypt(String text, String password) {
|
|
|
|
|
+ return encrypt(text, password, getSimpleSalt(), getSimpleIV());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String decrypt(String text, String password) {
|
|
|
|
|
+ return decrypt(text, password, getSimpleSalt(), getSimpleIV());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] encrypt(byte[] data, String password) {
|
|
|
|
|
+ return encrypt(data, password, getSimpleSalt(), getSimpleIV(),
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] decrypt(byte[] data, String password) {
|
|
|
|
|
+ return decrypt(data, password, getSimpleSalt(), getSimpleIV(),
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String encrypt(String text, String password, byte[] salt) {
|
|
|
|
|
+ return encrypt(text, password, salt, getSimpleIV());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String decrypt(String text, String password, byte[] salt) {
|
|
|
|
|
+ return decrypt(text, password, salt, getSimpleIV());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] encrypt(byte[] data, String password, byte[] salt) {
|
|
|
|
|
+ return encrypt(data, password, salt, getSimpleIV(),
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] decrypt(byte[] data, String password, byte[] salt) {
|
|
|
|
|
+ return decrypt(data, password, salt, getSimpleIV(),
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String encrypt(String text, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv) {
|
|
|
|
|
+ byte[] data = getRawBytes(text);
|
|
|
|
|
+ byte[] encryptedData = encrypt(data, password, salt, iv,
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ return base64Encode(encryptedData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String decrypt(String text, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv) {
|
|
|
|
|
+ byte[] encryptedData = base64Decode(text);
|
|
|
|
|
+ byte[] data = decrypt(encryptedData, password, salt, iv,
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ return getString(data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] encrypt(byte[] data, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv) {
|
|
|
|
|
+ return encrypt(data, password, salt, iv, KEY_SIZE_DEFAULT,
|
|
|
|
|
+ ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] decrypt(byte[] data, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv) {
|
|
|
|
|
+ return decrypt(data, password, salt, iv, KEY_SIZE_DEFAULT,
|
|
|
|
|
+ ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] encrypt(byte[] data, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv, int keySize) {
|
|
|
|
|
+ return encrypt(data, password, salt, iv, keySize,
|
|
|
|
|
+ ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] decrypt(byte[] data, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv, int keySize) {
|
|
|
|
|
+ return decrypt(data, password, salt, iv, keySize,
|
|
|
|
|
+ ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] encrypt(byte[] data, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv, int keySize, int iterationCount) {
|
|
|
|
|
+ return process(data, Cipher.ENCRYPT_MODE, password, salt, iv,
|
|
|
|
|
+ keySize, iterationCount);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] decrypt(byte[] data, String password, byte[] salt,
|
|
|
|
|
+ byte[] iv, int keySize, int iterationCount) {
|
|
|
|
|
+ return process(data, Cipher.DECRYPT_MODE, password, salt, iv,
|
|
|
|
|
+ keySize, iterationCount);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * AES encrypt function
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param original
|
|
|
|
|
+ * @param key 16, 24, 32 bytes available
|
|
|
|
|
+ * @param iv initial vector (16 bytes) - if null: ECB mode, otherwise:
|
|
|
|
|
+ * CBC mode
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
|
|
+ public static byte[] encrypt(byte[] original, byte[] key, byte[] iv) {
|
|
|
|
|
+ if (key == null
|
|
|
|
|
+ || (key.length != 16 && key.length != 24 && key.length != 32)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (iv != null && iv.length != 16) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ SecretKeySpec keySpec = null;
|
|
|
|
|
+ Cipher cipher = null;
|
|
|
|
|
+ if (iv != null) {
|
|
|
|
|
+ keySpec = new SecretKeySpec(key, KEY_AES_SPEC);
|
|
|
|
|
+ cipher = Cipher.getInstance(KEY_AES_SPEC);
|
|
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec,
|
|
|
|
|
+ new IvParameterSpec(iv));
|
|
|
|
|
+ } else // if(iv == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ keySpec = new SecretKeySpec(key, KEY_AES_SPEC);
|
|
|
|
|
+ cipher = Cipher.getInstance(KEY_AES_SPEC);
|
|
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return cipher.doFinal(original);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * AES decrypt function
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param encrypted
|
|
|
|
|
+ * @param key 16, 24, 32 bytes available
|
|
|
|
|
+ * @param iv initial vector (16 bytes) - if null: ECB mode, otherwise:
|
|
|
|
|
+ * CBC mode
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
|
|
+ public static byte[] decrypt(byte[] encrypted, byte[] key, byte[] iv) {
|
|
|
|
|
+ if (key == null
|
|
|
|
|
+ || (key.length != 16 && key.length != 24 && key.length != 32)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (iv != null && iv.length != 16) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ SecretKeySpec keySpec = null;
|
|
|
|
|
+ Cipher cipher = null;
|
|
|
|
|
+ if (iv != null) {
|
|
|
|
|
+ keySpec = new SecretKeySpec(key, "AES/CBC/PKCS7Padding");// AES/ECB/PKCS5Padding
|
|
|
|
|
+ cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
|
|
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, keySpec,
|
|
|
|
|
+ new IvParameterSpec(iv));
|
|
|
|
|
+ } else // if(iv == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ keySpec = new SecretKeySpec(key, "AES/ECB/PKCS7Padding");
|
|
|
|
|
+ cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
|
|
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return cipher.doFinal(encrypted);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static byte[] process(byte[] data, int mode, String password,
|
|
|
|
|
+ byte[] salt, byte[] iv, int keySize, int iterationCount) {
|
|
|
|
|
+ KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
|
|
|
|
|
+ iterationCount, keySize);
|
|
|
|
|
+ try {
|
|
|
|
|
+ SecretKeyFactory keyFactory = SecretKeyFactory
|
|
|
|
|
+ .getInstance("PBKDF2WithHmacSHA1");
|
|
|
|
|
+ byte[] keyBytes = keyFactory.generateSecret(keySpec)
|
|
|
|
|
+ .getEncoded();
|
|
|
|
|
+ SecretKey key = new SecretKeySpec(keyBytes, "AES");
|
|
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
|
|
|
+ IvParameterSpec ivParams = new IvParameterSpec(iv);
|
|
|
|
|
+ cipher.init(mode, key, ivParams);
|
|
|
|
|
+ return cipher.doFinal(data);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static String getSimplePassword() {
|
|
|
|
|
+ return "GZ9Gn2U5nhpea8hw";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static byte[] getSimpleSalt() {
|
|
|
|
|
+ return "rUiey8D2GNzV69Mp".getBytes();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ static byte[] getSimpleIV() {
|
|
|
|
|
+ byte[] iv = new byte[AES.IV_SIZE_DEFAULT];
|
|
|
|
|
+ Arrays.fill(iv, (byte) 5);
|
|
|
|
|
+ return iv;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // http://nelenkov.blogspot.jp/2012/04/using-password-based-encryption-on.html
|
|
|
|
|
+ public static class AESCrypto {
|
|
|
|
|
+ private static final int ITERATION_COUNT_DEFAULT = 100;
|
|
|
|
|
+ private static final int ITERATION_COUNT_MIN = 10;
|
|
|
|
|
+ private static final int ITERATION_COUNT_MAX = 5000;
|
|
|
|
|
+ private static final int KEY_SIZE_DEFAULT = 256;
|
|
|
|
|
+ private static final int KEY_SIZE_MIN = 64;
|
|
|
|
|
+ private static final int KEY_SIZE_MAX = 1024;
|
|
|
|
|
+ private static final int IV_SIZE = 16;
|
|
|
|
|
+ private String password;
|
|
|
|
|
+ private byte[] salt;
|
|
|
|
|
+ private byte[] iv;
|
|
|
|
|
+ private int keySize;
|
|
|
|
|
+ private int iterCount;
|
|
|
|
|
+
|
|
|
|
|
+ public AESCrypto(String password) {
|
|
|
|
|
+ initialize(password, AES.getSimpleSalt(), AES.getSimpleIV(),
|
|
|
|
|
+ KEY_SIZE_DEFAULT, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AESCrypto(String password, byte[] salt) {
|
|
|
|
|
+ initialize(password, salt, AES.getSimpleIV(), KEY_SIZE_DEFAULT,
|
|
|
|
|
+ ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AESCrypto(String password, int keySize, byte[] salt, byte[] iv) {
|
|
|
|
|
+ initialize(password, salt, iv, keySize, ITERATION_COUNT_DEFAULT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void initialize(String password, byte[] salt, byte[] iv,
|
|
|
|
|
+ int keySize, int iterCount) {
|
|
|
|
|
+ this.password = password;
|
|
|
|
|
+ this.salt = salt;
|
|
|
|
|
+ this.iv = iv;
|
|
|
|
|
+ this.keySize = keySize;
|
|
|
|
|
+ this.iterCount = iterCount;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public String encrypt(String text) {
|
|
|
|
|
+ byte[] data = getRawBytes(text);
|
|
|
|
|
+ byte[] encryptedData = encrypt(data);
|
|
|
|
|
+ return base64Encode(encryptedData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public byte[] encrypt(byte[] data) {
|
|
|
|
|
+ return process(data, Cipher.ENCRYPT_MODE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public String decrypt(String text) {
|
|
|
|
|
+ byte[] encryptedData = base64Decode(text);
|
|
|
|
|
+ byte[] data = decrypt(encryptedData);
|
|
|
|
|
+ return getString(data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public byte[] decrypt(byte[] encryptedData) {
|
|
|
|
|
+ return process(encryptedData, Cipher.DECRYPT_MODE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private byte[] process(byte[] data, int mode) {
|
|
|
|
|
+ return AES.process(data, mode, password, salt, iv, keySize,
|
|
|
|
|
+ iterCount);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static final class HEX {
|
|
|
|
|
+
|
|
|
|
|
+ private static final char[] HEX_DIGITS = new char[]{'0', '1', '2',
|
|
|
|
|
+ '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
|
|
|
+
|
|
|
|
|
+ private static final char[] FIRST_CHAR = new char[256];
|
|
|
|
|
+ private static final char[] SECOND_CHAR = new char[256];
|
|
|
|
|
+
|
|
|
|
|
+ static {
|
|
|
|
|
+ for (int i = 0; i < 256; i++) {
|
|
|
|
|
+ FIRST_CHAR[i] = HEX_DIGITS[(i >> 4) & 0xF];
|
|
|
|
|
+ SECOND_CHAR[i] = HEX_DIGITS[i & 0xF];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static final byte[] DIGITS = new byte['f' + 1];
|
|
|
|
|
+
|
|
|
|
|
+ static {
|
|
|
|
|
+ for (int i = 0; i <= 'F'; i++) {
|
|
|
|
|
+ DIGITS[i] = -1;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (byte i = 0; i < 10; i++) {
|
|
|
|
|
+ DIGITS['0' + i] = i;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (byte i = 0; i < 6; i++) {
|
|
|
|
|
+ DIGITS['A' + i] = (byte) (10 + i);
|
|
|
|
|
+ DIGITS['a' + i] = (byte) (10 + i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Quickly converts a byte array to a hexadecimal string representation.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array byte array, possibly zero-terminated.
|
|
|
|
|
+ */
|
|
|
|
|
+ public static String encodeHex(byte[] array, boolean zeroTerminated) {
|
|
|
|
|
+ char[] cArray = new char[array.length * 2];
|
|
|
|
|
+
|
|
|
|
|
+ int j = 0;
|
|
|
|
|
+ for (int i = 0; i < array.length; i++) {
|
|
|
|
|
+ int index = array[i] & 0xFF;
|
|
|
|
|
+ if (index == 0 && zeroTerminated) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cArray[j++] = FIRST_CHAR[index];
|
|
|
|
|
+ cArray[j++] = SECOND_CHAR[index];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new String(cArray, 0, j);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Quickly converts a hexadecimal string to a byte array.
|
|
|
|
|
+ */
|
|
|
|
|
+ public static byte[] decodeHex(String hexString) {
|
|
|
|
|
+ int length = hexString.length();
|
|
|
|
|
+
|
|
|
|
|
+ if ((length & 0x01) != 0) {
|
|
|
|
|
+ throw new IllegalArgumentException("Odd number of characters.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ boolean badHex = false;
|
|
|
|
|
+ byte[] out = new byte[length >> 1];
|
|
|
|
|
+ for (int i = 0, j = 0; j < length; i++) {
|
|
|
|
|
+ int c1 = hexString.charAt(j++);
|
|
|
|
|
+ if (c1 > 'f') {
|
|
|
|
|
+ badHex = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ final byte d1 = DIGITS[c1];
|
|
|
|
|
+ if (d1 == -1) {
|
|
|
|
|
+ badHex = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int c2 = hexString.charAt(j++);
|
|
|
|
|
+ if (c2 > 'f') {
|
|
|
|
|
+ badHex = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ final byte d2 = DIGITS[c2];
|
|
|
|
|
+ if (d2 == -1) {
|
|
|
|
|
+ badHex = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ out[i] = (byte) (d1 << 4 | d2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (badHex) {
|
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
|
+ "Invalid hexadecimal digit: " + hexString);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return out;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static final class HASH {
|
|
|
|
|
+ private static final String MD5 = "MD5";
|
|
|
|
|
+ private static final String SHA_1 = "SHA-1";
|
|
|
|
|
+ private static final String SHA_256 = "SHA-256";
|
|
|
|
|
+ private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4',
|
|
|
|
|
+ '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
|
|
|
|
+ private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4',
|
|
|
|
|
+ '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
|
|
|
+
|
|
|
|
|
+ public static String md5(byte[] data) {
|
|
|
|
|
+ return new String(encodeHex(md5Bytes(data)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String md5(String text) {
|
|
|
|
|
+ return new String(encodeHex(md5Bytes(getRawBytes(text))));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String md5(InputStream is) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return md5(IOUtils.readBytes(is));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ Log.w("exception", e);
|
|
|
|
|
+ return "";
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ IOUtils.closeQuietly(is);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] md5Bytes(byte[] data) {
|
|
|
|
|
+ return getDigest(MD5).digest(data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String sha1(byte[] data) {
|
|
|
|
|
+ return new String(encodeHex(sha1Bytes(data)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String sha1(String text) {
|
|
|
|
|
+ return new String(encodeHex(sha1Bytes(getRawBytes(text))));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] sha1Bytes(byte[] data) {
|
|
|
|
|
+ return getDigest(SHA_1).digest(data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String sha256(byte[] data) {
|
|
|
|
|
+ return new String(encodeHex(sha256Bytes(data)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String sha256(String text) {
|
|
|
|
|
+ return new String(encodeHex(sha256Bytes(getRawBytes(text))));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static byte[] sha256Bytes(byte[] data) {
|
|
|
|
|
+ return getDigest(SHA_256).digest(data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static MessageDigest getDigest(String algorithm) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return MessageDigest.getInstance(algorithm);
|
|
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
|
|
+ throw new IllegalArgumentException(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static char[] encodeHex(byte[] data) {
|
|
|
|
|
+ return encodeHex(data, true);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static char[] encodeHex(byte[] data, boolean toLowerCase) {
|
|
|
|
|
+ return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static char[] encodeHex(byte[] data, char[] toDigits) {
|
|
|
|
|
+ int l = data.length;
|
|
|
|
|
+ char[] out = new char[l << 1];
|
|
|
|
|
+ for (int i = 0, j = 0; i < l; i++) {
|
|
|
|
|
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
|
|
|
|
|
+ out[j++] = toDigits[0x0F & data[i]];
|
|
|
|
|
+ }
|
|
|
|
|
+ return out;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|