Navigation

Encrypt a Realm

Overview

You can the encrypt the realm database file on disk with AES-256 + SHA-2 by supplying a 64-byte encryption key when opening a realm.

Realm transparently encrypts and decrypts data with standard AES-256 encryption using the first 256 bits of the given 512-bit encryption key. Realm uses the other 256 bits of the 512-bit encryption key to validate integrity using a hash-based message authentication code (HMAC).

Considerations

Storing & Reusing Keys

You must pass the same encryption key when opening the realm again. Apps should store the encryption key in the Keychain so that other apps cannot read the key.

Performance Impact

Typically, reads and writes on encrypted realms can be up to 10% slower than unencrypted realms.

Encryption and Realm Sync

You can encrypt a synced realm. MongoDB Realm only encrypts the data on the device and stores the data unencrypted in your MongoDB Atlas data source.

Example

The following code demonstrates how to generate an encryption key and open an encrypted realm:

// Generate a random encryption key
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
    SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}

// Open the encrypted Realm file
let config = Realm.Configuration(encryptionKey: key)

do {
    let realm = try Realm(configuration: config)
    // Use the Realm as normal
    let dogs = realm.objects(Dog.self).filter("name contains 'Fido'")
    // ...

} catch let error as NSError {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    fatalError("Error opening realm: \(error)")
}
// Generate a random encryption key
NSMutableData *key = [NSMutableData dataWithLength:64];
(void)SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);

// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.encryptionKey = key;
NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    NSLog(@"Error opening realm: %@", error);
}

// Use the Realm as normal
RLMResults<Dog *> *dogs = [Dog objectsInRealm:realm where:@"name contains 'Fido'"];
// ...

The following Swift example demonstrates how to store and retrieve a generated key from the Keychain:

// Retrieve the existing encryption key for the app if it exists or create a new one
func getKey() -> Data {
    // Identifier for our keychain entry - should be unique for your application
    let keychainIdentifier = "io.Realm.EncryptionExampleKey"
    let keychainIdentifierData = keychainIdentifier.data(using: String.Encoding.utf8, allowLossyConversion: false)!

    // First check in the keychain for an existing key
    var query: [NSString: AnyObject] = [
        kSecClass: kSecClassKey,
        kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
        kSecAttrKeySizeInBits: 512 as AnyObject,
        kSecReturnData: true as AnyObject
    ]

    // To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
    // See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
    var dataTypeRef: AnyObject?
    var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
    if status == errSecSuccess {
        return dataTypeRef as! Data
    }

    // No pre-existing key from this application, so generate a new one
    // Generate a random encryption key
    var key = Data(count: 64)
    _ = key.withUnsafeMutableBytes({ (pointer: UnsafeMutableRawBufferPointer) in
        let result = SecRandomCopyBytes(kSecRandomDefault, 64, pointer.baseAddress!)
        assert(result == 0, "Failed to get random bytes")
    })

    // Store the key in the keychain
    query = [
        kSecClass: kSecClassKey,
        kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
        kSecAttrKeySizeInBits: 512 as AnyObject,
        kSecValueData: key as AnyObject
    ]

    status = SecItemAdd(query as CFDictionary, nil)
    assert(status == errSecSuccess, "Failed to insert the new key in the keychain")

    return key
}

// ...
// Use the getKey() function to get the stored encryption key or create a new one
let config = Realm.Configuration(encryptionKey: getKey())

do {
    // Open the realm with the configuration
    let realm = try Realm(configuration: config)
    
    // Use the realm as normal
    let dogs = realm.objects(Dog.self).filter("name contains 'Fido'");

    // ...

} catch let error as NSError {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    fatalError("Error opening realm: \(error)")
}