SecCopyItemMatching -34018 only on iOS 13

Hi,


I'm working on a SSL client authentication scheme, schematically:

- generate a RSA (for now) keypair on the phone (tagging it with a applcation data)

- generating PKCS#10 data (public key and meta data), send it to a server to be signed

- getting a signed certificate back and importing into the keychain


When receiving client authentication chanlenge, I search for the identity, using the tag as the query, then create a URL credential.


It works on iOS 12

However, if I try on a iOS 13 device, I get -34018 (Missing entitlements) systematically when I use SecCopyItemMatching.


In development, the certificate is signed by a self-signed CA.


I have no need for keychain group, but tried to add the app-identifier as a keychain group in the entitlements, but the result is the same.


Could it be a iOS 13 bug or some change in Security?

On iOS13, I can get the private key and the cert separately (lookup using the public key hash) so I don't understand why I couldn't get the identity.

If anyone have some insight on this, I'll take it.


Thanks!


Jean-Alexis


Here are some excerpt from the code:

    func generateCSR() -> Data? {
        let keyParameters : [CFString: Any] = [
            kSecAttrKeyType: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits: 2048,
            kSecPublicKeyAttrs: [
                kSecAttrLabel: "My Public Key" as NSString,
                kSecAttrIsPermanent: true,
            ],
            kSecPrivateKeyAttrs: [
                kSecAttrLabel: "My Private Key" as NSString,
                kSecAttrApplicationTag: Self.keyTag,
                kSecAttrIsPermanent: true,
//                kSecAttrAccessControl: controlAccess!,
            ]
        ]
        var privateKey: SecKey! // on success, privateKey will not be nil
        
        guard withCFErrorCheck(context: "Generate key pair", {
            privateKey = SecKeyCreateRandomKey(keyParameters as CFDictionary, $0)
        }) else {
            return nil
        }
        
        return pkcs10(privateKey)
    }
   


    func handleCertResponse(data: Data?, response: URLResponse?, error: Error?) {
        // pkcs1DER was generated by openssl, received and decoded from Data, certificate import seems to work
        guard let signedKey = SecCertificateCreateWithData(nil, pkcs1DER as CFData) else {
            print ("Invalid DER X.509 certificate")
            return
        }
        let keyAttributes : [CFString: Any] = [kSecClass: kSecClassCertificate, kSecValueRef: signedKey,
                                               kSecAttrLabel: Self.certificateLabel,
                                               kSecReturnRef: true,
                                               kSecReturnAttributes: true,
        ]
        
        var returned: CFTypeRef?
        let status = SecItemAdd(keyAttributes as CFDictionary, &returned)
        guard isStatusOK(status, context: "Add certificate in keychain") else {
            return
        }
        
        let certAttributes = returned as! CFDictionary as Dictionary
        print ("Certificate values: \(certAttributes)")
    }
//


    func getIdentity() -> SecIdentity? {

        let getquery: [CFString: Any] = [kSecClass: kSecClassIdentity,
                                       kSecAttrApplicationTag: Self.keyTag,
                                       kSecReturnRef: true,
        ]
        var item: CFTypeRef?
        let statusSearch = SecItemCopyMatching(getquery as CFDictionary, &item)
        guard isStatusOK(statusSearch, context: "Searching identity") else {
            return nil
        }
        let identity = item as! SecIdentity
        print ("Identity is:\(identity)")
        return identity
    }

Replies

Is this just on the simulator? Or just on real devices? Or both?

Share and Enjoy

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

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

It works on the simulator iOS 13 and iOS 12 devices.

Fails on iOS 13 devices.

Hmmm, weird. I can’t think of any reason why

SecItemCopyMatching
would return
errSecMissingEntitlement
in that case. I’m going to recommend that you open a DTS tech support incident, which will allow me to allocate time look at this in more depth.

Share and Enjoy

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

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

I wrote a small demonstrator and it worked, so I got back to my project.


Deleting all KeyChain items fixed the problem.

It must have been poluted with some tests I've done with control access. I'll investigate and post here if I found something.


Still, I don't understand the missing entitlement. It would be so great if iOS would tell me which one is missing.


Thanks,


Jean-Alexis

I don’t think there’s actually an entitlement missing, at least not in the sense that you’re suggesting. Rather, the keychain uses three entitlements to decide on your keychain access group list (see the discussion in Sharing Access to Keychain Items Among a Collection of Apps), and it’s likely that one of the leftover items in your keychain was in a keychain access group to which you no longer have access.

Share and Enjoy

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

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

The problems appears as soon as I use controll access. I don't think it's related to access group as I have only the application one (so no entitlement created explicitly)


        var controlAccess : SecAccessControl!
        let controlAccessFlags = SecAccessControlCreateFlags(arrayLiteral: [.userPresence])
        guard withCFErrorCheck (context: "Create control access structure", {
            controlAccess = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, controlAccessFlags, $0)
        }) else {
            return nil
        }
        

        let keyParameters : [CFString: Any] = [
            kSecAttrKeyType: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits: 2048,
            kSecPublicKeyAttrs: [
                kSecAttrLabel: "Public Key" as NSString,
            ],
            kSecPrivateKeyAttrs: [
                kSecAttrLabel: "Private Key" as NSString,
                kSecAttrApplicationTag: Self.keyTag,
                kSecAttrIsPermanent: true,
                kSecAttrAccessControl: controlAccess!,
            ]
        ]
        var privateKey: SecKey! // on success, privateKey will not be nil
        
        guard withCFErrorCheck(context: "Generate key pair", {
            privateKey = SecKeyCreateRandomKey(keyParameters as CFDictionary, $0)
        }) else {
            return nil
        }
       


On the simulator when I try SecCopyItemMatching, I have a 25300 error.

On the device a 34018.


I'll try now to add a LAContext.

Same problem with LAContext.


If I try with access control on iOS 12, I have a 25291 error.


I Must be doing something wrong.