Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /
Realm Files

Cifra un Realm - Java SDK

You can encrypt your realms to ensure that the data stored to disk can't be read outside of your application. You encrypt the realm file on disk with AES-256 + SHA-2 by supplying a 64-byte encryption key when opening a realm.

Realm cifra y descifra datos de forma transparente con estándares Cifrado AES-256Utilizando los primeros 256 bits de la clave de cifrado de bits 512dada. Realm utiliza los otros 256 bits de la 512clave de cifrado de bits para validar la integridad mediante un código de autenticación de mensajes basado en hash (HMAC).

Advertencia

Do not use cryptographically-weak hashes for realm encryption keys. For optimal security, we recommend generating random rather than derived encryption keys.

The following are key impacts to consider when encrypting a realm.

Debe proporcionar la misma llave de cifrado a RealmConfiguration.Builder.encryptionKey() cada vez que se abre el realm. Si no proporciona una clave o especifica la clave incorrecta para un realm cifrado, el SDK de Realm lanzará un error.

Las aplicaciones deben almacenar la llave de cifrado en el Android KeyStore para que otras aplicaciones no puedan leer la llave.

Las lecturas y escrituras en los "realms" cifrados pueden ser hasta un 10% más lentas que en los "realms" sin cifrar.

Puede encriptar un realm sincronizado.

Realm only encrypts the data on the device and stores the data unencrypted in your Atlas data source. Any users with authorized access to the Atlas data source can read the data, but the following still applies:

  • Users must have the correct read permissions to read the synced data.

  • Data stored in Atlas is always encrypted at a volume (disk) level.

  • The transfer between client and server is always fully encrypted.

You can also enable Customer Key Management to encrypt stored Atlas data using your cloud provider's key (e.g. AWS KMS, Azure Key Vault, Google Cloud KMS).

If you need unique keys for each user of your application, you can use an OAuth provider or use one of the Realm authentication providers and an authentication trigger to create a 64-bit key and store that key in a user object.

Changed in version 10.14.0.

A partir de la versión 10.14.0 del SDK de Realm Java, Realm admite abrir el mismo Realm cifrado en varios procesos.

If your app uses Realm Java SDK version 10.14.0 or earlier, attempting to open an encrypted realm from multiple processes throws this error: Encrypted interprocess sharing is currently unsupported.

The following steps describe the recommended way to use the Android KeyStore for encryption with Realm:

  1. Genere una clave RSA asimétrica que Android pueda almacenar y recuperar de forma segura mediante Android KeyStore.

    Nota

    Versión de Android M y superiores: Seguridad del almacén de claves

    Versions M and above require user PIN or fingerprint to unlock the KeyStore.

  2. Genere una clave simétrica (AES) que pueda utilizar para cifrar el reino.

  3. Cifre la clave AES simétrica utilizando su clave RSA privada.

  4. Almacena la clave AES cifrada en el sistema de archivos (en un SharedPreferences, por ejemplo).

Cuando se deba usar el realm cifrado:

  1. Retrieve your encrypted AES key.

  2. Descifre su clave AES cifrada utilizando la clave RSA pública.

  3. Utilice la clave AES descifrada en el RealmConfiguration para abrir el realm cifrado.

Tip

Para ver un ejemplo de extremo a extremo de almacenamiento y reutilización de claves de cifrado, consulte el proyecto de ejemplo store_password, que demuestra la API de huellas dactilares.

El siguiente código demuestra cómo generar y almacenar de forma segura una clave de cifrado para un reino:

// Create a key to encrypt a realm and save it securely in the keystore
public byte[] getNewKey() {
// open a connection to the android keystore
KeyStore keyStore;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
Log.v("EXAMPLE", "Failed to open the keystore.");
throw new RuntimeException(e);
}
// create a securely generated random asymmetric RSA key
byte[] realmKey = new byte[Realm.ENCRYPTION_KEY_LENGTH];
new SecureRandom().nextBytes(realmKey);
// create a cipher that uses AES encryption -- we'll use this to encrypt our key
Cipher cipher;
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Log.e("EXAMPLE", "Failed to create a cipher.");
throw new RuntimeException(e);
}
// generate secret key
KeyGenerator keyGenerator;
try {
keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
Log.e("EXAMPLE", "Failed to access the key generator.");
throw new RuntimeException(e);
}
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(
"realm_key",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(
AUTH_VALID_DURATION_IN_SECOND)
.build();
try {
keyGenerator.init(keySpec);
} catch (InvalidAlgorithmParameterException e) {
Log.e("EXAMPLE", "Failed to generate a secret key.");
throw new RuntimeException(e);
}
keyGenerator.generateKey();
// access the generated key in the android keystore, then
// use the cipher to create an encrypted version of the key
byte[] initializationVector;
byte[] encryptedKeyForRealm;
try {
SecretKey secretKey =
(SecretKey) keyStore.getKey("realm_key", null);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
encryptedKeyForRealm = cipher.doFinal(realmKey);
initializationVector = cipher.getIV();
} catch (InvalidKeyException | UnrecoverableKeyException
| NoSuchAlgorithmException | KeyStoreException
| BadPaddingException | IllegalBlockSizeException e) {
Log.e("EXAMPLE", "Failed encrypting the key with the secret key.");
throw new RuntimeException(e);
}
// keep the encrypted key in shared preferences
// to persist it across application runs
byte[] initializationVectorAndEncryptedKey =
new byte[Integer.BYTES +
initializationVector.length +
encryptedKeyForRealm.length];
ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt(initializationVector.length);
buffer.put(initializationVector);
buffer.put(encryptedKeyForRealm);
activity.getSharedPreferences("realm_key", Context.MODE_PRIVATE).edit()
.putString("iv_and_encrypted_key",
Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP))
.apply();
return realmKey; // pass to a realm configuration via encryptionKey()
}
// Create a key to encrypt a realm and save it securely in the keystore
fun getNewKey(): ByteArray {
// open a connection to the android keystore
val keyStore: KeyStore
try {
keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
} catch (e: Exception) {
Log.v("EXAMPLE", "Failed to open the keystore.")
throw RuntimeException(e)
}
// create a securely generated random asymmetric RSA key
val realmKey = ByteArray(Realm.ENCRYPTION_KEY_LENGTH)
SecureRandom().nextBytes(realmKey)
// create a cipher that uses AES encryption -- we'll use this to encrypt our key
val cipher: Cipher
cipher = try {
Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7)
} catch (e: Exception) {
Log.e("EXAMPLE", "Failed to create a cipher.")
throw RuntimeException(e)
}
// generate secret key
val keyGenerator: KeyGenerator
keyGenerator = try {
KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore")
} catch (e: NoSuchAlgorithmException) {
Log.e("EXAMPLE", "Failed to access the key generator.")
throw RuntimeException(e)
}
val keySpec = KeyGenParameterSpec.Builder(
"realm_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(
AUTH_VALID_DURATION_IN_SECOND)
.build()
try {
keyGenerator.init(keySpec)
} catch (e: InvalidAlgorithmParameterException) {
Log.e("EXAMPLE", "Failed to generate a secret key.")
throw RuntimeException(e)
}
keyGenerator.generateKey()
// access the generated key in the android keystore, then
// use the cipher to create an encrypted version of the key
val initializationVector: ByteArray
val encryptedKeyForRealm: ByteArray
try {
val secretKey = keyStore.getKey("realm_key", null) as SecretKey
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
encryptedKeyForRealm = cipher.doFinal(realmKey)
initializationVector = cipher.iv
} catch (e: Exception) {
Log.e("EXAMPLE", "Failed encrypting the key with the secret key.")
throw RuntimeException(e)
}
// keep the encrypted key in shared preferences
// to persist it across application runs
val initializationVectorAndEncryptedKey = ByteArray(Integer.BYTES +
initializationVector.size +
encryptedKeyForRealm.size)
val buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey)
buffer.order(ByteOrder.BIG_ENDIAN)
buffer.putInt(initializationVector.size)
buffer.put(initializationVector)
buffer.put(encryptedKeyForRealm)
activity!!.getSharedPreferences("realm_key", Context.MODE_PRIVATE).edit()
.putString("iv_and_encrypted_key",
Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP))
.apply()
return realmKey // pass to a realm configuration via encryptionKey()
}

El siguiente código muestra cómo acceder y descifrar una clave de cifrado almacenada de forma segura para un realm:

// Access the encrypted key in the keystore, decrypt it with the secret,
// and use it to open and read from the realm again
public byte[] getExistingKey() {
// open a connection to the android keystore
KeyStore keyStore;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
Log.e("EXAMPLE", "Failed to open the keystore.");
throw new RuntimeException(e);
}
// access the encrypted key that's stored in shared preferences
byte[] initializationVectorAndEncryptedKey = Base64.decode(activity
.getSharedPreferences("realm_key", Context.MODE_PRIVATE)
.getString("iv_and_encrypted_key", null), Base64.DEFAULT);
ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
buffer.order(ByteOrder.BIG_ENDIAN);
// extract the length of the initialization vector from the buffer
int initializationVectorLength = buffer.getInt();
// extract the initialization vector based on that length
byte[] initializationVector = new byte[initializationVectorLength];
buffer.get(initializationVector);
// extract the encrypted key
byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length
- Integer.BYTES
- initializationVectorLength];
buffer.get(encryptedKey);
// create a cipher that uses AES encryption to decrypt our key
Cipher cipher;
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Log.e("EXAMPLE", "Failed to create cipher.");
throw new RuntimeException(e);
}
// decrypt the encrypted key with the secret key stored in the keystore
byte[] decryptedKey;
try {
final SecretKey secretKey =
(SecretKey) keyStore.getKey("realm_key", null);
final IvParameterSpec initializationVectorSpec =
new IvParameterSpec(initializationVector);
cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec);
decryptedKey = cipher.doFinal(encryptedKey);
} catch (InvalidKeyException e) {
Log.e("EXAMPLE", "Failed to decrypt. Invalid key.");
throw new RuntimeException(e);
} catch (UnrecoverableKeyException | NoSuchAlgorithmException
| BadPaddingException | KeyStoreException
| IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
Log.e("EXAMPLE",
"Failed to decrypt the encrypted realm key with the secret key.");
throw new RuntimeException(e);
}
return decryptedKey; // pass to a realm configuration via encryptionKey()
}
// Access the encrypted key in the keystore, decrypt it with the secret,
// and use it to open and read from the realm again
fun getExistingKey(): ByteArray {
// open a connection to the android keystore
val keyStore: KeyStore
try {
keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
} catch (e: Exception) {
Log.e("EXAMPLE", "Failed to open the keystore.")
throw RuntimeException(e)
}
// access the encrypted key that's stored in shared preferences
val initializationVectorAndEncryptedKey = Base64.decode(activity
?.getSharedPreferences("realm_key", Context.MODE_PRIVATE)
?.getString("iv_and_encrypted_key", null), Base64.DEFAULT)
val buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey)
buffer.order(ByteOrder.BIG_ENDIAN)
// extract the length of the initialization vector from the buffer
val initializationVectorLength = buffer.int
// extract the initialization vector based on that length
val initializationVector = ByteArray(initializationVectorLength)
buffer[initializationVector]
// extract the encrypted key
val encryptedKey = ByteArray(initializationVectorAndEncryptedKey.size
- Integer.BYTES
- initializationVectorLength)
buffer[encryptedKey]
// create a cipher that uses AES encryption to decrypt our key
val cipher: Cipher
cipher = try {
Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7)
} catch (e: Exception) {
Log.e("EXAMPLE", "Failed to create cipher.")
throw RuntimeException(e)
}
// decrypt the encrypted key with the secret key stored in the keystore
val decryptedKey: ByteArray
decryptedKey = try {
val secretKey = keyStore.getKey("realm_key", null) as SecretKey
val initializationVectorSpec = IvParameterSpec(initializationVector)
cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec)
cipher.doFinal(encryptedKey)
} catch (e: InvalidKeyException) {
Log.e("EXAMPLE", "Failed to decrypt. Invalid key.")
throw RuntimeException(e)
} catch (e: Exception ) {
Log.e("EXAMPLE",
"Failed to decrypt the encrypted realm key with the secret key.")
throw RuntimeException(e)
}
return decryptedKey // pass to a realm configuration via encryptionKey()
}

El siguiente código demuestra cómo abrir un realm cifrado con el método encryptionKey():

// use a new encryption key to write and read from a realm
byte[] realmKey = getNewKey();
// use the key to configure a realm
final SyncConfiguration realmConfig =
new SyncConfiguration.Builder(user, PARTITION)
.allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.encryptionKey(realmKey)
.build();
// once we've used the key to generate a config, erase it in memory manually
Arrays.fill(realmKey, (byte) 0);
// open and write and read from the realm
Realm encryptedRealm = Realm.getInstance(realmConfig);
ObjectId id = new ObjectId();
encryptedRealm.executeTransaction(eR -> {
eR.createObject(Frog.class, id);
});
Frog frog = encryptedRealm.where(Frog.class).findFirst();
ObjectId written_id = frog.get_id();
Log.v("EXAMPLE", "generated id: " + id
+ ", written frog id: " + written_id);
encryptedRealm.close();
// get the encryption key from the key store a second time
byte[] decryptedKey = getExistingKey();
// configure a realm with the key
final SyncConfiguration realmConfigDecrypt =
new SyncConfiguration.Builder(user, PARTITION)
.allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.encryptionKey(decryptedKey)
.build();
// once we've used the key to generate a config, erase it in memory manually
Arrays.fill(decryptedKey, (byte) 0);
// note: realm is encrypted, this variable just demonstrates that we've
// decrypted the contents with the key in memory
Realm decryptedRealm = Realm.getInstance(realmConfigDecrypt);
Frog frogDecrypt = decryptedRealm.where(Frog.class).findFirst();
Log.v("EXAMPLE", "generated id: " + id
+ ", decrypted written frog id: " + frogDecrypt.get_id());
decryptedRealm.close();
// use a new encryption key to write and read from a realm
val realmKey = getNewKey()
// use the key to configure a realm
val realmConfig = SyncConfiguration.Builder(user, PARTITION)
.allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.encryptionKey(realmKey)
.build()
// once we've used the key to generate a config, erase it in memory manually
Arrays.fill(realmKey, 0.toByte())
// open and write and read from the realm
val encryptedRealm = Realm.getInstance(realmConfig)
val id = ObjectId()
encryptedRealm.executeTransaction {
eR: Realm -> eR.createObject(Frog::class.java, id)
}
val frog = encryptedRealm.where(Frog::class.java).findFirst()
val written_id = frog!!._id
Log.v("EXAMPLE", "generated id: " + id
+ ", written frog id: " + written_id)
encryptedRealm.close()
// get the encryption key from the key store a second time
val decryptedKey = getExistingKey()
// configure a realm with the key
val realmConfigDecrypt = SyncConfiguration.Builder(user, PARTITION)
.allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.encryptionKey(decryptedKey)
.build()
// once we've used the key to generate a config, erase it in memory manually
Arrays.fill(decryptedKey, 0.toByte())
// note: realm is encrypted, this variable just demonstrates that we've
// decrypted the contents with the key in memory
val decryptedRealm = Realm.getInstance(realmConfigDecrypt)
val frogDecrypt = decryptedRealm.where(Frog::class.java).findFirst()
Log.v("EXAMPLE", "generated id: " + id
+ ", decrypted written frog id: " + frogDecrypt!!._id)
decryptedRealm.close()

Volver

Bundle a Realm

En esta página