
Secure data in Android — Encrypting Large Data
This article is a part of “Secure data in Android” series:
- Encryption
- Encryption in Android (Part 1)
- Encryption in Android (Part 2)
- Encrypting Large Data
- Initialization Vector
- Key Invalidation
- Fingerprint
- Confirm Credentials
Those describes the “Secure data in Android” workshop topics. Sample application with full code snippets is available on GitHub.
In previous “Encryption in Android (Part 2)” article we spoke and tried to store, generate and manage an asynchronous keys, tried to encrypt and decrypt data using Android Key Store provider. This article will show you how to work with symmetric keys, how to use other Java providers and what key wrapping is.
Table of Contents
- Key Size
- What to do
- Default Providers
- Symmetric Keys
- Key Wrapping
- Usage Example
- Whats Next
- Security Tips
Key Size
Up to this moment, we already tried to encrypt small “Hello World” message. Lets try to encrypt larger one, larger then 250 symbols. Oops, IllegalBlockSizeException
:

RSA was not designed to work with large amount of data. You can process messages only with limited length, that depends on the key size. The bigger key is, the bigger message can be encrypted.
Be aware that using big key sizes will increase encryption time and may affect application performance. Avoid large data encryption on main application thread.
During the key generation process, you are able to specify the key size with setKeySize()
method:
It’s not possible to customize key size on API 18, in Android Key Store provider.
Default key size will be used if nothing was set manually. Default value may depend on provider and platform version. For RSA keys, in Android Key Store provider, it is 2048-bit.
Supported RSA keys sizes are: 512, 768, 1024, 2048, 3072, 4096. Check more details on supported algorithms and key sizes in documentation.
There’s a formula that you can use to calculate maximum RSA message length. For a n-bit RSA key (with PKCS1 padding) direct encryption works for arbitrary binary messages up to:
floor(n/8)-11 bytes
For a 1024-bit RSA key (128 bytes), you can use messages up to 117 bytes. So, the longest message we can get with RSA key can contain maximum 468 bytes (using 4096-bit key).
What to do
Pretty bad situation here, no possibility to use asymmetric key, and symmetric is available only from API 23+. And not so many options to choose from, to escape it:
- Create symmetric key with one of default Java Providers. Encrypt / decrypt message with it. Then encrypt this key raw data with
RSA
public key and save it somewhere to the disk. On decryption, get encrypted raw key data, decrypt it withRSA
private key and use it for message decryption. - Separate large message on parts and encrypt / decrypt each of the part individually.
I know what you just thought, about second option: “But we are using ECB mode already, it shell do this automatically, no ?” (see Encryption, Modes & Paddings).
Unfortunately, even with ECB mode (in Android Key Store provider implementation) RSA algorithm can process only one block of data, and if message is longer than one block size — it will crash.
To workaround this, you can manually separate data and work with it parts (simulate the ECB mode). But again, RSA is not designed for tasks like this.
It will be more secure to continue with first option.
You can check the implementation of second option here.
Default Providers
And the first thing we are going to do is to: generate symmetric key with one of default Java Providers.
The most common default Java provider in android is the cut version of BC
provider created by the popular third party Java cryptographic library provider — Bouncy Castle.
KeyGenerator
class is responsible for symmetric keys generation. Use one of its getInstance()
methods to create an instance of the AES
key.
You can fetch all available providers by calling Security.getProviders()
method:
Security.getProviders().forEach { logi(it.name) }
Note: available providers may vary across different platforms and vendors. If provider, that you are looking for, doesn’t exist on device, you can register your own (or some third party) provider by calling one of:
Security.addProvider(provider)// With this you are able to control the most preferred provider for
// your application. See Encryption in Android (Part 2)
Security.insertProviderAt(provider, position)
Or you can use getInstance(algorithm)
, that will return you the most preferred implementation, basing on your providers list.
val keyGenerator = KeyGenerator.getInstance("AES")// Providers List:
// 1. Android Key Store
// 2. Custom Provider
// 3. BC// Will output BC, if Android Key Store and Custom Provider do not // have AES algorithm.
keyGenerator.provider.name
Symmetric Keys
Basically, we do not have symmetric keys up to 23 API. But starting from M, we can use just one symmetric key from Android Key Store provider.
Use KeyGenerator
in pair with KeyGenParameterSpec
to create one. Also do not forget about Cipher
transformation that we will need for symmetric encryption:
Full source code is available here.
Key Wrapping
To protect our AES
key and safely save it on device public persistent storage (such as preferences, files or databases), we will use RSA
key, that is stored in Android Key Store, to encrypt and decrypt it. This process is also known as key wrapping.
For this, Cipher
has separate WRAP_MODE
and wrap()
method. To decrypt the key, use UNWRAP_MODE
and unwrap()
method.
Full source code is available here.
Usage Example
Lets try to encrypt and decrypt large message, using new approach:
Running on M and later, use one symmetric key :
Before M, use two, asymmetric and symmetric, keys:
And its not enough again, we’re able to encrypt message, but not to decrypt it.
Full source code is available here.
Whats Next
In next “Initialization Vector” article from “Secure data in Android” series:
Initialization Vector is a fixed-size input to a cryptographic primitive. It is typically required to be random or pseudorandom. The point of an IV is to tolerate the use of the same key to encrypt several distinct messages.
Security Tips
In general, the best approach for user data security is to minimize the use of APIs that access sensitive or personal user data.
Consider if there is a way that your application logic can be implemented using a hash or non-reversible form of the data
If your application accesses personal information such as passwords or user names, keep in mind that some jurisdictions may require you to provide a privacy policy explaining your use and storage of that data. Following the security best practice of minimizing access to user data may also simplify compliance.
More about encryption in Android you can learn at: