ViewController.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. import Foundation
  19. import RealmSwift
  20. import Security
  21. import UIKit
  22. // Model definition
  23. class EncryptionObject: Object {
  24. @objc dynamic var stringProp = ""
  25. }
  26. class ViewController: UIViewController {
  27. let textView = UITextView(frame: UIScreen.main.applicationFrame)
  28. // Create a view to display output in
  29. override func loadView() {
  30. super.loadView()
  31. view.addSubview(textView)
  32. }
  33. override func viewDidAppear(_ animated: Bool) {
  34. super.viewDidAppear(animated)
  35. // Use an autorelease pool to close the Realm at the end of the block, so
  36. // that we can try to reopen it with different keys
  37. autoreleasepool {
  38. let configuration = Realm.Configuration(encryptionKey: getKey() as Data)
  39. let realm = try! Realm(configuration: configuration)
  40. // Add an object
  41. try! realm.write {
  42. let obj = EncryptionObject()
  43. obj.stringProp = "abcd"
  44. realm.add(obj)
  45. }
  46. }
  47. // Opening with wrong key fails since it decrypts to the wrong thing
  48. autoreleasepool {
  49. do {
  50. let configuration = Realm.Configuration(encryptionKey: "1234567890123456789012345678901234567890123456789012345678901234".data(using: String.Encoding.utf8, allowLossyConversion: false))
  51. _ = try Realm(configuration: configuration)
  52. } catch {
  53. log(text: "Open with wrong key: \(error)")
  54. }
  55. }
  56. // Opening wihout supplying a key at all fails
  57. autoreleasepool {
  58. do {
  59. _ = try Realm()
  60. } catch {
  61. log(text: "Open with no key: \(error)")
  62. }
  63. }
  64. // Reopening with the correct key works and can read the data
  65. autoreleasepool {
  66. let configuration = Realm.Configuration(encryptionKey: getKey() as Data)
  67. let realm = try! Realm(configuration: configuration)
  68. if let stringProp = realm.objects(EncryptionObject.self).first?.stringProp {
  69. log(text: "Saved object: \(stringProp)")
  70. }
  71. }
  72. }
  73. func log(text: String) {
  74. textView.text += "\(text)\n\n"
  75. }
  76. func getKey() -> NSData {
  77. // Identifier for our keychain entry - should be unique for your application
  78. let keychainIdentifier = "io.Realm.EncryptionExampleKey"
  79. let keychainIdentifierData = keychainIdentifier.data(using: String.Encoding.utf8, allowLossyConversion: false)!
  80. // First check in the keychain for an existing key
  81. var query: [NSString: AnyObject] = [
  82. kSecClass: kSecClassKey,
  83. kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
  84. kSecAttrKeySizeInBits: 512 as AnyObject,
  85. kSecReturnData: true as AnyObject
  86. ]
  87. // To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
  88. // See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
  89. var dataTypeRef: AnyObject?
  90. var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
  91. if status == errSecSuccess {
  92. return dataTypeRef as! NSData
  93. }
  94. // No pre-existing key from this application, so generate a new one
  95. let keyData = NSMutableData(length: 64)!
  96. let result = SecRandomCopyBytes(kSecRandomDefault, 64, keyData.mutableBytes.bindMemory(to: UInt8.self, capacity: 64))
  97. assert(result == 0, "Failed to get random bytes")
  98. // Store the key in the keychain
  99. query = [
  100. kSecClass: kSecClassKey,
  101. kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
  102. kSecAttrKeySizeInBits: 512 as AnyObject,
  103. kSecValueData: keyData
  104. ]
  105. status = SecItemAdd(query as CFDictionary, nil)
  106. assert(status == errSecSuccess, "Failed to insert the new key in the keychain")
  107. return keyData
  108. }
  109. }