Josh Josh - 3 months ago 78
Android Question

Android AES 256-bit Encrypt data

So I've seen a lot of examples, and done a lot of googling, and looked at examples on Stack Overflow... and I need help. I've got an Android application and I'm storing username and passwords on the device, and I need to encrypt them AES 256. From looking at examples, this is what I have so far:

public class Security {
Cipher ecipher;
Cipher dcipher;

// 8-byte Salt
byte[] salt = {
(byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
(byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
};

// Iteration count
int iterationCount = 19;

public Security (String passPhrase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWithSHAAndAES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());

// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) { e.printStackTrace(); }
}

public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");

// Encrypt
byte[] enc = ecipher.doFinal(utf8);

// Encode bytes to base64 to get a string
return Base64.encodeToString(enc, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = Base64.decode(str, Base64.DEFAULT);

// Decrypt
byte[] utf8 = dcipher.doFinal(dec);

// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}


I'm trying to make it password based, so a user will create an account the first time using the username and password needed to communicate back to the server, and create a PIN that will be used as the key for these credentials stored in the database.

What I'm mainly concerned about is does this look secure? I know a fixed salt is bad, how do I fix that?

I know there's been like a billion questions about this, but I want someone to just come out and say "THIS IS SECURE" or "THIS IS NOT SECURE, CHANGE THIS"

Thanks!




EDIT:

So this is the code I have so far, and it seems to be working...
public class Security {

Cipher ecipher;
Cipher dcipher;
byte[] salt = new byte[8];
int iterationCount = 200;

public Security (String passPhrase) {
try {
// generate a random salt
SecureRandom random = new SecureRandom();
random.nextBytes(salt);

// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWithSHA256And256BitAES-CBC-BC").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());

// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) { e.printStackTrace(); }
}

public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");

// Encrypt
byte[] enc = ecipher.doFinal(utf8);

// Encode bytes to base64 to get a string
return Base64.encodeToString(enc, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = Base64.decode(str, Base64.DEFAULT);

// Decrypt
byte[] utf8 = dcipher.doFinal(dec);

// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public int getIterationCount() {
return iterationCount;
}

public String getSalt() {
return Base64.encodeToString(salt, Base64.DEFAULT);
}
}


I used this code to test it:

Security s = new Security(pinBox.getText().toString());
String encrypted = s.encrypt(passwordBox.getText().toString());
String decrypted = s.decrypt(encrypted);
builder.setMessage("pin: " + pinBox.getText().toString() + "\n" +
"password: " + passwordBox.getText().toString() + "\n" +
"encrypted: " + encrypted + "\n" +
"decrypted: " + decrypted + "\n" +
"salt: " + s.getSalt());


So I don't need to worry about an initialization vector? Or specifically hardcode a Cipher algorithm?

Thanks again!

Answer

EDIT: While the code below is correct, what you have is doing basically the same thing, with the IV derived from the password, so you don't have to store it separately.

Does your code work as expected? For the actual encryption/decryption you would want to use AES, most probably in CBC mode. Then you would need an IV, so it becomes something like this:

ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] enc = ecipher.doFinal(utf8);

Whether it is secure depends on what you are using this for. The purpose of the salt is to make it harder to brute force the passphrase: if it's random the attacker cannot use pre-generated passphrase tables (passphrase->key). If you are not too worried about this sort of attack, you might leave it fixed. If you decide to make it random, just store it with the encrypted data. Same with the IV.

Comments