0
# Encryption and Security
1
2
Built-in encryption capabilities with key management, supporting AES encryption for secure data storage and key transformation operations.
3
4
## Capabilities
5
6
### Encryption Key Management
7
8
Manage encryption keys for secure data storage with up to 16-byte keys.
9
10
```java { .api }
11
/**
12
* Get the current encryption key.
13
* @return The encryption key string, or null if not encrypted
14
*/
15
public String cryptKey();
16
17
/**
18
* Transform plain text into encrypted text, or vice versa.
19
* Can also change existing encryption key with a different key.
20
* @param cryptKey The new encryption key (no more than 16 bytes), null to remove encryption
21
* @return True if successful, false otherwise
22
*/
23
public boolean reKey(String cryptKey);
24
25
/**
26
* Reset the encryption key without encrypting or decrypting anything.
27
* Usually called 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
import com.tencent.mmkv.MMKV;
37
38
// Create encrypted MMKV instance
39
MMKV secureKv = MMKV.mmkvWithID("secure_data", MMKV.SINGLE_PROCESS_MODE, "my-secret-key");
40
41
// Check current encryption key
42
String currentKey = secureKv.cryptKey(); // "my-secret-key"
43
44
// Store sensitive data
45
secureKv.encode("password", "user-password");
46
secureKv.encode("api_key", "secret-api-key");
47
secureKv.encode("token", "access-token-12345");
48
49
// Change encryption key (re-encrypts all data)
50
boolean success = secureKv.reKey("new-secret-key");
51
if (success) {
52
Log.d("MMKV", "Successfully changed encryption key");
53
} else {
54
Log.e("MMKV", "Failed to change encryption key");
55
}
56
57
// Remove encryption (converts to plain text)
58
secureKv.reKey(null);
59
60
// Add encryption to existing plain text data
61
secureKv.reKey("another-secret-key");
62
```
63
64
### Multi-Process Encryption Synchronization
65
66
Handle encryption key changes across multiple processes safely.
67
68
```java { .api }
69
/**
70
* Reset the encryption key without encrypting/decrypting.
71
* Use this when another process has changed the encryption key.
72
* @param cryptKey The new encryption key that was set by another process
73
*/
74
public void checkReSetCryptKey(String cryptKey);
75
```
76
77
**Usage Example:**
78
79
```java
80
// In Process A - change encryption key
81
MMKV multiProcessKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE, "old-key");
82
boolean changed = multiProcessKv.reKey("new-key");
83
84
// In Process B - synchronize the key change
85
MMKV sharedKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE, "old-key");
86
if (changed) {
87
// Reset to the new key without re-encrypting
88
sharedKv.checkReSetCryptKey("new-key");
89
// Now Process B can access the data with the new key
90
}
91
```
92
93
### Encryption Status Check
94
95
Check if an MMKV instance has encryption enabled.
96
97
```java { .api }
98
/**
99
* Check if encryption is enabled for this instance.
100
* @return True if encryption is enabled, false otherwise
101
*/
102
private boolean isEncryptionEnabled();
103
```
104
105
**Usage Example:**
106
107
```java
108
MMKV plainKv = MMKV.mmkvWithID("plain_data");
109
MMKV encryptedKv = MMKV.mmkvWithID("encrypted_data", MMKV.SINGLE_PROCESS_MODE, "secret");
110
111
// Note: isEncryptionEnabled() is private, but you can check via cryptKey()
112
boolean plainHasEncryption = (plainKv.cryptKey() != null); // false
113
boolean encryptedHasEncryption = (encryptedKv.cryptKey() != null); // true
114
115
Log.d("MMKV", "Plain KV encrypted: " + plainHasEncryption);
116
Log.d("MMKV", "Encrypted KV encrypted: " + encryptedHasEncryption);
117
```
118
119
### Security Best Practices
120
121
Best practices for using MMKV encryption securely.
122
123
**Key Generation Example:**
124
125
```java
126
import java.security.SecureRandom;
127
import java.util.Base64;
128
129
public class MMKVSecurity {
130
131
/**
132
* Generate a secure random encryption key.
133
* @return A base64-encoded encryption key suitable for MMKV
134
*/
135
public static String generateSecureKey() {
136
SecureRandom random = new SecureRandom();
137
byte[] keyBytes = new byte[16]; // 16 bytes = 128 bits
138
random.nextBytes(keyBytes);
139
return Base64.getEncoder().encodeToString(keyBytes);
140
}
141
142
/**
143
* Create an encrypted MMKV instance with a secure key.
144
* @param instanceId The unique ID for the MMKV instance
145
* @param encryptionKey The encryption key (store securely!)
146
* @return The encrypted MMKV instance
147
*/
148
public static MMKV createSecureMMKV(String instanceId, String encryptionKey) {
149
return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
150
}
151
}
152
153
// Usage
154
String secureKey = MMKVSecurity.generateSecureKey();
155
// Store secureKey in Android Keystore or other secure location
156
MMKV secureStorage = MMKVSecurity.createSecureMMKV("user_credentials", secureKey);
157
```
158
159
### Android Keystore Integration
160
161
Example of integrating MMKV encryption with Android Keystore for enhanced security.
162
163
**Usage Example:**
164
165
```java
166
import android.security.keystore.KeyGenParameterSpec;
167
import android.security.keystore.KeyProperties;
168
import java.security.KeyStore;
169
import javax.crypto.KeyGenerator;
170
import javax.crypto.SecretKey;
171
import javax.crypto.Cipher;
172
import android.util.Base64;
173
174
public class MMKVKeystoreHelper {
175
176
private static final String KEYSTORE_ALIAS = "MMKVMasterKey";
177
private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
178
179
/**
180
* Generate or retrieve a master key from Android Keystore.
181
* @return The master key for encrypting MMKV keys
182
*/
183
public static SecretKey getMasterKey() throws Exception {
184
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
185
keyStore.load(null);
186
187
if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
188
// Generate new key
189
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE);
190
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
191
KEYSTORE_ALIAS,
192
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
193
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
194
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
195
.build();
196
keyGenerator.init(keyGenParameterSpec);
197
return keyGenerator.generateKey();
198
} else {
199
// Retrieve existing key
200
return (SecretKey) keyStore.getKey(KEYSTORE_ALIAS, null);
201
}
202
}
203
204
/**
205
* Create an MMKV instance with Keystore-protected encryption.
206
* @param instanceId The unique ID for the MMKV instance
207
* @return The encrypted MMKV instance
208
*/
209
public static MMKV createKeystoreProtectedMMKV(String instanceId) throws Exception {
210
// Generate MMKV encryption key
211
SecureRandom random = new SecureRandom();
212
byte[] mmkvKey = new byte[16];
213
random.nextBytes(mmkvKey);
214
215
// Encrypt the MMKV key with Keystore key
216
SecretKey masterKey = getMasterKey();
217
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
218
cipher.init(Cipher.ENCRYPT_MODE, masterKey);
219
byte[] encryptedKey = cipher.doFinal(mmkvKey);
220
221
// Store encrypted key in SharedPreferences or other location
222
// (In real app, you'd save this encrypted key for later retrieval)
223
224
// Create MMKV with the raw key
225
String mmkvKeyString = Base64.encodeToString(mmkvKey, Base64.NO_WRAP);
226
return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, mmkvKeyString);
227
}
228
}
229
230
// Usage
231
try {
232
MMKV keystoreProtectedKv = MMKVKeystoreHelper.createKeystoreProtectedMMKV("secure_user_data");
233
keystoreProtectedKv.encode("sensitive_info", "highly confidential data");
234
} catch (Exception e) {
235
Log.e("MMKV", "Failed to create keystore-protected MMKV", e);
236
}
237
```
238
239
### Performance Considerations
240
241
Encryption impact on performance and optimization strategies.
242
243
**Usage Example:**
244
245
```java
246
public class MMKVPerformanceExample {
247
248
public void demonstrateEncryptionPerformance() {
249
// Plain MMKV - fastest
250
MMKV plainKv = MMKV.mmkvWithID("plain_data");
251
252
// Encrypted MMKV - slower due to encryption overhead
253
MMKV encryptedKv = MMKV.mmkvWithID("encrypted_data", MMKV.SINGLE_PROCESS_MODE, "key");
254
255
// Disable compare-before-set for encrypted instances (it's inefficient)
256
// encryptedKv.enableCompareBeforeSet(); // Don't do this with encryption
257
258
long startTime, endTime;
259
String testData = "This is test data for performance measurement";
260
261
// Measure plain storage performance
262
startTime = System.nanoTime();
263
for (int i = 0; i < 1000; i++) {
264
plainKv.encode("key_" + i, testData);
265
}
266
endTime = System.nanoTime();
267
long plainTime = endTime - startTime;
268
269
// Measure encrypted storage performance
270
startTime = System.nanoTime();
271
for (int i = 0; i < 1000; i++) {
272
encryptedKv.encode("key_" + i, testData);
273
}
274
endTime = System.nanoTime();
275
long encryptedTime = endTime - startTime;
276
277
Log.d("MMKV", String.format("Plain: %d ns, Encrypted: %d ns, Overhead: %.2fx",
278
plainTime, encryptedTime, (double)encryptedTime / plainTime));
279
}
280
}
281
```
282
283
### Encryption Migration
284
285
Migrating existing plain text data to encrypted storage.
286
287
**Usage Example:**
288
289
```java
290
public class MMKVEncryptionMigration {
291
292
/**
293
* Migrate existing plain MMKV data to encrypted storage.
294
* @param instanceId The MMKV instance ID to migrate
295
* @param encryptionKey The encryption key to use
296
* @return True if migration successful
297
*/
298
public static boolean migrateToEncrypted(String instanceId, String encryptionKey) {
299
try {
300
// Get existing plain instance
301
MMKV plainKv = MMKV.mmkvWithID(instanceId);
302
303
// Get all existing keys and values
304
String[] allKeys = plainKv.allKeys();
305
if (allKeys == null || allKeys.length == 0) {
306
// No data to migrate, just enable encryption
307
return plainKv.reKey(encryptionKey);
308
}
309
310
// Create temporary encrypted instance
311
String tempId = instanceId + "_temp_encrypted";
312
MMKV tempEncryptedKv = MMKV.mmkvWithID(tempId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
313
314
// Copy all data to encrypted instance
315
for (String key : allKeys) {
316
// Try different data types (you might want to track types separately)
317
try {
318
String stringValue = plainKv.decodeString(key);
319
if (stringValue != null) {
320
tempEncryptedKv.encode(key, stringValue);
321
continue;
322
}
323
} catch (Exception ignored) {}
324
325
try {
326
int intValue = plainKv.decodeInt(key, Integer.MIN_VALUE);
327
if (intValue != Integer.MIN_VALUE) {
328
tempEncryptedKv.encode(key, intValue);
329
continue;
330
}
331
} catch (Exception ignored) {}
332
333
// Add other type checks as needed...
334
335
// Fallback to bytes
336
byte[] bytesValue = plainKv.decodeBytes(key);
337
if (bytesValue != null) {
338
tempEncryptedKv.encode(key, bytesValue);
339
}
340
}
341
342
// Replace original with encrypted version
343
plainKv.clearAll();
344
long importCount = plainKv.importFrom(tempEncryptedKv);
345
boolean reKeySuccess = plainKv.reKey(encryptionKey);
346
347
// Clean up temporary instance
348
MMKV.removeStorage(tempId);
349
350
return reKeySuccess && importCount > 0;
351
352
} catch (Exception e) {
353
Log.e("MMKV", "Failed to migrate to encrypted storage", e);
354
return false;
355
}
356
}
357
}
358
359
// Usage
360
boolean migrated = MMKVEncryptionMigration.migrateToEncrypted("user_data", "secret-key");
361
if (migrated) {
362
Log.d("MMKV", "Successfully migrated to encrypted storage");
363
} else {
364
Log.e("MMKV", "Failed to migrate to encrypted storage");
365
}
366
```
367
368
## Security Notes
369
370
1. **Key Length**: Encryption keys must be no more than 16 bytes (128-bit AES)
371
2. **Key Storage**: Store encryption keys securely using Android Keystore or other secure mechanisms
372
3. **Performance**: Encryption adds computational overhead; use only for sensitive data
373
4. **Multi-Process**: Use `checkReSetCryptKey()` to synchronize key changes across processes
374
5. **Compare-Before-Set**: Disable `enableCompareBeforeSet()` for encrypted instances as it's inefficient
375
6. **Key Rotation**: Use `reKey()` to change encryption keys and re-encrypt existing data
376
7. **Migration**: Plan migration strategy when adding encryption to existing plain text data