MMKV is an efficient, small, easy-to-use mobile key-value storage framework used in the WeChat application.
—
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.
Manage encryption keys for MMKV instances with secure key operations.
/**
* Get the encryption key (no more than 16 bytes).
* @return The encryption key, or null if no encryption is used
*/
public String cryptKey();
/**
* Transform plain text into encrypted text, or vice versa by passing a null encryption key.
* You can also change existing crypt key with a different cryptKey.
* @param cryptKey The new encryption key (no more than 16 bytes)
* @return True if success, otherwise False
*/
public boolean reKey(String cryptKey);
/**
* Just reset the encryption key (will not encrypt or decrypt anything).
* Usually you should call this method after another process has reKey() the multi-process MMKV instance.
* @param cryptKey The new encryption key (no more than 16 bytes)
*/
public void checkReSetCryptKey(String cryptKey);Usage Example:
// Create encrypted MMKV instance
MMKV encryptedKv = MMKV.mmkvWithID("secure_data", MMKV.SINGLE_PROCESS_MODE, "my_secret_key");
// Store sensitive data (automatically encrypted)
encryptedKv.encode("credit_card", "4111-1111-1111-1111");
encryptedKv.encode("api_token", "secret_api_token_12345");
// Check current encryption key
String currentKey = encryptedKv.cryptKey();
Log.d("MMKV", "Current encryption key: " + (currentKey != null ? "[ENCRYPTED]" : "None"));
// Change encryption key (re-encrypts all data)
boolean success = encryptedKv.reKey("new_secret_key");
if (success) {
Log.d("MMKV", "Successfully changed encryption key");
} else {
Log.e("MMKV", "Failed to change encryption key");
}
// Remove encryption (converts to plain text)
boolean removed = encryptedKv.reKey(null);
if (removed) {
Log.d("MMKV", "Encryption removed, data is now plain text");
}Manage encryption keys across multiple processes safely.
/**
* Just reset the encryption key (will not encrypt or decrypt anything).
* Usually you should call this method after another process has reKey() the multi-process MMKV instance.
* @param cryptKey The new encryption key (no more than 16 bytes)
*/
public void checkReSetCryptKey(String cryptKey);Usage Example:
// In main process: Create encrypted multi-process instance
MMKV mainKv = MMKV.mmkvWithID("shared_secure", MMKV.MULTI_PROCESS_MODE, "shared_key");
mainKv.encode("secure_config", "sensitive_value");
// Change encryption key in main process
boolean keyChanged = mainKv.reKey("new_shared_key");
// In other processes: Update to new key without re-encryption
MMKV otherKv = MMKV.mmkvWithID("shared_secure", MMKV.MULTI_PROCESS_MODE, "shared_key");
if (keyChanged) {
// Synchronize key change from main process
otherKv.checkReSetCryptKey("new_shared_key");
}
// Now both processes can access the data with the new key
String config = otherKv.decodeString("secure_config", "");// 1. Use strong, randomly generated keys
SecureRandom random = new SecureRandom();
byte[] keyBytes = new byte[16]; // 16 bytes for AES-128
random.nextBytes(keyBytes);
String encryptionKey = Base64.encodeToString(keyBytes, Base64.NO_WRAP);
MMKV secureKv = MMKV.mmkvWithID("user_secrets", MMKV.SINGLE_PROCESS_MODE, encryptionKey);
// 2. Store encryption keys securely (Android Keystore)
private String getOrCreateEncryptionKey() {
// Use Android Keystore for key management
return KeystoreManager.getOrCreateKey("mmkv_encryption_key");
}
// 3. Separate encrypted and unencrypted data
MMKV publicKv = MMKV.mmkvWithID("public_data"); // No encryption for non-sensitive data
MMKV privateKv = MMKV.mmkvWithID("private_data", MMKV.SINGLE_PROCESS_MODE, getEncryptionKey());
publicKv.encode("app_version", "1.0.0");
publicKv.encode("user_preferences", "theme:dark");
privateKv.encode("auth_token", userToken);
privateKv.encode("biometric_data", biometricInfo);
// 4. Handle key rotation securely
private void rotateEncryptionKey(MMKV kv) {
String oldKey = kv.cryptKey();
String newKey = generateNewKey();
// Create backup before key rotation
boolean backupSuccess = MMKV.backupOneToDirectory(
kv.mmapID(),
getBackupDirectory(),
null
);
if (backupSuccess) {
boolean rotationSuccess = kv.reKey(newKey);
if (rotationSuccess) {
Log.d("MMKV", "Key rotation successful");
// Update stored key reference
updateStoredEncryptionKey(newKey);
} else {
Log.e("MMKV", "Key rotation failed, keeping old key");
}
}
}
// 5. Verify encryption status
private boolean isEncrypted(MMKV kv) {
return kv.cryptKey() != null;
}
// 6. Handle encryption errors gracefully
private void safeEncryptedOperation(MMKV kv, String key, String value) {
try {
if (isEncrypted(kv)) {
boolean success = kv.encode(key, value);
if (!success) {
Log.e("MMKV", "Failed to encrypt and store data");
// Handle encryption failure
}
} else {
Log.w("MMKV", "Storing sensitive data without encryption");
kv.encode(key, value);
}
} catch (Exception e) {
Log.e("MMKV", "Encryption operation failed", e);
// Handle error appropriately
}
}Use encryption with Anonymous Shared Memory for secure inter-process communication.
/**
* Create an MMKV instance base on Anonymous Shared Memory with encryption.
* @param context The context of Android App
* @param mmapID The unique ID of the MMKV instance
* @param size The maximum size of the underlying Anonymous Shared Memory
* @param mode The process mode of the MMKV instance
* @param cryptKey The encryption key (no more than 16 bytes)
* @return MMKV instance
* @throws RuntimeException if there's a runtime error
*/
public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, String cryptKey);Usage Example:
// Create encrypted ashmem instance for secure IPC
String sharedKey = getSharedEncryptionKey(); // Securely shared between processes
MMKV secureIPC = MMKV.mmkvWithAshmemID(
this,
"secure_ipc_channel",
1024 * 1024, // 1MB
MMKV.MULTI_PROCESS_MODE,
sharedKey
);
// Share encrypted data between processes
secureIPC.encode("sensitive_message", "confidential_data");
secureIPC.encode("auth_credentials", userCredentials);
// Data is automatically encrypted in memory and during IPC
ParcelableMMKV parcelable = new ParcelableMMKV(secureIPC);
// Pass parcelable to other process...// Encryption impacts performance - use judiciously
public class EncryptionStrategy {
private MMKV publicKv;
private MMKV encryptedKv;
public EncryptionStrategy() {
// Fast access for non-sensitive data
publicKv = MMKV.mmkvWithID("public_data");
// Encrypted storage for sensitive data
encryptedKv = MMKV.mmkvWithID("sensitive_data", MMKV.SINGLE_PROCESS_MODE, getEncryptionKey());
}
public void storeUserPreference(String key, String value) {
// Non-sensitive preferences - use fast unencrypted storage
publicKv.encode(key, value);
}
public void storeAuthToken(String token) {
// Sensitive data - use encrypted storage
encryptedKv.encode("auth_token", token);
}
public void storeBiometricTemplate(byte[] template) {
// Highly sensitive data - use encrypted storage
encryptedKv.encode("biometric_template", template);
}
}// Secure key management using Android Keystore
public class SecureMMKVManager {
private static final String KEYSTORE_ALIAS = "mmkv_encryption_key";
public MMKV createSecureInstance(String instanceId) {
String encryptionKey = getOrCreateKeystoreKey();
return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
}
private String getOrCreateKeystoreKey() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
// Generate new key in Android Keystore
generateKeystoreKey();
}
// Retrieve and use key for MMKV encryption
return retrieveKeystoreKey();
} catch (Exception e) {
Log.e("MMKV", "Keystore operation failed", e);
return null;
}
}
private void generateKeystoreKey() {
// Implementation depends on Android version and requirements
// Use KeyGenerator with AndroidKeyStore provider
}
private String retrieveKeystoreKey() {
// Retrieve key from Android Keystore
// Convert to format suitable for MMKV
return "keystore_derived_key";
}
}// Important encryption considerations:
// 1. Key length limit (16 bytes maximum)
String validKey = "1234567890123456"; // 16 bytes - OK
String invalidKey = "12345678901234567"; // 17 bytes - Will be truncated
// 2. Performance impact
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
encryptedKv.encode("key" + i, "value" + i);
}
long encryptedTime = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
plainKv.encode("key" + i, "value" + i);
}
long plainTime = System.currentTimeMillis() - startTime;
Log.d("MMKV", "Encrypted: " + encryptedTime + "ms, Plain: " + plainTime + "ms");
// 3. Incompatibility with compare-before-set optimization
MMKV encryptedKv = MMKV.mmkvWithID("test", MMKV.SINGLE_PROCESS_MODE, "key");
try {
encryptedKv.enableCompareBeforeSet(); // Will throw exception
} catch (RuntimeException e) {
Log.w("MMKV", "Compare-before-set not available with encryption");
}
// 4. Multi-process key synchronization complexity
// Ensure all processes use the same key and synchronize key changesInstall with Tessl CLI
npx tessl i tessl/maven-com-tencent--mmkv-shared