-34018 A required entitlement isn't present

Hi,


I have created a new command line tool project, where I am trying to add a key to the keychain.


Error:

OSStatus: -34018

Error Description: A required entitlement isn't present.


Questions:

1. How can I resolve this error ? I am able to do the same thing using a mac app instead of a command line tool project.

2. Am I missing some key steps ?


Points to note:

- Signing: Automatically manage Signing

- Signing certificate - Development


main.swift


import Foundation
import CryptoKit

func storeKey(label: String) {
    
    //Generate key
    let key = P256.Signing.PrivateKey()
    
    let attributes = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
                      kSecAttrKeyClass: kSecAttrKeyClassPrivate] as [String: Any]
    
    // Get a SecKey representation.
    guard let secKey = SecKeyCreateWithData(key.x963Representation as CFData,
                                            attributes as CFDictionary,
                                            nil) else {
                                                print("Unable to create SecKey representation.")
                                                return
    }
    
    let query = [kSecClass: kSecClassKey,
                 kSecAttrApplicationLabel: label,
                 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
                 kSecUseDataProtectionKeychain: true,
                 kSecValueRef: secKey] as [String: Any]
    
    // Add the key to the keychain.
    let status = SecItemAdd(query as CFDictionary, nil)
    
    guard status == errSecSuccess else {
        print("Unable to store item: \(status)")
        
        
        print(SecCopyErrorMessageString(status, nil) ?? "")
        return
    }
    
    print("successful")
}

storeKey(label: "TEST-KeyChain-Mac")



Attempts made:

1. Add Info.plist (contents shown below)

2. Add TestExample.entitlements (contents shown below)


After adding entitlements I get the following error:

Message from debugger: Error 1

Program ended with exit code: -1


Build Settings Changes:


CODE_SIGN_ENTITLEMENTS = TestExample/TestExample.entitlements

CODE_SIGN_INJECT_BASE_ENTITLEMENTS

INFOPLIST_FILE = TestExample/Info.plist

CREATE_INFOPLIST_SECTION_IN_BINARY = YES

PRODUCT_BUNDLE_IDENTIFIER = .TestExample


Info.plist


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleIdentifier</key>
  <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
  <key>CFBundleName</key>
  <string>$(PRODUCT_NAME)</string>
</dict>
</plist>


TestExample.entitlements


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>keychain-access-groups</key>
  <array>
  <string>$(AppIdentifierPrefix)<same reverse DNS>.TestExample</string>
  </array>
</dict>
</plist>

Accepted Reply

If you want to use the iOS style keychain (which is what you’re doing when you specify

kSecUseDataProtectionKeychain
), you need a provisioning profile to whitelist your app identifier entitlement. macOS does not support this directly, so neither does Xcode. To make this work, you have to do something kludgy. See my Packaging a Daemon with a Provisioning Profile post for details.

Share and Enjoy

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

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

Replies

If you want to use the iOS style keychain (which is what you’re doing when you specify

kSecUseDataProtectionKeychain
), you need a provisioning profile to whitelist your app identifier entitlement. macOS does not support this directly, so neither does Xcode. To make this work, you have to do something kludgy. See my Packaging a Daemon with a Provisioning Profile post for details.

Share and Enjoy

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

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

Quinn, Thanks a ton!!! was breaking my head over this.


I have a few doubts:

1. Even when kSecUseDataProtectionKeychain is removed command line tool throws the same missing entitlement error:


    let query = [kSecClass: kSecClassKey,
                 kSecAttrApplicationLabel: label,
                 kSecValueRef: secKey] as [String: Any]



2. Based on the https://forums.developer.apple.com/message/408009#408009 are we are creating a mac cocoa app / target, striping out the cocoa bits of the mac app, then using the command line executable that is built part of the mac app ?

3. Are we doing the above to enable us to use the provisioning profile ?

4. Based on the link, where is the launchd property list file (quoted below from the link)?


Now modify your

launchd
property list file so that the
Program
property (or the first item of the
ProgramArguments
array) points to the executable within
Contents/MacOS/
.

1. Even when

kSecUseDataProtectionKeychain
is removed command line tool throws the same missing entitlement error:

Interesting. I’m not sure why that is. You should be able to access the file-based keychain without any entitlements. It’s possibly because the key is coming from CryptoKit. I’ve not tried that combination before.

How you proceed here is up to you. The file-based keychain is still supported and it’s quite capable of storing a private key like this. So, you can either dig into the reason for that failure or continue on with the data protection keychain.

3. Are we doing the above to enable us to use the provisioning profile ?

Yes. macOS will pick up the profile from

Contents/embedded.provisionprofile
. You can manually build all of this app-like structure yourself, but it’s much easier to let Xcode do the heavy lifting.

4. Based on the link, where is the launchd property list file (quoted below from the link)?

That would only be necessary if you were building an actual daemon. If you’re building a command-line tool, just run the executable from the

Contents/MacOS/
directory.

Share and Enjoy

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

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

Thanks a lot !!!


Very well documented, thank you so much, works really well.


I just wanted to validate my approach:


Archive and Exported a copy:

I have exported a copy of the archived app (archive, then chose copy app)


Execute from command line:

Then I run from the command line as follows:

./Example4.app/Contents/MacOS/Example4


Question:

So if I wanted both a macOS app with some UI and Command line interface would I have to the following?

- Create another target in the same project and add the common source files to both the targets ? (both targets would be macOS app targets, but one is a stripped down version as in the documentation link, the other would be a normal app target with the app delegate, and other UI elements).

- Different schemes would point to different targets, so could export them when needed.

So if I wanted both a macOS app with some UI and Command line interface would I have to the following?

You may be able to get away with using the same executable for this. For example, I recently created a program that would either run as an app or an agent, so I replaced

main.swift
with this:
import Cocoa

private func main() {
    if CommandLine.arguments.dropFirst() == ["agent"] {
        Agent.main()
    } else {
        AppDelegate.main()
    }
}

main()

and then set up my

launchd
property list to pass in the
agent
argument.

Share and Enjoy

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

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

Wow this sounds like a pretty cool idea !!


I don't know much about deamons, I should probably read about them and come back.


Learnt so much, thanks a ton !!

Thanks a lot Quinn, sorry for the delayed response.


I really like your solution of determining what code to run based on the first command line argument. Really neat !!


I read a little about user agents (background process) and now I understand your posts better, as you had pointed out in my case we don't need a launchd property list because it is not going to be a deamon / user agent.


Thanks a lot, your solution was so helpful and I learned a lot !!