0
# Encryption and Security
1
2
Encryption capabilities for protecting sensitive data with AES encryption, key management, and secure key rotation. MMKV provides transparent encryption/decryption for all stored data when an encryption key is provided.
3
4
## Capabilities
5
6
### Key Management
7
8
Manage encryption keys for MMKV instances with secure key operations.
9
10
```java { .api }
11
/**
12
* Get the encryption key (no more than 16 bytes).
13
* @return The encryption key, or null if no encryption is used
14
*/
15
public String cryptKey();
16
17
/**
18
* Transform plain text into encrypted text, or vice versa by passing a null encryption key.
19
* You can also change existing crypt key with a different cryptKey.
20
* @param cryptKey The new encryption key (no more than 16 bytes)
21
* @return True if success, otherwise False
22
*/
23
public boolean reKey(String cryptKey);
24
25
/**
26
* Just reset the encryption key (will not encrypt or decrypt anything).
27
* Usually you should call this method after another process has reKey() the multi-process MMKV instance.
28
* @param cryptKey The new encryption key (no more than 16 bytes)
29
*/
30
public void checkReSetCryptKey(String cryptKey);
31
```
32
33
**Usage Example:**
34
35
```java
36
// Create encrypted MMKV instance
37
MMKV encryptedKv = MMKV.mmkvWithID("secure_data", MMKV.SINGLE_PROCESS_MODE, "my_secret_key");
38
39
// Store sensitive data (automatically encrypted)
40
encryptedKv.encode("credit_card", "4111-1111-1111-1111");
41
encryptedKv.encode("api_token", "secret_api_token_12345");
42
43
// Check current encryption key
44
String currentKey = encryptedKv.cryptKey();
45
Log.d("MMKV", "Current encryption key: " + (currentKey != null ? "[ENCRYPTED]" : "None"));
46
47
// Change encryption key (re-encrypts all data)
48
boolean success = encryptedKv.reKey("new_secret_key");
49
if (success) {
50
Log.d("MMKV", "Successfully changed encryption key");
51
} else {
52
Log.e("MMKV", "Failed to change encryption key");
53
}
54
55
// Remove encryption (converts to plain text)
56
boolean removed = encryptedKv.reKey(null);
57
if (removed) {
58
Log.d("MMKV", "Encryption removed, data is now plain text");
59
}
60
```
61
62
### Multi-Process Key Synchronization
63
64
Manage encryption keys across multiple processes safely.
65
66
```java { .api }
67
/**
68
* Just reset the encryption key (will not encrypt or decrypt anything).
69
* Usually you should call this method after another process has reKey() the multi-process MMKV instance.
70
* @param cryptKey The new encryption key (no more than 16 bytes)
71
*/
72
public void checkReSetCryptKey(String cryptKey);
73
```
74
75
**Usage Example:**
76
77
```java
78
// In main process: Create encrypted multi-process instance
79
MMKV mainKv = MMKV.mmkvWithID("shared_secure", MMKV.MULTI_PROCESS_MODE, "shared_key");
80
mainKv.encode("secure_config", "sensitive_value");
81
82
// Change encryption key in main process
83
boolean keyChanged = mainKv.reKey("new_shared_key");
84
85
// In other processes: Update to new key without re-encryption
86
MMKV otherKv = MMKV.mmkvWithID("shared_secure", MMKV.MULTI_PROCESS_MODE, "shared_key");
87
if (keyChanged) {
88
// Synchronize key change from main process
89
otherKv.checkReSetCryptKey("new_shared_key");
90
}
91
92
// Now both processes can access the data with the new key
93
String config = otherKv.decodeString("secure_config", "");
94
```
95
96
### Encryption Best Practices
97
98
```java
99
// 1. Use strong, randomly generated keys
100
SecureRandom random = new SecureRandom();
101
byte[] keyBytes = new byte[16]; // 16 bytes for AES-128
102
random.nextBytes(keyBytes);
103
String encryptionKey = Base64.encodeToString(keyBytes, Base64.NO_WRAP);
104
105
MMKV secureKv = MMKV.mmkvWithID("user_secrets", MMKV.SINGLE_PROCESS_MODE, encryptionKey);
106
107
// 2. Store encryption keys securely (Android Keystore)
108
private String getOrCreateEncryptionKey() {
109
// Use Android Keystore for key management
110
return KeystoreManager.getOrCreateKey("mmkv_encryption_key");
111
}
112
113
// 3. Separate encrypted and unencrypted data
114
MMKV publicKv = MMKV.mmkvWithID("public_data"); // No encryption for non-sensitive data
115
MMKV privateKv = MMKV.mmkvWithID("private_data", MMKV.SINGLE_PROCESS_MODE, getEncryptionKey());
116
117
publicKv.encode("app_version", "1.0.0");
118
publicKv.encode("user_preferences", "theme:dark");
119
120
privateKv.encode("auth_token", userToken);
121
privateKv.encode("biometric_data", biometricInfo);
122
123
// 4. Handle key rotation securely
124
private void rotateEncryptionKey(MMKV kv) {
125
String oldKey = kv.cryptKey();
126
String newKey = generateNewKey();
127
128
// Create backup before key rotation
129
boolean backupSuccess = MMKV.backupOneToDirectory(
130
kv.mmapID(),
131
getBackupDirectory(),
132
null
133
);
134
135
if (backupSuccess) {
136
boolean rotationSuccess = kv.reKey(newKey);
137
if (rotationSuccess) {
138
Log.d("MMKV", "Key rotation successful");
139
// Update stored key reference
140
updateStoredEncryptionKey(newKey);
141
} else {
142
Log.e("MMKV", "Key rotation failed, keeping old key");
143
}
144
}
145
}
146
147
// 5. Verify encryption status
148
private boolean isEncrypted(MMKV kv) {
149
return kv.cryptKey() != null;
150
}
151
152
// 6. Handle encryption errors gracefully
153
private void safeEncryptedOperation(MMKV kv, String key, String value) {
154
try {
155
if (isEncrypted(kv)) {
156
boolean success = kv.encode(key, value);
157
if (!success) {
158
Log.e("MMKV", "Failed to encrypt and store data");
159
// Handle encryption failure
160
}
161
} else {
162
Log.w("MMKV", "Storing sensitive data without encryption");
163
kv.encode(key, value);
164
}
165
} catch (Exception e) {
166
Log.e("MMKV", "Encryption operation failed", e);
167
// Handle error appropriately
168
}
169
}
170
```
171
172
### Encrypted Anonymous Shared Memory
173
174
Use encryption with Anonymous Shared Memory for secure inter-process communication.
175
176
```java { .api }
177
/**
178
* Create an MMKV instance base on Anonymous Shared Memory with encryption.
179
* @param context The context of Android App
180
* @param mmapID The unique ID of the MMKV instance
181
* @param size The maximum size of the underlying Anonymous Shared Memory
182
* @param mode The process mode of the MMKV instance
183
* @param cryptKey The encryption key (no more than 16 bytes)
184
* @return MMKV instance
185
* @throws RuntimeException if there's a runtime error
186
*/
187
public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, String cryptKey);
188
```
189
190
**Usage Example:**
191
192
```java
193
// Create encrypted ashmem instance for secure IPC
194
String sharedKey = getSharedEncryptionKey(); // Securely shared between processes
195
MMKV secureIPC = MMKV.mmkvWithAshmemID(
196
this,
197
"secure_ipc_channel",
198
1024 * 1024, // 1MB
199
MMKV.MULTI_PROCESS_MODE,
200
sharedKey
201
);
202
203
// Share encrypted data between processes
204
secureIPC.encode("sensitive_message", "confidential_data");
205
secureIPC.encode("auth_credentials", userCredentials);
206
207
// Data is automatically encrypted in memory and during IPC
208
ParcelableMMKV parcelable = new ParcelableMMKV(secureIPC);
209
// Pass parcelable to other process...
210
```
211
212
### Encryption Performance Considerations
213
214
```java
215
// Encryption impacts performance - use judiciously
216
public class EncryptionStrategy {
217
private MMKV publicKv;
218
private MMKV encryptedKv;
219
220
public EncryptionStrategy() {
221
// Fast access for non-sensitive data
222
publicKv = MMKV.mmkvWithID("public_data");
223
224
// Encrypted storage for sensitive data
225
encryptedKv = MMKV.mmkvWithID("sensitive_data", MMKV.SINGLE_PROCESS_MODE, getEncryptionKey());
226
}
227
228
public void storeUserPreference(String key, String value) {
229
// Non-sensitive preferences - use fast unencrypted storage
230
publicKv.encode(key, value);
231
}
232
233
public void storeAuthToken(String token) {
234
// Sensitive data - use encrypted storage
235
encryptedKv.encode("auth_token", token);
236
}
237
238
public void storeBiometricTemplate(byte[] template) {
239
// Highly sensitive data - use encrypted storage
240
encryptedKv.encode("biometric_template", template);
241
}
242
}
243
```
244
245
### Integration with Android Keystore
246
247
```java
248
// Secure key management using Android Keystore
249
public class SecureMMKVManager {
250
private static final String KEYSTORE_ALIAS = "mmkv_encryption_key";
251
252
public MMKV createSecureInstance(String instanceId) {
253
String encryptionKey = getOrCreateKeystoreKey();
254
return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
255
}
256
257
private String getOrCreateKeystoreKey() {
258
try {
259
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
260
keyStore.load(null);
261
262
if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
263
// Generate new key in Android Keystore
264
generateKeystoreKey();
265
}
266
267
// Retrieve and use key for MMKV encryption
268
return retrieveKeystoreKey();
269
} catch (Exception e) {
270
Log.e("MMKV", "Keystore operation failed", e);
271
return null;
272
}
273
}
274
275
private void generateKeystoreKey() {
276
// Implementation depends on Android version and requirements
277
// Use KeyGenerator with AndroidKeyStore provider
278
}
279
280
private String retrieveKeystoreKey() {
281
// Retrieve key from Android Keystore
282
// Convert to format suitable for MMKV
283
return "keystore_derived_key";
284
}
285
}
286
```
287
288
### Encryption Limitations and Considerations
289
290
```java
291
// Important encryption considerations:
292
293
// 1. Key length limit (16 bytes maximum)
294
String validKey = "1234567890123456"; // 16 bytes - OK
295
String invalidKey = "12345678901234567"; // 17 bytes - Will be truncated
296
297
// 2. Performance impact
298
long startTime = System.currentTimeMillis();
299
for (int i = 0; i < 1000; i++) {
300
encryptedKv.encode("key" + i, "value" + i);
301
}
302
long encryptedTime = System.currentTimeMillis() - startTime;
303
304
startTime = System.currentTimeMillis();
305
for (int i = 0; i < 1000; i++) {
306
plainKv.encode("key" + i, "value" + i);
307
}
308
long plainTime = System.currentTimeMillis() - startTime;
309
310
Log.d("MMKV", "Encrypted: " + encryptedTime + "ms, Plain: " + plainTime + "ms");
311
312
// 3. Incompatibility with compare-before-set optimization
313
MMKV encryptedKv = MMKV.mmkvWithID("test", MMKV.SINGLE_PROCESS_MODE, "key");
314
try {
315
encryptedKv.enableCompareBeforeSet(); // Will throw exception
316
} catch (RuntimeException e) {
317
Log.w("MMKV", "Compare-before-set not available with encryption");
318
}
319
320
// 4. Multi-process key synchronization complexity
321
// Ensure all processes use the same key and synchronize key changes
322
```