7 Replies
      Latest reply on Apr 1, 2020 8:42 PM by newwbee
      newwbee Level 1 Level 1 (0 points)

        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>
        
        • Re: -34018 A required entitlement isn't present
          eskimo Apple Staff Apple Staff (13,385 points)

          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"

            • Re: -34018 A required entitlement isn't present
              newwbee Level 1 Level 1 (0 points)

              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/.

                • Re: -34018 A required entitlement isn't present
                  eskimo Apple Staff Apple Staff (13,385 points)

                  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"

                    • Re: -34018 A required entitlement isn't present
                      newwbee Level 1 Level 1 (0 points)

                      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.

                        • Re: -34018 A required entitlement isn't present
                          eskimo Apple Staff Apple Staff (13,385 points)

                          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"

                            • Re: -34018 A required entitlement isn't present
                              newwbee Level 1 Level 1 (0 points)

                              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 !!

                              • Re: -34018 A required entitlement isn't present
                                newwbee Level 1 Level 1 (0 points)

                                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 !!