evgenyneu / keychain-swift

Helper functions for saving text in Keychain securely for iOS, OS X, tvOS and watchOS.
MIT License
2.82k stars 345 forks source link

Using access groups not possible #103

Open ConcapptPrakti opened 5 years ago

ConcapptPrakti commented 5 years ago

I am currently trying to use the iOS Keychain feature for within an Unity App. The ultimate goal is to store data between apps. I have set up the communication and followed tutorials online about keychain.

Using this GitHub I wrote the following code:

 ` let keychain = KeychainSwift()
    keychain.accessGroup = "MyTeamPrefix.myKeychainGroup1"
    // MyTeamPrefix is the actual 10 char string from Apple Developer

    if keychain.set("hello world", forKey: "my key") {
        // Keychain item is saved successfully
    } else {
        print(keychain.lastResultCode)
    }

`

Unfortunately it does not set a new Keychain Item, but instead prints -25243

This is referring to the access group I set to the keychain object. It says that I am not authorised to set new keychain items. When I do it without setting the keychain it works fine.

What I did

In my eyes I did everything right to the following tutorial: https://evgenii.com/blog/sharing-keychain-in-ios/ I created a keychain group (entitlements says: $(AppIdentifierPrefix)myKeychainGroup1) I got the prefix for Apple Developer for this right bundle id (same prefix as for my other apps anyways) Then I wrote the code, but it does not work.

I don't know what to do at this point. I went through a lot of online post, but no solution. Every help is well appreciated. Cheers

PS: The code runs on a real device.

Similar post that didn't help as far as I tested: https://stackoverflow.com/questions/7989258/ios-keychain-secitemadd-returns-25243

evgenyneu commented 5 years ago

Hi @ConcapptPrakti, thanks for reporting the issue. It looks like you did everything correctly. I would try to isolate the case and create two simple apps in Xcode: one that writes a shared key, and another that reads it. Here is the code for the two apps.

Writer app

import UIKit

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    // Create a keychain item

    let itemKey = "My key"
    let itemValue = "My secretive bee 🐝"
    let keychainAccessGroupName = "ENTER_YOUR_TEAM_ID_HERE.myKeychainGroup1234564321"

    let queryDelete: [String: AnyObject] = [
      kSecClass as String: kSecClassGenericPassword,
      kSecAttrAccount as String: itemKey as AnyObject,
      kSecAttrAccessGroup as String: keychainAccessGroupName as AnyObject
    ]

    let resultCodeDelete = SecItemDelete(queryDelete as CFDictionary)

    if resultCodeDelete != noErr {
      print("Error deleting from Keychain: \(resultCodeDelete)")
    }

    guard let valueData = itemValue.data(using: String.Encoding.utf8) else {
      print("Error saving text to Keychain")
      return
    }

    let queryAdd: [String: AnyObject] = [
      kSecClass as String: kSecClassGenericPassword,
      kSecAttrAccount as String: itemKey as AnyObject,
      kSecValueData as String: valueData as AnyObject,
      kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
      kSecAttrAccessGroup as String: keychainAccessGroupName as AnyObject
    ]

    let resultCode = SecItemAdd(queryAdd as CFDictionary, nil)

    if resultCode != noErr {
      print("Error saving to Keychain: \(resultCode)")
    }
  }
}

Reader app

import UIKit

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    let itemKey = "My key"
    let keychainAccessGroupName = "ENTER_YOUR_TEAM_ID_HERE.myKeychainGroup1234564321"

    // Read the keychain
    let queryLoad: [String: AnyObject] = [
      kSecClass as String: kSecClassGenericPassword,
      kSecAttrAccount as String: itemKey as AnyObject,
      kSecReturnData as String: kCFBooleanTrue,
      kSecMatchLimit as String: kSecMatchLimitOne,
      kSecAttrAccessGroup as String: keychainAccessGroupName as AnyObject
    ]

    var result: AnyObject?

    let resultCodeLoad = withUnsafeMutablePointer(to: &result) {
      SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
    }

    if resultCodeLoad == noErr {
      if let result = result as? Data,
        let keyValue = NSString(data: result,
                                encoding: String.Encoding.utf8.rawValue) as? String {

        // Found successfully
        print(keyValue)
      }
    } else {
      print("Error loading from Keychain: \(resultCodeLoad)")
    }
  }
}

Replace the team ID and the group

ConcapptPrakti commented 5 years ago

@evgenyneu Thank you for the quick reply. I went through your very good answer and got some good and some bad results.

The good news is that the script is working perfectly fine on the simulator. On the real device it is an other story. After calling SecItemAdd, resultCode is 25243. Meaning it prints: "Error saving to Keychain: -25243"

I hope it comes down to some hardware differences on my device. It is an iPad (3rd generation) Model: A1416 (iOS 9.3.5). So quit a bit outdated (no software updates officially available anymore)

Maybe someone has the knowledge, why keychain would't work on my device.

Thanks for everyone helping.

evgenyneu commented 5 years ago

I'm sorry you are having this issue on a device. Does it show the same error if you delete the lines with kSecAttrAccessGroup from queryDelete, queryAdd and queryLoad?

ConcapptPrakti commented 5 years ago

@evgenyneu Without kSecAttrAccessGroup in the queries it is working fine on the device.

evgenyneu commented 5 years ago

Thanks for having a look. The keychain sharing should definitely work on iOS 9 on a device. It could be a bug. I would submit a bug report to Apple.