【RealmSwift】暗号化する方法

暗号化と聞くと難しそうな印象がありますが、RealmSwift は意外と簡単にできるようになっています。
デフォルトでは暗号化されないので、自分で設定しない限りデータベースは丸裸です (//ω//)

開発環境(前提条件)

・Xcode11.6
・Swift5
・RealmSwift(2022年9月28日に最新バージョンで再確認済み)

暗号化する方法

Realm.Configuration に encryptionKey プロパティというものがあり、それに64バイトの暗号化キーを指定することで暗号化が可能になります。
順を追って記載していくため、一部変数名は被ると思いますので、コピペする際に書き直してください。

保存する前にキーチェンの有無を確認

最初はキーチェーンに暗号化キーがあるかの確認を行います。

func getExistKey() -> Data? {
    let identifierData = "com.example.tag".data(using: .utf8,
                                                allowLossyConversion: false)!     // ★1
    let query: [String: Any] = [kSecClass as String: kSecClassKey,
                                kSecAttrApplicationTag as String: identifierData,
                                kSecAttrKeySizeInBits as String: 512,
                                kSecReturnData as String: kCFBooleanTrue as Any]
    var data: AnyObject?
    let status = withUnsafeMutablePointer(to: &data) {  // ★2
        SecItemCopyMatching(query as CFDictionary,
                            UnsafeMutablePointer($0))
    }
    if status == errSecSuccess {
        // 見つかったときの処理
    }
    // 参考程度のコードなんで、テキトーにキャストできるかで返します。w
    return data ? Data
}

★1…キーチェーンは一意の ID が必要になるので、”com.example.tag” は変えてください。
★2…コンパイル時にエラーにならないようにするための対策。Swift 側のバグらしいです。

見つからなければ、暗号化キーを新たに生成します。
最初は暗号化キーの生成から書いていたのですが、処理的に順番が変になるなと思い、書き直しました。笑
いよいよ暗号化キーを作ってきます (*^ω^)人(^ω^*)

暗号化キーを生成

Apple が提供している SecRandomCopyBytes(_:_:_:) を使うことで、動的に暗号化キーを生成することができます。暗号化キーをアプリに直接保存していると特定されるかもしれないので、出来るだけ動的に生成するようにしましょう。

func createKey() -> Data? {
    let data = NSMutableData(length: 64)!
    let status = SecRandomCopyBytes(kSecRandomDefault,
                                    64,
                                    data.mutableBytes.bindMemory(to: UInt8.self,
                                                                 capacity: 64))
    if status == errSecSuccess {
        print(keyData)  // ★3
        return data as! Data
    } else {
        return nil
    }
}

★3…ちゃんと暗号化されているか確認。実行するたびに異なる値が表示されていれば大丈夫です。
こんなやーつ。
{length = 64, bytes = 0x86cbf77c faf6224a afbe630b 4d4486e7 … f154e988 c41b3981 }
{length = 64, bytes = 0x870f1120 fb220aae bde40898 b532f508 … 8dbe2b90 81a5bc57 }

キーチェーンへの保存

こちらも Apple が提供している SecItemAdd(_:_:) を使うことで、キーチェーンに保存することができます。

func saveKey(_ data: Data) -> Bool {
    let identifierData = "com.example.tag".data(using: .utf8,
                                                allowLossyConversion: false)!
    let query: [String: Any] = [kSecClass as String: kSecClassKey,
                                kSecAttrApplicationTag as String: identifierData,
                                kSecAttrKeySizeInBits as String: 512,
                                kSecValueData as String: data]
    let status = SecItemAdd(query as CFDictionary,
                            nil)
    return status == errSecSuccess
}

改めてですが、関数ごとに分けてるのは説明しやすからなので、コピペして好きに改良してください。笑

Realm に暗号化を設定する

作成した暗号化キーを encryptionKey に設定することで、AES-256 で暗号化されて、SHA-2 HMAC で検証が行われるようになります。

do {
    let config = Realm.Configuration(encryptionKey: keyData)
    let realm = try Realm(configuration: config)
    // ここでようやく Realm がいじれる
} catch {
    print(error.localizedDescription)
}

Realm を暗号化する方法はこれだけです。
…キーチェーンの説明の方が長い!笑
でも、簡単に暗号化が使えるようになっているように感じます。
一応ここに書いてる処理は最低限なので、キーチェーンが無くならないよう設計する必要もあるかと思います。

さいごに

アプリ内で保存している以上、完全にデータが守られるわけではありませんが、一応セキュリティレベルが上がるよねって感じなので、できれば設定しましょうってやーつです。
海外の記事か StackOverflow かで、パフォーマンスが落ちると書かれていましたが、ガクンと落ちることはないので気にしなくていいのかなと思ってますが、誰か詳しい人いたら教えてください。

Realm
スポンサーリンク

コメント