Access Keychain When iPhone Locked

I have implemented the following code to create a keychain item:


let keychainQuery = NSMutableDictionary(dictionary: [
    NSString(format: kSecClass) : kSecClassGenericPasswordValue,
    NSString(format: kSecAttrService) : "my_service",
    NSString(format: kSecAttrAccount) : "my.app.domain",
    NSString(format: kSecAttrAccessible) : NSString(format: kSecAttrAccessibleAfterFirstUnlock),
    NSString(format: kSecReturnData) : "my_password")
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
if status != errSecSuccess (
   print("status not success: \(status)")
}


Unfortunately, when the iPhone is locked (press the lock button), even though the accessible element has been set to "after first unlock", I get an error (-25300) when my code tries to retrieve the information stored in the keychain. The app can access information in the keychain while the app is in the background, just not when the iPhone is locked.


The code to retrieve:

let keychainQuery: NSMutableDictionary = NSMutableDictionary(dictionary: [
    kSecClassValue: kSecClassGenericPasswordValue,
    NSString(format: kSecAttrService) : "my_service",
    NSString(format: kSecAttrAccount) : "my.app.domain",
    NSString(format: kSecReturnData) : kCFBooleanTrue,
    NSString(format: kSecMatchLimit) : NSString(format: kSecMatchLimitOne)
])


iOS 10.3.1


Is there any way to allow programmatic access to contents in the keychain on iOS when an app is in the background and the phone is locked?

Thanks,

Karl

Your basic strategy seems sound. I’m not 100% sure what’s causing the problem you’re seeing but I have couple of suggestions:

  • The code you posted doesn’t seem to be the code you’re actually running. Specifically:

    • There’s no such thing as
      kSecClassGenericPasswordValue
    • On line 6 of your first listing, you have
      kSecReturnData
      when you should have
      kSecValueData
  • In addition, the code is way more complex than it needs to be. I’ve pasted in some snippets below that show the way.

My recommendation is that you fix these issues and then retest. It’s even possible that this might fix your problem; your use of

NSString(format:)
could get your into trouble in various interesting ways (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

WWDC runs Mon, 5 Jun through to Fri, 9 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face. http://developer.apple.com/wwdc/

private func addKeychainItem() {
    let err = SecItemAdd([
        kSecClass:          kSecClassGenericPassword, 
        kSecAttrService:    "my_service",
        kSecAttrAccount:    "my.app.domain",
        kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock, 
        kSecValueData:      "my_password".data(using: .utf8)!
    ] as NSDictionary, nil)
    switch err {
        case errSecSuccess, errSecDuplicateItem:
            break
        default:
            fatalError()
    }
}

private func readKeychainItem() -> String {
    var result: CFTypeRef? = nil
    let err = SecItemCopyMatching([
        kSecClass:          kSecClassGenericPassword, 
        kSecAttrService:    "my_service",
        kSecAttrAccount:    "my.app.domain",
        kSecReturnData:     true
    ] as NSDictionary, &result)
    guard err == errSecSuccess else {
        return "Error: \(err)"
    }
    let password = String(bytes: (result as! Data), encoding: .utf8)!
    let protectedState = UIApplication.shared.isProtectedDataAvailable ? "Unprotected" : "Protected"
    return "\(protectedState): '\(password)'"
}

I actually sat down and tested this today. Here’s what I did:

  1. I created a small app with two functions:

    • One tab of the app’s UI lets me advertise a beacon using CoreBluetooth

    • Another tab of the app’s UI lets me start a beacon location request using CoreLocation

    The test app has

    location
    in the
    UIBackgroundModes
    property so that the location query continues while the app is in the background.
  2. When the app detects a beacon, it runs my test code (

    readKeychainItem()
    from my previous post) and posts a notification with the result.
  3. On one device I ran the app from the Home screen, started a beacon search, and then locked the device.

  4. I waited for 30 seconds for that device to fully suspend.

  5. On another device I started advertising the beacon.

At this point the system resumed the app on the first device and called

locationManager(_:didEnterRegion:)
. That ran the keychain query and posted a notification with the results. Here’s what I see:
  • If the keychain item’s accessible attribute is set to

    kSecAttrAccessibleAfterFirstUnlock
    , the code to read it always works.
  • OTOH, if I change that attribute to

    kSecAttrAccessibleWhenUnlocked
    , the code to read it typically fails with
    errSecInteractionNotAllowed
    (-25308).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

WWDC runs Mon, 5 Jun through to Fri, 9 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face. http://developer.apple.com/wwdc/

Hello eskimo,


I have similar kind of functionalities where I am planning to store the 'token/password' in the keychain and this need to access it even before the FIRST UNLOCK of iPhone device after restart/reboot.


My app has Significant changes and Region Monitoring enabled. This records the locations in all the scenarios of iPhone locked/unlocked/no passcode/reboot etc. Depending upon this location recording, the app has a scenario in which it needs to access keychain even before first unlock of iPhone. I want to know that: "IS IT POSSIBLE TO STORE AND ACCESS KEYCHAIN JUST AFTER iPhone RESTART/REBOOT AND BEFORE THE FIRST UNOCK OF iPhone DEVICE?"


My app is compatible with iOS 9,10,11 and for iPhone 4s to iPhone X.


Thanks,

Srideo

Is it possible to store and access keychain just after iPhone restart/reboot and before the first unlock of iPhone device?

Yes. To do this you need to set the

kSecAttrAccessible
property of the keychain item to
kSecAttrAccessibleAlways
(or
kSecAttrAccessibleAlwaysThisDeviceOnly
).

WARNING Doing this will measurably reduce the security of the item. Thus, you should only do this for items that you absolutely have to access before first unlock, and continue using tighter security for all other items.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Access Keychain When iPhone Locked
 
 
Q