package com.jdroid.android.utils;
import android.content.SharedPreferences;
import android.util.Base64;
import com.jdroid.java.exception.UnexpectedException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AndroidEncryptionUtils {
private static final String BASE64_KEY = "base64Key";
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
private static final String ALGORITHM = "AES";
private static final String SHA_ALGORITHM = "SHA-1";
private static final String UTF_8 = "UTF-8";
private static String base64Key;
/**
* Returns the data encrypted. Avoid calling this method on the UI thread if possible, since it may access to shared
* preferences.
*
* @param cleartext
* @return encrypted data
*/
public static String encrypt(String cleartext) {
if (cleartext != null) {
byte[] result = doFinal(Base64.decode(getBase64Key(), Base64.DEFAULT), Cipher.ENCRYPT_MODE,
cleartext.getBytes());
return Base64.encodeToString(result, Base64.DEFAULT);
}
return null;
}
/**
* Returns the original data. Avoid calling this method on the UI thread if possible, since it may access to shared
* preferences.
*
* @param base64Encrypted
* @return the original data
*/
public static String decrypt(String base64Encrypted) {
if (base64Encrypted != null) {
byte[] enc = Base64.decode(base64Encrypted, Base64.DEFAULT);
byte[] result = doFinal(Base64.decode(getBase64Key(), Base64.DEFAULT), Cipher.DECRYPT_MODE, enc);
return new String(result);
}
return null;
}
private static byte[] doFinal(byte[] raw, int opMode, byte[] input) {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITHM);
cipher.init(opMode, skeySpec);
return cipher.doFinal(input);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
throw new UnexpectedException(e);
} catch (InvalidKeyException e) {
throw new UnexpectedException(e);
}
}
/**
* Returns the encryption key stored on {@link SharedPreferences}. If the key is already on memory, it doesn't access the file system.
* You shouldn't call this method in the UI thread.
*/
private static String getBase64Key() {
if (base64Key == null) {
base64Key = SharedPreferencesHelper.get().loadPreference(BASE64_KEY);
if (base64Key == null) {
base64Key = generateBase64Key();
SharedPreferencesHelper.get().savePreference(BASE64_KEY, base64Key);
}
}
return base64Key;
}
private static String generateBase64Key() {
final int outputKeyLength = 128;
try {
SecureRandom secureRandom = new SecureRandom();
KeyGenerator keyGenerator;
keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(outputKeyLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
return Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
} catch (NoSuchAlgorithmException e) {
throw new UnexpectedException(e);
}
}
private static String toHexEncode(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
int low = aByte & 0xF;
int high = (aByte >>> 4) & 0xF;
sb.append(Character.forDigit(high, 16));
sb.append(Character.forDigit(low, 16));
}
return sb.toString();
}
/**
* Generates the SHA hash for the input string.
*
* @param text the input string to hash
* @return the hash for the input string in hexadecimal encoding
*/
public static String generateShaHash(String text) {
try {
MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
byte[] bytes = text.getBytes(UTF_8);
bytes = digest.digest(bytes);
return toHexEncode(bytes);
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
}