package com.whatscloud.logic.security;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import android.util.Log;
import com.bugsense.trace.BugSenseHandler;
import com.whatscloud.config.debug.Logging;
import com.whatscloud.config.reporting.BugSense;
import com.whatscloud.logic.auth.User;
import com.whatscloud.utils.objects.Singleton;
import com.whatscloud.utils.strings.StringUtils;
import java.io.ByteArrayOutputStream;
import java.security.AlgorithmParameters;
import java.security.MessageDigest;
import java.security.spec.KeySpec;
import java.util.Random;
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;
public class AES
{
public static final int KEY_SIZE = 256;
public static final int ITERATIONS = 10;
public static final String SALT = "WhatsCloud";
public static final String HASHING_ALGORITHM = "SHA-256";
public static SecretKeySpec getAESSecret(Context context) throws Exception
{
//--------------------------------
// Get encryption key
//--------------------------------
String key = User.getEncryptionKey(context);
//--------------------------------
// Hash it
//--------------------------------
key = get256BitHash(key);
//--------------------------------
// Generate salt hash
//--------------------------------
String salt = get256BitHash(SALT);
//--------------------------------
// Use PBKDF2 key derivation
//--------------------------------
SecretKeyFactory derivation = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
//--------------------------------
// Supply the key, salt, iterations
// and key size
//--------------------------------
KeySpec spec = new PBEKeySpec(key.toCharArray(), salt.getBytes("UTF-8"), ITERATIONS, KEY_SIZE);
//--------------------------------
// Generate a secret
//--------------------------------
SecretKey secret = derivation.generateSecret(spec);
//--------------------------------
// Prepare it for AES
//--------------------------------
SecretKeySpec secretSpec = new SecretKeySpec(secret.getEncoded(), "AES");
//--------------------------------
// Return it
//--------------------------------
return secretSpec;
}
private static byte[] generateRandomIV()
{
//--------------------------------
// Get random generator
//--------------------------------
Random random = new Random();
//--------------------------------
// Prepare 16-bit iv
//--------------------------------
byte[] iv = new byte[16];
//--------------------------------
// Get 16 random bytes
//--------------------------------
random.nextBytes(iv);
//--------------------------------
// Return IV
//--------------------------------
return iv;
}
public static String encrypt(String message, Context context)
{
//--------------------------------
// No need to encrypt empty strings
// --------------------------------
if (StringUtils.stringIsNullOrEmpty(message))
{
return "";
}
try
{
//--------------------------------
// Get AES secret
//--------------------------------
SecretKeySpec secret = getAESSecret(context);
//--------------------------------
// Get AES cipher
//--------------------------------
Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
//--------------------------------
// Get generated IV
//--------------------------------
byte[] iv = generateRandomIV();
//--------------------------------
// Get generated IV spec
//--------------------------------
IvParameterSpec ivSpec = new IvParameterSpec(iv);
//--------------------------------
// Initialize it with secret
//--------------------------------
aes.init(aes.ENCRYPT_MODE, secret, ivSpec);
//--------------------------------
// Get AES cipher text
//--------------------------------
byte[] cipherText = aes.doFinal(message.getBytes("UTF-8"));
//--------------------------------
// Merge IV and cipher into array
//--------------------------------
byte[] encrypted = mergeByteArrays(iv, cipherText);
//--------------------------------
// Base64 encode the byte array
//--------------------------------
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
}
catch( Exception exc )
{
//--------------------------------
// Send to BugSense
//--------------------------------
BugSenseHandler.sendException(exc);
//--------------------------------
// Log the error
//--------------------------------
Log.e(Logging.TAG_NAME, "Encryption error: " + exc.getMessage());
}
//--------------------------------
// Something went wrong,
// return nothing
//--------------------------------
return "";
}
public static byte[] mergeByteArrays(byte[] array1, byte[] array2) throws Exception
{
//--------------------------------
// Create tmp byte stream
//--------------------------------
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//--------------------------------
// Write both arrays
//--------------------------------
outputStream.write( array1 );
outputStream.write( array2 );
//--------------------------------
// Merge them
//--------------------------------
return outputStream.toByteArray();
}
public static byte[] getEncryptionIV(byte[] data)
{
//--------------------------------
// Prepare IV byte array
//--------------------------------
byte[] iv = new byte[16];
//--------------------------------
// Copy first 16 bytes
//--------------------------------
System.arraycopy(data, 0, iv, 0, iv.length);
//--------------------------------
// Return IV
//--------------------------------
return iv;
}
public static byte[] getEncryptionCipher(byte[] data)
{
//--------------------------------
// Prepare cipher byte array
//--------------------------------
byte[] cipher = new byte[data.length - 16];
//--------------------------------
// Copy from 17th byte
//--------------------------------
System.arraycopy(data, 16, cipher, 0, cipher.length );
//--------------------------------
// Return cipher
//--------------------------------
return cipher;
}
public static String decrypt(String message, Context context)
{
//--------------------------------
// No need to decrypt empty strings
// --------------------------------
if (StringUtils.stringIsNullOrEmpty(message))
{
return "";
}
try
{
//--------------------------------
// Decode from Base64
//--------------------------------
byte[] data = Base64.decode(message.getBytes("UTF-8"), Base64.NO_WRAP);
//--------------------------------
// Get IV from byte array
//--------------------------------
byte[] iv = getEncryptionIV(data);
//--------------------------------
// Get cipher from byte array
//--------------------------------
byte[] cipherText = getEncryptionCipher(data);
//--------------------------------
// Get AES secret
//--------------------------------
SecretKeySpec secret = getAESSecret(context);
//--------------------------------
// Get AES cipher
//--------------------------------
Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
//--------------------------------
// Init cipher with secret & IV
//--------------------------------
aes.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
//--------------------------------
// Actually decrypt
//--------------------------------
cipherText = aes.doFinal(cipherText);
//--------------------------------
// Convert back to string
//--------------------------------
String decrypted = new String(cipherText, "UTF-8");
//--------------------------------
// Return decrypted string
//--------------------------------
return decrypted;
}
catch (Exception exc)
{
//--------------------------------
// Send to BugSense
//--------------------------------
BugSenseHandler.sendException(exc);
//--------------------------------
// Log the error
//--------------------------------
Log.e(Logging.TAG_NAME, "Decryption error: " + exc.getMessage());
}
//--------------------------------
// Something went wrong,
// return nothing
//--------------------------------
return "";
}
public static String get256BitHash(String input) throws Exception
{
//--------------------------------
// Prepare hashing algorithm
//--------------------------------
MessageDigest digest = MessageDigest.getInstance(HASHING_ALGORITHM);
//--------------------------------
// Actually hash the string
//--------------------------------
byte[] hash = digest.digest(input.getBytes("UTF-8"));
//--------------------------------
// Create string buffer
// to convert it to String
//--------------------------------
StringBuffer hexString = new StringBuffer();
//--------------------------------
// Traverse byte array
//--------------------------------
for (int i = 0; i < hash.length; i++)
{
//--------------------------------
// Convert int to hex
//--------------------------------
String hex = Integer.toHexString(0xff & hash[i]);
//--------------------------------
// Pad with 0
//--------------------------------
if ( hex.length() == 1 )
{
hexString.append('0');
}
//--------------------------------
// Append hex to string buffer
//--------------------------------
hexString.append(hex);
}
//--------------------------------
// Convert buffer to string
//--------------------------------
return hexString.toString();
}
public static String getRandomEncryptionKey()
{
//--------------------------------
// Get random generator
//--------------------------------
Random random = new Random();
//--------------------------------
// Generate random key
//--------------------------------
int key = 100000 + random.nextInt(900000);
//--------------------------------
// Convert to String
//--------------------------------
return key + "";
}
}