Using Password-based Encryption on Android

Why password-based encryption is needed


There are various reasons why one would want to encrypt data in an Android application: to make sure that files exported to shared storage (SD card, etc.) are not easily accessible to other apps; to encrypt sensitive information (such as authentication information for third-party services) stored by the app or to provide some sort of a DRM-like scheme where content is only accessible to users who own the appropriate key to access it. The Android SDK includes the Java Cryptography Extension (JCE) interfaces that provide easy access to common cryptographic operations, and all mainstream Android devices come with JCE providers that implement current symmetric encryption algorithms such as AES. Thus encrypting application data is fairly easily accomplished in Android by using standard APIs.

However, as in other systems, the harder part is not performing the actual cryptographic operations, but key management. If a key is stored along with the encrypted data, or even as a file private to the application, it is fairly easy to extract it, especially on a rooted device, and decrypt the data. The same is true for keys embedded in the application source code, even if they are somewhat obfuscated.There are generally two solutions to this problem: use a system service to protect the key, or don't store the key on the device at all, and have it entered each time access to protected data is needed. Android does provide a system key chain facility since version 4.0 (ICS), accessible via the KeyChain class. However, as discussed here, it can currently only be used to store RSA private keys and certificates. It is not generic enough to allow secure storage of arbitrary user data, including symmetric encryption keys. That leaves us with the other option: do not store encryption keys on the device. However, symmetric encryption keys are long random strings of bits, and it cannot be expected that someone would actually remember them, let alone enter them using an onscreen keyboard. On the other hand, users are quite familiar with passwords, and thus a way to generate strong cryptographic keys based on a humanly-manageable passwords is needed. There are standard and secure ways to do this, but let's first look at some non-standard, and generally not secure, but quite common ways of producing a key from a password. We will be using AES as the encryption algorithm for all examples, both because it is the current standard and is considered highly secure, and because it is practically the only symmetric algorithm guaranteed to be available on all Android versions. All key derivation methods presented here are implemented in the sample application (screenshot below, source is on github).


How not to generate a key from a password: padded password


Since a symmetric cipher's key has no structure and is just a string of bits with a predefined length, pretty much any string of sufficient length can be used to construct a key. For example, a 16 character password is easily converted to an 128 bit AES key by simply getting the raw bytes of the string in a particular encoding (such as UTF-8). While some implementations will reject known weak keys (such as ones composed only of zero bits), you will get a perfectly valid key, and will be able encrypt and decrypt with it. You might find 'helpful' sample code to achieve this on forums and the like that goes something like this:

int keyLength = 128; 
byte[] keyBytes = new byte[keyLength / 8];
// explicitly fill with zeros
Arrays.fill(keyBytes, (byte) 0x0);

// if password is shorter then key length, it will be zero-padded
// to key length
byte[] passwordBytes = password.getBytes("UTF-8");
int length = passwordeBytes.length < keyBytes.length ? passwordBytes.length
                    : keyBytes.length;
System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
SecretKey key = new SecretKeySpec(keyBytes, "AES");

Since most people wouldn't pick a 16 character password (let alone a 32 character one for a 256 bit key), the key 'derivation' code makes due with what is available: if the password doesn't have enough characters for a full key, it pads it with zeros bytes (or some other fixed value) to create a valid key. Here's whey this (or variations of it) code generates weak keys:
  • it limits the range of bytes used for the key to those encoding printable characters, thus effectively reducing the key size (out of 256 possible values for a byte, only 95 are printable ASCII characters). While there are 2^128 possible 128 bit AES keys, if only printable characters are used to construct the key, there are about 2^105 possible keys (equivalent to using a 105 bit AES key if such a key were possible).
  • if the password is shorter than the key size, the fixed padding further reduces the key space. For example, if the user picks up an 8-character password, that would result in roughly 2^52 possible keys. That is less even than DES's 56 bit key which has been considered weak for ages and can be brute-forced in less than a day using commercial hardware.
  • since the password is used as is to construct the key, the cost of generating a key 'derived' using this method is practically zero. Thus an attacker can easily generate a bunch of keys based on a list of common passwords and use them for a brute force attack. Since the number of keys (=common passwords) is limited, such an attack is very efficient, and if a poor password has been chosen, more often than not it will succeed.
You might think that no one would use such a naive key derivation scheme, but as it turns out, even fairly popular key manager apps are known to have used it. 

To sum this up: a symmetric encryption key needs to be random to provide sufficient security, and user-entered passwords are a poor source of randomness. Don't use them as is to construct a key.

How not to generate a key from a password: SHA1PRNG

Since, as mentioned above, a key needs to be random, it stands to reason to use a random number generator (RNG) to generate one. There are two flavours of those: "true" random generators that base their output on physical phenomena that are regarded as random (e.g., radioactive decay), and pseudo-random generators (PRNG) whose output is determined by a fairly short initialization value, know as a seed. By using a "truly random" (or close) value as the seed, PRNG's can produce sufficiently random output. To generate a random symmetric key based on a password we can use the password (in some form) to seed a PRNG, and thus produce predictable keys. There are standard key derivation algorithms based on this idea, which we will introduce later, but let's first look at some fairly common derivation code that implements this idea quite literally. You might come across code similar to this on 'code snippet' sites or even StackOverflow:

KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] seed = password.getBytes("UTF-8");
sr.setSeed(seed);
kgen.init(KEY_LENGTH, sr);
SecretKey key = kgen.generateKey();

This creates a random generator instance (SecureRandom) using the SHA1PRNG PRNG algorithm (which is currently the only RNG algorithm available on commercial Android devices), and seeds it with the password bytes. A KeyGenerator is then initialized with the SecureRandom instance, making sure that our password-seeded PRNG will be used when generating keys. Lastly, since a KeyGenerator for a symmetric algorithm simply requests a number of bits equal to the key size from the underlying (or system) RNG, we get a pseudorandom secret key based on the used password.

This scheme is not as bad as the previous one, since it produces a pseudorandom key, and doesn't reduce key size, but it is still not a good idea to use it. The first reason is the same as the last one for the padding method: generating a key is cheap and thus keys based on a password list can be readily generated, facilitating a brute force attack. How cheap: essentially the cost of a SHA-1 hash round, which is generally implemented in native code and is pretty fast. The second reason is that it is neither standard, nor portable. Even the JavaDoc entry for Android's SecureRandom says so: 'Not guaranteed to be compatible with the SHA1PRNG algorithm on the reference implementation.' The code above when run on Android and on a desktop system using Java SE produces the following 128 bit keys from the password string 'password'. Note that those may differ even between different Android platform or Java SE versions:

Android: 80A4495EF27725345AB3AFA08CE3A692
Java SE: 2470C0C06DEE42FD1618BB99005ADCA2

In short: while this method is slightly better than the previous one, it doesn't effectively prevent from brute force attacks and is not portable. Don't use it. Update: As of Android 4.2, the default SHA1PRNG provider is based on OpenSSL and this method doesn't work out of the box. If you need to use it for compatibility reasons, you have to explicitly specify the "Crypto" provider when getting a SecureRandom instance. But again, don't use it.

Proper key derivation: PKCS#5 and PKCS#12


A standard way to derive a symmetric encryption key from a password is defined in PKCS#5 (Public Key Cryptography Standard) published by RSA (the company). It was originally developed for generating DES keys, but the current versions (2.0 and draft of 2.1) extend it to be algorithm independent. Version 2.0 is also published as RFC 2898

The standard is based on two main ideas: using a salt to protect from table-assisted (pre-computed) dictionary attacks (salting) and using a large iteration count to make the key derivation computationally expensive (key stretching). As mentioned above, if a key is directly constructed from a password, it is easy to use pre-generated keys based on a list of common passwords for a brute force attack. By using a random 'salt' (so called because it is used to 'season' the password), multiple keys can be constructed based on the same password, and thus an attacker needs to generate a new key table for each salt value, making pre-computed table attacks much harder. A key point to note is that, while the salt is used along with the password to derive the key, unlike the password, it does not need to be kept secret. Its purpose is only to make a dictionary attack more difficult and it is often stored along with the encrypted data. The other approach applied in PKCS#5 is repeating the key derivation operation multiple times to produce the final key. This has little effect on legitimate use, where only one try is needed to derive the key from the correct password, but considerably slows down brute force attacks which try out multiple passwords in a row. 

PKCS#5 defines two key derivation functions, aptly named PBKDF1 and PBKDF2. PBKDF1 applies a hash function (MD5 or SHA-1) multiple times to the salt and password, feeding the output of each round to next one to produce the final output. The length of the final key is thus bound by the hash function output length (16 bytes for MD5, 20 bytes for SHA-1). PBKDF1 was originally designed for DES and its 16 or 20 byte output was enough to derive both a key (56 bits) and an initialization vector (64 bits) to encrypt in CBC mode. However, since this is not enough for algorithms with longer keys such as 3DES and AES, PBKDF1 shouldn't be used and is only left in the standard for backward compatibility reasons.
PBKDF2 doesn't suffer from the limitations of PBKDF1: it can produce keys of arbitrary length by generating as many blocks as needed to construct the key. To generate each block, a pseudorandom function is repeatedly applied to to the concatenation of the password, salt and block index. The pseudorandom function is configurable, but in practice HMAC-SHA1/256/384/512 are used, with HMAC-SHA1 being the most common. The password is used as the HMAC key and the salt takes the role of the message. Unlike PBKDF1, PBKDF2 doesn't specify how to derive an IV (initialization vector), so a randomly generated one is used.

Android's main JCE provider (Bouncy Castle) currently only supports PBKDF2WithHmacSHA1. Let's see how to use it to encrypt data with a 256 bit AES key derived from a password:

String password  = "password";
int iterationCount = 1000;
int keyLength = 256;
int saltLength = keyLength / 8; // same size as key output

SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
randomb.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
                    iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory
                    .getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize());
random.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));

Here we generate a random salt and use 1000 iterations to initialize the SecretKeyFactory which generates our key. The last step of key generation might be a little confusing though: we don't use the SecretKey produced by the factory as is, but use its encoded value to create a new SecretKeySpec object. That is done because the output of generateSecret() is actually a PBEKey instance which does not contain an initialized IV -- the Cipher object expects that from a PBEKey and will throw an exception if it is not present. The iteration count is as recommended by PKCS#5, but that standard was written a while ago, so you might want to increase it. For some perspective, AES 256 bit keys used to encrypt backups in Android 4.0 (ICS) are derived using 10,000 iterations and a 512 bit salt; iOS 4.0 also uses 10,000 iterations. The size of the salt should typically match the key size, for example 16 bytes when using a AES with a 128 bit key (128 / 8 = 16). Next we generate a random IV, initialize the cipher and output the cipher text.

To be able to decrypt the cipher text we need: the password, the iteration count, the salt and the IV. The password will be input by the user, and the iteration count is generally fixed (if you decide to make it variable, you need to store it along with the other parameters), so that leaves the salt and the IV. As discussed above, the salt is not a secret, and neither is the IV. Thus they can be saved along with the cipher text. If they are stored in a single blob/file, some sort of structure is needed to be able the parse it into its components. The sample app 'saves' the encrypted message to a Base64-encoded string and simply concatenates the salt, IV and cipher text delimited by "]" (any character not used Base64 will do). Decryption is very similar to the code above, except that the salt and IV are not generated randomly, but retrieved from the encrypted message.

String[] fields = ciphertext.split("]");
byte[] salt = fromBase64(fields[0]);
byte[] iv = fromBase64(fields[1]);
byte[] cipherBytes = fromBase64(fields[2]);
// as above
SecretKey key = deriveKeyPbkdf2(salt, password);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherBytes);
String plainrStr = new String(plaintext , "UTF-8");

Another standard key derivation mechanism is the one defined in PKCS#12. It doesn't appear to have a catchy name like the previous two, and is generally only used for backward compatibility with Microsoft's original PFX format. Like PBKDF2, it can also generate keys and IV's with arbitrary length. The Bouncy Castle provider supports a bunch of variations compatible with AES such as PBEWITHSHA256AND256BITAES-CBC-BC. The IV is generated based on the password and salt, so you don't have to generate and store it separately. The sample app includes a PKCS#12 key derivation mode, refer to the source code if you want to check how the implementation differs from the code above.

Derivation speed

We've mentioned that the first two 'derivation' methods are very fast and thus provide no real protection against table assisted brute force attacks. PKCS#5 and PKCS#12 compliant derivation methods deliberately make the process slower to impede brute force attacks. But what exactly is the speed difference? The following table summarizes the average computation times for the four presented derivation methods. Measurements were performed on a Nexus One (1GHz CPU) using 1000 iterations and a 8 byte salt for both PKCS#5 and PKCS#12.  As you can see, even a relatively small number of iterations matters: iteration based methods are at least an order of magnitude slower, which in this case is a good thing since it makes brute force attacks harder.

Password derivation speed on Nexus One
Padding SHA1PRNG PKCS#12 PBKDF2
< 1 [ms] 32 [ms] 160 [ms] 370 [ms]

Of course, the actual password matters a lot. If it is easily guessable, an attacker can easily find the encryption key, no matter how many iterations you used in your implementation. Thus regular password selection policies apply for password-based encryption (PBE) as well: do not use common dictionary words, mix lower and upper case letters with numbers and symbols. If possible, generate passwords automatically, and do not entrust users with password selection.

Conclusion

Using symmetric encryption on Android is quite straightforward, but since a general purpose, system-level secure storage is not available, key management could be complicated. One solution is not to store keys, but derive them from user-entered passwords. Password strings cannot be used as symmetric encryption keys as is, so some sort of key derivation is required. There are a few ways to derive keys, but most of them are not particularly secure. To ensure encryption keys are both sufficiently random and hard to brute force, you should use standard PBE key derivation methods. Of those, the one currently regarded secure and available on Android is PBKDF2WithHmacSHA1. In short: when deriving a key from a password use PBKDF2WithHmacSHA1, a sufficiently long randomly generated salt and an iteration count suitable for your app.

Comments

thematic said…
This comment has been removed by the author.
thematic said…
First, I want to say that I'm finding a lot of very helpful information on your site.

You mention that using the keychain API in ICS would not be a good fit for key management because it's only for storing certificates and their private key. But is it technically an option to use such a private key, either directly or through further derivations, to encrypt application data?

Also, and this is probably a beginners question, but I'm unclear on how the private key is made available to an application if there's a password required to access that key. And is the keychain insecure on a rooted phone or through bootloader hacking etc?
Nikolay Elenkov said…
Glad you find my posts useful.

Technically you could. Once you get a PrivateKey reference you can perform any cryptographic operation with it. However, it might not be a good fit because: 1) size of encrypted data is limited by key size; 2) anything you encrypt with the private key can be decrypted with the public key, so you'll have to keep that secret as well. The current interface is tailored for usage with signing and client authentication and not really encryption. You can use the lower interface to store secret keys directly though, although this can break in future versions (see the 'Storing application secrets' article).

As for protecting the key, it is encrypted only on disk. Once you unlock the phone the decryption key is stored in memory and keys are decrypted on demand, when you access them. It is 'secure' against rooting in the sense that having root access doesn't let you access the keys if you don't know the keystore unlock password. Additionally, on Jelly Bean (4.1) Android can take advantage of hardware to protect keys.

Read the trust/credential storage implementation articles if you are interested in the details.

HTH
thematic said…
Thanks for the reply.

Good point regarding the public key. I would use the private key as if it's a user-supplied password such that it would get used to derive the actual key for encryption operations. So I'm thinking that I would not need to protect the public key in that case and I could use it (along with its certificate and private key) for mutual authentication purposes, if needed.

I guess I'm still unclear on the inherent security of a keystore. If the private key is protected with a password, then my application would need to supply the password directly, right? keyStore.getKey(String alias, char[] password). But with ICS, the new KeyChain API (I read your post on the ICS trust store implementation :-) doesn't seem to require the password to obtain the private key, so I guess any application can use any identity in the store?
Nikolay Elenkov said…
This comment has been removed by the author.
Nikolay Elenkov said…
You seem to be confusing two different things: a standard Java keystore file and the system keystore (used through the KeyChain API) service in Android. A key store file is protected by a password and you need to supply it to use the private key(s) stored inside. The Android keystore (credential storage) password is automatically handled by the system -- your application never sees it and doesn't need to know it. In ICS it is the same as the device unlock password and the keystore is unlocked as soon as you unlock the device. In previous versions the password is independent from the device unlock one and you have to enter it separately.
thematic said…
OK. And regarding the new keychain API, before an application can access a certificate and its private key residing in the system key store, the user is prompted to allow or deny that access.
Nikolay Elenkov said…
Right, the first time you access a key you are prompted to grant access. The grant is then saved in a database, so you don't have to grant access manually again next time.
florian KORVER said…
Hi Nikolay, very interesting explanation!
Following a factory reset, I cannot access any more to the crypted data on my microSD.
I had selected the option "crypt only used memory", and it looks like the crypted data are still there (500 Mbytes are missing on my microSD).
If I go on Linux and manage to access to this crypted partition, will it be possible to get back the crypted data? I know the password I used to crypt the data...but I do not know what kind of software I could use to enter my password....
Thanks a lot if you have any response to this.

florian
Nikolay Elenkov said…
Unless you used the code in this article to encrypt the data, I cannot really help you. It seems you used some function of a custom Android version (ROM), refer to its documentation or support groups for help with this.
Rahul gaddam said…
Hi Nikolay,
I found this blog to be very much helpful. Thanks for explaining various options for using PBE.
But I do have very basic Question. For generating keys, we use user entered password, but my question is , where do we store this password for android app& how do we validate the user input?
where do we store the password(on the device?) & how is it guaranteed that its stored securely?

Thanks,
Rahul
Nikolay Elenkov said…
Not storing the password is kind of the point, as the first section explains. It is hard to store keys securely, that is why you derive them from something the user can remember -- the password. To make sure it remains a secret, you keep it off the device. You can validate user input by decrypting your data and checking format or contents.

For other options regarding protecting keys, see this article: http://nelenkov.blogspot.jp/2012/05/storing-application-secrets-in-androids.html
Rahul gaddam said…
Thanks Nikolay,
I guess Iam missing something!!
The point I am missing is, where do we store the user entered password? If we can store the password off the device & retrieve it for the validation, can't we do the same thing with the keys? then why do we need PBE?

Thanks,
Rahul
Nikolay Elenkov said…
It's only 'stored' in the user's head. You don't need the actual password to do validation, just some idea of what the encrypted data looks like. For example, if it is an XML file, if you decrypt it and it starts with '<?xml...', you can be reasonably sure that you decrypted it correctly, and therefore the provided password was correct.

What are you trying to do?
Rahul gaddam said…
Nikolay,
I am just trying to encrypt the sensitive data of the user(stored on device).
So the app prompts the password from the user(setup by user 1st time), & with that password , we generate the key everytime & use the same for decrypting the user data & encrypting the data while storing.
But, to generate the key, we need to validate the password entered by user.(if its same as the setup one). so how do we validate that password?

If that password is supposed to be stored off the device & retreieved for the validation, why can't we store the keys themeselves off the device & retrieve them to encrypt & decrypt ?

Thanks,
Nikolay Elenkov said…
Encrypt and store some known value the first time and use it for validation (say 'foobar'). The next time derive a key from the entered password, decrypt the known value and compare to 'foobar'. If it matches, the user has entered the same password as before.

The password is *not* supposed to be stored anywhere, but entered by the user. You cannot realistically expect a user to remember and enter a random 256-bit key, that is why you use a password instead. You might want to re-read the article, all of this is covered in the first half.
Rahul gaddam said…
Hi Nikolav,
Now I got it. Thanks again !!
but 1 quick question, that 'known value' is some hard-coded value in application.
isn't this a concern? I guess its still safe as it's just used to comparison.

Thanks
Nikolay Elenkov said…
Shouldn't be a problem it that's only thing you use it for. If someone reverses and/or modifies your app code, you will probably have bigger problems anyway.
Rahul Raj said…
Hi,

When I enter the invalid password & try to decrypt the encrypted known value, Iam getting below exception.
10-11 11:01:28.836: E/AndroidRuntime(527): Caused by: javax.crypto.BadPaddingException: pad block corrupted
10-11 11:01:28.836: E/AndroidRuntime(527): at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:715)

Can we safely assume that we get this exception if we try to decrypt the encrypted known string with the key generated from wrong password?

If I enter correct password, its working fine.

any thoughts?
BTW, Iam using PBKDF2_DERIVATION_ALGORITHM & is not supported in api level 8. any suggestions?
Nikolay Elenkov said…
Re: the padding error, this is usually the cause of this, but it is not the only cause, the cipher text might be otherwise corrupted. If the algorithm you want to use is not available, you need to bundle a library that implements it with your app, such as Spongy Castle (repackaged Bouncy Castle).
SeattleAndrew said…
So correct me if I'm wrong but if the Private keys are encrypted using the password, is there any added benefit to utilizing Android's Full Device Encryption for the keystore?
Nikolay Elenkov said…
Not quite sure what your point its here. The keystore is password protected, so you cannot get the keys if you don't know the password. That has nothing to do with whether the device uses full disk encryption (FDE). The benefits of FDE are that you cannot boot the device if you don't know the password and that someone cannot just copy the files off the device if they steal it, etc (assuming it's switched off). See the 'Changing the disk encryption password' post for some details.
Ramprasad said…
Hi,
You article provided me the right insight of encrypting and decrypting using the same password. I implemented the way you had mentioned in your article.
the Code although works, but when i continuously running throws "Given final block not properly padded" some times and others as well. I feel that i making a small error some where.
Can you help me to resolve the issue

import java.security.SecureRandom;
import java.security.spec.KeySpec;

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 MCrypt {

private int iterationCount = 10000;
private int saltLength = 8; // bytes; 64 bits
private int keyLength = 128;

public static void main(String[] args) throws Exception {
MCrypt mc = new MCrypt();
String encryptedData = mc.encrypt("1234");
MCrypt mc1 = new MCrypt();
System.out.println(new String(mc1.decrypt(new String(encryptedData),
"1234"), "UTF-8"));
}

public MCrypt() {
}

public String encrypt(String text) throws Exception {
if (text == null || text.length() == 0)
throw new Exception("Empty string");

byte[] encrypted = null;

SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
random.nextBytes(salt);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

byte[] iv = new byte[cipher.getBlockSize()];
random.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);

KeySpec keySpec = new PBEKeySpec(text.toCharArray(), salt,
iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");

cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);

encrypted = cipher.doFinal(text.getBytes("UTF-8"));

StringBuffer strBuf = new StringBuffer();
strBuf.append(new String(encrypted));
strBuf.append("]");
strBuf.append(new String(salt));
strBuf.append("]");
strBuf.append(new String(iv));

return new String(Base64.encodeBytes(strBuf.toString().getBytes()));
}

public byte[] decrypt(String code, String pwd) throws Exception {
if (code == null || code.length() == 0)
throw new Exception("Empty string");

String[] fields = new String(Base64.decode(code)).split("]");
byte[] cipherBytes = fields[0].getBytes();
byte[] salt = fields[1].getBytes();
byte[] iv = fields[2].getBytes();

KeySpec keySpec = new PBEKeySpec(pwd.toCharArray(), salt,
iterationCount, keyLength);
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);

byte[] decrypted = null;
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
decrypted = cipher.doFinal(cipherBytes);

return decrypted;
}
}
Nikolay Elenkov said…
This is better suited for StackOverflow, consider posting there. From first glance `new String(encrypted)`, etc. seems suspicious: you may get strange results when treating binary blobs as Java strings. Consider using Base64 encoding to covert to string, if you need to.
Ramprasad said…
Thanks a lot. The same continues to happen although I encoded it.
I will try to post in stackoverflow. Anyways thanks for the suggestion and the post.
Ramprasad said…
I resolved the issue encoded all of byte arrays then it worked. thanks for the hint.... initially tried for one of the encrypted pwd string....
Unknown said…
Thanks for the excellent guide.
Correct me if I'm wrong but I seem to understand that it's necessary condition that the password, used to derivate the key, is entered by the user and not stored in source code. But what if the users forgets his password?
Nikolay Elenkov said…
The idea is to show hot to properly derive a strong encryption key from a password. Of course, embedding the password in the app defeats the whole purpose -- if you did that, you might as well simply embed they key. If you want to implement password recovery, etc. it's up to you to come up with a suitable protocol. For example, you could keep two copies of a master key, each encrypted with a different password, etc. If they forget the 1st password, you can ask for the second one and recover the key if it matches.
Francesco Jo said…
Brilliant article. Helped me a lot.
Johann Blake said…
This comment has been removed by the author.
Johann Blake said…
I'm confused about a number of issues. Does Android use the open source code from Bouncy Castle? If so, I read somewhere that there is no guarantee that 256 bit encryption keys are available on all Android devices. Is that true?

I actually wrote code for an earlier version of Android that used this (even though you don't recommend using it):

SecureRandom.getInstance("SHA1PRNG");

When my app ran on a later version of Android, the decryption failed. From StackOverflow, I found out that Google had altered something in the latest release and I was required to use this instead:

SecureRandom.getInstance("SHA1PRNG", "Crypto");

Google essentially destroy backward compatibility when they did this and now I seriously question why anyone would want to use Android's built-in encryption. Makes no sense that your app encrypts it on one version of Android but breaks on another. And if they broke compatibility this time, what's to stop them from repeating this again?

Would it not be better to download the Bouncy Castle jar files and install them with your app and rely upon that instead? Then you wouldn't have to worry about Google breaking compatibility.

Another question. It seems that you are trying to stress the importance of generating a random key and not relying upon a user's password. Would just UUID to generate a random 128 bit number be the simplest and best solution? I use that function throughout my code.
Nikolay Elenkov said…
Generating keys from a seed by using SHA1PRNG is both broken and implementation-dependent. It only worked by chance until now, so there is really no reason to complain it broke -- this was never guaranteed to work in the first place. I only mention it here because it seems to be especially widespread on Android.

As for the JCE, etc. implementation on Android, yes it does use Bouncy Castle, but that is an implementation detail you cannot rely on. For example, in recent version a lot of the default algorithm implementations have been moved to native code (OpenSSL). If you really need to guarantee that your code works with the same JCE implementation across different Android versions, bundling Spongy Castle (a repackaged version of Bouncy Castle) is an option.

The UUID is random, and therefore not reproducible. It doesn't really work for key derivation. If you need a random key, it might work, but then you can simply (and had better) use SecureRandom.
Johann Blake said…
I was thinking of using the UUID for the key/password. The customer would never enter a password. They are forced to use a 128 bit key which should ensure a decent level of security. The app generates it and the user has to manually copy it from one device to another in order to decrypt it. As for Spongy Castle, I actually found that earlier today but was kind of shocked at its footprint of 1.5 meg. For a mobile app, that's a whopping size.
Nikolay Elenkov said…
You can't reasonably expect people to copy an UUID correctly. If you use a proper derivation method you will have a 'decent' key as well, of course that doesn't protect you from '1234' passwords, but you can enforce password policy.

As for Spongy Castle, etc. simply use ProGuard it will strip unused code. You are very unlikely to use the whole of SC, so most of it will be removed.
Johann Blake said…
Actually, manually writing down a 32 character like EEB418A0-7F3E-40ac-B36A-065055CB3A8E isn't that bad and considering it guarantees the maximum security, I don't think customers will complain. After all, millions of people have entered almost such long keys when installing Windows for years. Still, I'll do my best to avoid it. Thanks for the tip on ProGuard. I'll take a look at that.
Johann Blake said…
This comment has been removed by the author.
Flex said…
Hi, I'm trying to get 256-bit AES encryption working on android but cant seem to get it working, I've tried your example about and it wont work, any help would be greatly appreciated.

Thanks.
Nikolay Elenkov said…
This is a rather vague description. If you have any specific problems with the sample code, post here or open an issue on Github. Otherwise Stack Overflow might be more appropriate (you can link the question here if you want).
Unknown said…
Hm', I thought I have already posted here, but lets retry...

My real problem here was to figure out that the password in your sample-code is a passphrase and the plaintext is to be meant the password2be2encrypted. And that the passphrase resp. pin remains constant while the password change. It took my quite a while to figure out the distinction of the mixture of the term password.

Suggestion: password ==> passphraseOrPin and plaintext ==> password2be2encrypted

my 2 cents
Mike Perez said…
This is a useful post, but almost all of your code examples have variables that aren't defined anywhere in the document, so I'm left to guess what they're suppose to be. It'd be great if you perhaps put an entire 'encrypt' or 'decrypt' method.

Also I think 'randomb' should be changed to 'random' in one of them (CTRL-F it).
Nikolay Elenkov said…
Full code is available on Github, ctrl-f and then click.
This comment has been removed by the author.
http://stackoverflow.com/questions/22418901/password-encrytion-decryption-in-android
can you please check this link i have a question here and i used your code but something is wrong and i cant find the problem
Sergey Ivaskhin said…
Dear Nikolay,

I am currently working on the encryption program that encrypts XML strings which are stored in the files using AES. This post is also related to the AES encryption. However, the problem is that you generate an encryption key using password while I need to generate a secret key randomly. The idea is that I want to wrap symmetric key with public/private keys and store them in the KeyStore which became a public API from Jelly bean 4.3 (I have read this post as well). So I am not sure how to properly generate a secret key.

I am currently generating it like this:

public static SecretKey generateKey() throws NoSuchAlgorithmException {
// Generate a 256-bit key
final int outputKeyLength = 256;

SecureRandom secureRandom = new SecureRandom();
// Do *not* seed secureRandom! Automatically seeded from system entropy.
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(outputKeyLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
return key;
}


And then using secretKeySpec to initialize cipher.
byte[] convertedKeyToByte = Key.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(convertedKeyToByte, 0, convertedKeyToByte.length,
AESalgorithm);

Is it an inefficient way of generating and applying key?

Yours sincerely,
Sergejs

Nikolay Elenkov said…
Your project may be Android-related, but this is a generic question, and is not related to this post or Android. Please post such questions, on StackOverflow, security.stackexchange.com, etc.

Check the JCE specs for details, but you don't really need the whole SecretKeySpect part.
Sergey Ivaskhin said…
Dear Nikolay,

Thank you very much for your reply. I am sorry, I will not post unrelated questions anymore.

Kind Regards,
Sergejs
Brian B. said…
Nikolay,
Have you used DexGuard at all? What did you think?
Brian
arun venkatesan said…
Hi Nikolay,
What should be the "password" value for sms-based authentication apps (like Whats app)? Or for apps that use OAuth, where the token is generated on the server and needs to be stored securely on the device?

In such apps, the server sends a hash via API response and a code via sms, The user enters this code and this is sent to the server along with the original hash. If the values are equal, the user is authenticated.
Can the concat of the hash + code be used as the "password" here?

Also, since this generated key be used as an API key to distinguish one user from another, since the client and server can generate the same value?

-Thanx
Chaitanya Duse said…
Hi @Nikolay
facing similar scenario.. How to implement forgot password module for such an application. the most stringent way... ?
Maniac said…
Hi Nikolay,

A very good post with a lot of details. Thank you!
Where can I find the sample app?

Thanks!
adicode said…
Hi Nikolay,

I have tested your android PBE on a Nexus 5 running Lollipop. I have seen that there is an issue with the decryption of the data encrypted with a key generated using the PKCS#12 derivation method.

PBKDF2 on the other hand worked fine.

In Detail :

I have tested the same on a variety of devices - running Android 4.0, 4.2.2, 4.3, 4.4.2, 5.0.1, 5.0.2.

I have seen that PKCS#12 works on 4.2.2. It failed on 4.3, 4.4.2, 5.0.1, 5.0.2.

PBKDF2 works on all devices and versions.

Now, what might be the reason for this ? Does it have something to do with the change in the SecureRandom Implementation from BC to OpenSSL from Android 4.2. But that doesn't fit well since I have seen 4.2.2 working.

@Nikolay : Any clues ?

Last but not the least... Excellent Write up!. Thanks a lot for the effort.
Nikolay Elenkov said…
What exactly is the error/problem? It could be due to the switch to OpenSSL, but I don't think it has to do with SecureRandom. IIRC, it works fine on 4.4 at least.
Hi.
Can I use this method as PIN protection to encrypt a Symetric key previously generated and store on device? Like this:
1-Two users configures a password to encrypt/decrypt data to be transmitted
2-My app ask a pin code to protect app(user have to enter each time)
3-Encrypt initial key (1) with this pincode
4-User enter to app, input the pincode, decrypts the stored ecrypted key (1) and read data

How can I check if the decryption of key(1) was succesful?
dontai said…
Thanks for the explanation, Nikolay. I'm just getting into crypto and wanted to rely on PBE. your explanation really helped. One comment is that you use but not explain the term "IV". Maybe the first time you use IV you can also add "initialization vector" in parenthesis. For new readers this would help.
AnkuLua App said…
Hi Nikolay,
I used your code. It works perfectly between devices after post-4.2 Android.
There is one problem.
For code encrypted by post-4.2, pre-4.2 decryption generate error "pad block corrupted".
For code encrypted by pre-4.2, post-4.2 decryption generate error "error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt"

Please help

Popular posts from this blog

Password storage in Android M

Decrypting Android M adopted storage

Unpacking Android backups