IOS not able to create privatekey if only devicecode is set on device?

Viewed 1155

Im trying to generate a privatekey that only is accessible when either devicecode or current set of biometrics( that is already registered on device) is used.

It works when i have a finger registered on device, then its all good. But if i delete my registered "touchid-finger", and try to generate a new key, then it returns nil

Errorcode is -25293

Code example:

func generateKey() -> SecKey?{
    var error: Unmanaged<CFError>?
    let accessControl = SecAccessControlCreateWithFlags(
            kCFAllocatorDefault,
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            [.devicePasscode,.or,.biometryCurrentSet],
            nil)

    let attributes:[String : Any] = [kSecAttrType as String: kSecAttrKeyTypeRSA,
    kSecAttrKeySizeInBits as String:4096,
    kSecPrivateKeyAttrs as String: [
      kSecAttrIsPermanent:true,
      kSecAttrCanSign: true,
      kSecAttrApplicationTag: "yes.its.my.tag",
      kSecAttrAccessControl:accessControl!]]
      let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
     if(error != nil || privateKey == nil) {
      fatalError("explode Kittens")
     }
     return privateKey
}

fyi.Its actually works on simulators but not on real devices.

Am i doing something wrong? Is this working as intended? is it a bug? (sooo many questions :D )

2 Answers

I'm generating a private key, you need to add in ACL object parameter kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. This will allow you to get a private key using a passcode or biometric data.

    guard let aclObject = SecAccessControlCreateWithFlags(
        kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
        [.userPresence, .privateKeyUsage], nil) else {
        return ""
    }
    // private key parameters
    let privateKeyParams: [String: AnyObject] = [
        kSecAttrAccessControl as String:    aclObject as AnyObject, //protect with touch id
        kSecAttrIsPermanent as String:      true as AnyObject
        ]

    // global parameters for our key generation
    let parameters: [String: AnyObject] = [
        kSecAttrTokenID as String:          kSecAttrTokenIDSecureEnclave,
        kSecAttrKeyType as String:          kSecMessECCKeyType,
        kSecAttrKeySizeInBits as String:    kSecMessECCKeySize as AnyObject,
        kSecAttrLabel as String:            kSecMessECCSignLabel as AnyObject,
        kSecPrivateKeyAttrs as String:      privateKeyParams as AnyObject
    ]

    guard let eCCPrivKey = SecKeyCreateRandomKey(parameters as CFDictionary, nil) else {
        print("ECC KeyGen Error!")
        return ""
    }

    guard let eCCPubKey = SecKeyCopyPublicKey(eCCPrivKey) else {
        print("ECC Pub KeyGen Error")
        return ""
    }

"The problem relates to how the item is /created/, and that’s not what these flags control.

Indeed, for .biometryCurrentSet to have any meaning at the time of use, there must actually be a current set of biometrics at the time of creation, and I think that’s the source of the errSecAuthFailed.

My recommendation is manually fall back to using just .devicePasscode if there’s no biometrics available. Two ways: A. Catch the error and retry B. Preflight the request using the LocalAuthentication framework."

Apple says no can do :D

Related