Due to the way Android requires SD cards to be formatted in VFAT, it leaves a bit of a hole when it comes to security for files stored here. VFAT is an old standard that doesn’t support the access controls of Linux, so data stored here is unprotected. Because of this, all storage here is shared with all programs on the device. So storing sensitive information here isn’t going to be the best thing to do. With some devices having limited internal storage though, this might be your only option, or depending on what the data is, you may require large amounts of storage space.
One way around this is to simply encrypt the data from within your application. This can be achieved via the ‘javax.crypto’ library.
You have a few options, but an easy one is simply to encrypt any plain text data with a master password and AES 128. A basic implementation would look like this:
String seed = "SuperSecretPassword"; String plaintext = "This is insecure data!"; //API for generating symmetric cryptographic keys KeyGenerator keygen = KeyGenerator.getInstance("AES"); //Returns a new instance of SecureRandom that utilizes the //SHA1 algorithm. SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG"); //Reseeds this SecureRandom instance with the specified seed. secrand.setSeed(seed.getBytes()); //Initializes this KeyGenerator instance for the specified //key size (in bits) using the specified randomness source. keygen.init(128, secrand); //Generates a secret key. SecretKey seckey = keygen.generateKey(); //Returns the encoded form of the key. byte rawKey = seckey.getEncoded(); //Create a new SecretKeySpec for the key data and AES algorithm. SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES"); //Creates a new Cipher for the specified transformation. //The installed providers are searched in order for an //implementation of the specified transformation. //The first found provider providing the transformation //is used to create the cipher. Cipher cipher = Cipher.getInstance("AES"); //Initializes this cipher instance with the specified key. //The cipher is initialized for the specified operational //mode (one of: encryption, decryption, key wrapping or key unwrapping) //depending on opmode. //If this cipher instance needs any algorithm parameters //or random values that the specified key cannot provide, //the underlying implementation of this cipher is supposed //to generate the required parameters (using its provider or random values). cipher.init(Cipher.ENCRYPT_MODE, skeySpec); //Finishes a multi-part transformation (encryption or decryption). //Processes the bytes in input buffer, and any bytes that //have been buffered in previous update calls. byte encrypted = cipher.doFinal(plaintext.getBytes());
This can obviously be optimized, but the idea is there and shows how easy it is to encrypt data within Android. Don’t forget we will probably want to read this data back in, so decrypting is just as easy. Everything is the same except you pass in the DECRYPT_MODE in the cipher.init() call:
Now you have no reason not to ensure that your user’s data isn’t secure. I’m talking to you, app developers!