Hello,I have a local WebSocket server running inside an iOS app on iOS 13+. I'm using Swift NIO Transport Services for the server.I'm using NWProtocolTLS.Options from Network framework to specify TLS options for my server.I am providing my server as an XCFramework and want to let users to be able to specify different parameters when launching the server.For specifiying the TLS supported version, everything is working fine by using :public func sec_protocol_options_set_max_tls_protocol_version(_ options: sec_protocol_options_t, _ version: tls_protocol_version_t)
public func sec_protocol_options_set_min_tls_protocol_version(_ options: sec_protocol_options_t, _ version: tls_protocol_version_t)But I also want to be able to specify some cipher suites. I saw that I can use :public func sec_protocol_options_append_tls_ciphersuite(_ options: sec_protocol_options_t, _ ciphersuite: tls_ciphersuite_t)But it seems that some cipher suites are enabled by default and I can't restrict the cipher suites just to the ones I want, I can just append others.NWProtocolTLS.Options class has an init() function which states "Initializes a default set of TLS connection options" on Apple documentation.So my question is, is there a way to know what TLS parameters this initialization does ? Especially the list of cipher suites enabled by default ? Because I can't find any information about it from my research. I used a tool to test handshake with my server to discover the cipher suites supported and enabled by default but I don't think it is a good way to be sure about this information.And is there a way to specify only cipher suites I want to be supported by my server by using NWProtocolTLS.Options ?Thank you in advance,Christophe
General
RSS for tagPrioritize user privacy and data security in your app. Discuss best practices for data handling, user consent, and security measures to protect user information.
Post
Replies
Boosts
Views
Activity
My existing chrome extension has "Sign in with Apple" given that we have iOS users.
When user clicks "Continue with Apple" button in the extension log in pop up, this is what we do:
javascript
window.open(
'https://appleid.apple.com/auth/authorize?client_id=' + clientID + '&redirect_uri=' + backEndURL + '&response_type=id_token%20code&response_mode=form_post&scope=email%20name',
'Sign in with Apple', 'height=500,width=400,left=600,top=200,status=no,location=no,toolbar=no,menubar=no'
)
In chrome, this opens a popup window with that URL.
In Safari Converted Web Extension, it opens custom Apple sign in flow, where it says:
"Do you want to sign in to *** with your Apple ID YYY?"
and then with my mac password I'm able to authenticate.
Afterwards, nothing happens.
Expected: a redirect to the URL specified in the window.open.
Now let's do a trick:
I'll wrap the above window.open code into
javascript
setTimeout (() = {window.open (...)}, 3000)
Because of security reasons, safari then won't open the popup after 3s and will display a notification in the toolbar "Popup blocked..".
If we allow the popup, then it finally opens as a normal window popup and after sign in, it redirects to our backend and it successfully authenticates.
Any ides what how to solve this?
P.S. We're not able to use embedded Sign in with Apple JS - https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/configuring_your_webpage_for_sign_in_with_apple script because we can't host a remote code in the extension (it will be deprecated soon). So, we arere using this. - https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms
Quinn, you've often suggested that to validate the other side of an XPC connection, we should use the audit token. But that's not available from the XPC object, whereas the PID is. So everyone uses the PID.
While looking for something completely unrelated, I found this in the SecCode.h file
OSStatus SecCodeCreateWithXPCMessage(xpc_object_t message, SecCSFlags flags,
SecCodeRef * __nonnull CF_RETURNS_RETAINED target);
Would this be the preferred way to do this now? At least from 11.0 and up.
Like I said, I was looking for something completely unrelated and found this and don't have the cycles right now to try it. But it looks promising from the description and I wanted to check in with you about it in case you can say yes or no before I get a chance to test it.
Thanks
Hi,
When attempting to perform a credential registration, I constantly get the error
Application with identifier <...> is not associated with domain <domain>
I've verified the following:
The file is present on the required URL path
https://123.com/.well-known/apple-app-site-association
{
"webcredentials": {
"apps": [ "<PREFIX>.bundleID" ]
}
}
Added the Capabilities to my domain
Confirmed that the CA is valid and there are no redirects.
Am I missing something? My device is able to properly access the file
I'm developing an authorization plugin to provide 2 Factor Authentication (2FA) for macOS. When FileVault is enabled, macOS Recovery prompts the user for a password to unlock FileVault FDE (Full Disk Encryption) before macOS can startup.
The FDE password entered during Recovery is saved somehow so that after macOS starts up it can be used to log the user in without prompting them to re-enter their password. This feature is configurable with setting 'DisableFDEAutoLogin'.
We would like our authorization plugin to implement the same behavior. The first place I thought to look for the FDE password (from within our authorization mechanism) is in Context value kAuthorizationEnvironmentPassword but it's not there.
Is it possible for an authorization plugin to obtain this password the same as the standard login mechanism and if so how?
I regularly help developers with code signing problems and I find that a lot of those problems stem from a fundamental misunderstanding of how code signing requests work. This post is my attempt at explaining that.
Note After posting this I then went on to write TN3161 Inside Code Signing: Certificates, which covers similar ground in a much more comprehensive way. I think this post still has value, but you should definitely start by reading TN3161.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Certificate Signing Requests Explained
I see a lot of folks confused by how code signing requests (CSRs) work, and that causes problems down the line. This is my attempt to explain the process and head off those problems.
IMPORTANT This post covers the ‘classic’ certificate creation process described in Developer Account Help > Create certificates > Create a certificate signing request. Things work differently if you use Xcode to create your certificates, and very differently if you use cloud-managed certificates.
Here’s a basic outline of the CSR process:
You run Keychain Access and choose Certificate Assistant > Request a Certificate from a Certificate Authority.
You run through the workflow as described in Developer Account Help > Create certificates > Create a certificate signing request.
This does two things:
It generates a public / private key pair in your keychain. To see these, run Keychain Access and select “login” on the left and Keys at the top. Look for keys whose names match the Common Name you entered in step 2.
It prompts you to save a .certSigningRequest file (CSR). This contains a copy of the public key.
You upload the CSR file to the developer web site.
The developer web site issues you a certificate. In human terms this certificate says “Apple certifies that the subject of this certificate holds the private key that matches the public key embedded in this certificate.”
Note The developer web site sets the subject information in the certificate based on your developer account. It ignores the subject information in the CSR. So, you can enter any information you want in step 2. This is a good way to distinguish between different keys in your keychain. For example, you might set the Common Name field in step 2 to include a unique identifier that allows you to easily identify the public / private key pair generated in step 3.
You download the certificate and add it to your keychain.
At this point your keychain contains a digital identity, that is, a certificate and the private key that matches the public key embedded in that certificate. To see this in Keychain Access, select “login” on the left and My Certificates at the top.
What’s This My Certificates Thing?
There’s an industry-wide terminology problem here. Folks use the term certificate to mean two different things:
A digital identity, that is, a certificate and its matching private key
An actual certificate
This industry-wide confusion extends into the Apple ecosystem. For example:
The Security framework gets this right, drawing a clear distinction between a digital identity (SecIdentity) and a certificate (SecCertificate).
Keychain Access uses My Certificates for digital identities.
Other user-facing apps use different terms. For example, Apple Configurator uses signing identity (yay for them!). OTOH, the help for Apple Mail uses the term personal certificate.
Xcode and its documentation uses the term signing certificate to denote a digital identity that can be used for code signing.
This terminological inexactitude causes all sorts of problems. For example, imagine you’re setting up a new Mac. You download your certificate from the developer web site and then wonder why you can’t sign your code. That’s because the developer web site gives you a certificate, not a digital identity. Indeed, the developer web site can’t give you a digital identity because it never got a copy of your private key [1].
[1] Again, we’re talking about the classic certificate creation process here; this statement is not true for cloud-managed certificates.
Digital Identity Formation
Apple platforms form a digital identity by:
Extracting the public key from the certificate.
Calculating a SHA-1 digest of that.
Looking for a private key whose kSecAttrApplicationLabel attribute matches that SHA-1 hash.
For more background on this, see my SecItem attributes for keys post.
Note that it’s perfectly valid for multiple certificates to match against the same private key, yielding a digital identity for each certificate. You regularly see this when you renew a certificate.
Looking Inside a CSR
A CSR is a PEM file (PEM is short for Privacy-Enhanced Mail) with the CERTIFICATE REQUEST label:
% cat CertificateSigningRequest.certSigningRequest
-----BEGIN CERTIFICATE REQUEST-----
MIICgjCCAWoCAQAwPTEcMBoGCSqGSIb3DQEJARYNZnJvZ0Bmcm9nLmNvbTEQMA4G
…
Ur9x5voYb6CafUBZMMiYw6aFXcgnsx4ZXxe8VEqNCarrQi+9tqitDD/bCuymT5Da
2+t64DGjpVM2lwtwqvH6Qh6QdPjkUw==
-----END CERTIFICATE REQUEST-----
To see inside, run the openssl tool as shown below:
% openssl req -in CertificateSigningRequest.certSigningRequest -text -noout
Certificate Request:
Data:
Version: 0 (0x0)
Subject: emailAddress=mrgumby@opendoor.com, CN=Mr Gumby, C=US
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b1:b4:a0:15:4d:4a:d7:29:1d:ed:d6:b7:c2:7c:
…
28:b9:8a:58:a4:04:63:fe:45:b2:4f:db:bd:93:20:
4e:8b
Exponent: 65537 (0x10001)
Attributes:
a0:00
Signature Algorithm: sha256WithRSAEncryption
80:f9:0e:73:8e:42:d8:3c:e3:e0:06:54:13:d7:48:ef:a8:71:
…
2f:74:e1:2e:cf:e7:ed:3e:64:b4:78:85:f4:ac:38:07:b1:15:
6b:3c:39:f9
For even more details, convert the file to DER form and then dump that as ASN.1:
% openssl req -in CertificateSigningRequest.certSigningRequest -out CertificateSigningRequest.der -outform der
% dumpasn1 -p -a CertificateSigningRequest.der
SEQUENCE {
SEQUENCE {
INTEGER 0
SEQUENCE {
SET {
SEQUENCE {
OBJECT IDENTIFIER emailAddress (1 2 840 113549 1 9 1)
IA5String 'mrgumby@opendoor.com'
}
}
SET {
SEQUENCE {
OBJECT IDENTIFIER commonName (2 5 4 3)
UTF8String 'Mr Gumby'
}
}
SET {
SEQUENCE {
OBJECT IDENTIFIER countryName (2 5 4 6)
PrintableString 'US'
}
}
}
SEQUENCE {
SEQUENCE {
OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
NULL
}
BIT STRING, encapsulates {
SEQUENCE {
INTEGER
00 B1 B4 A0 15 4D 4A D7 29 1D ED D6 B7 C2 7C 74
…
28 B9 8A 58 A4 04 63 FE 45 B2 4F DB BD 93 20 4E
8B
INTEGER 65537
}
}
}
[0]
Error: Object has zero length.
}
SEQUENCE {
OBJECT IDENTIFIER sha256WithRSAEncryption (1 2 840 113549 1 1 11)
NULL
}
BIT STRING
80 F9 0E 73 8E 42 D8 3C E3 E0 06 54 13 D7 48 EF
…
ED 3E 64 B4 78 85 F4 AC 38 07 B1 15 6B 3C 39 F9
}
I’m using the dumpasn1 tool, available here.
To extract the public key from the CSR, run this command:
% openssl req -in CertificateSigningRequest.certSigningRequest -noout -pubkey -out public.pem
% cat public.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsbSgFU1K1ykd7da3wnx0
…
FymGqUEcwfIISlG1C9VXyMRPzDcMrzjHm4i9qI9NliYouYpYpARj/kWyT9u9kyBO
iwIDAQAB
-----END PUBLIC KEY-----
To further explore that key, use the techniques in my On Cryptographic Key Formats post.
Revision History
2024-07-23 Updated to include a link to TN3161 Inside Code Signing: Certificates.
2022-11-03 First posted.
I am working on to revoke the apple access token on deletion of user account.
I hit the api, attaching a photo of the postman response
Even though I got the success response - 200, but the access token is still not revoke, Able to see the apps linked with Apple ID.
Please help me out to fix that issue
Trusted execution is a generic name for a Gatekeeper and other technologies that aim to protect users from malicious code.
General:
DevForums tag: Gatekeeper
Developer > Signing Mac Software with Developer ID
Apple Platform Security support document
Safely open apps on your Mac support article
Hardened Runtime document
WWDC 2022 Session 10096 What’s new in privacy covers some important Gatekeeper changes in macOS 13 (starting at 04: 32), most notably app bundle protection
WWDC 2023 Session 10053 What’s new in privacy covers an important change in macOS 14 (starting at 17:46), namely, app container protection
WWDC 2024 Session 10123 What’s new in privacy covers an important change in macOS 15 (starting at 12:23), namely, app group container protection
Updates to runtime protection in macOS Sequoia news post
Testing a Notarised Product DevForums post
Resolving Trusted Execution Problems DevForums post
App Translocation Notes DevForums post
Most trusted execution problems are caused by code signing or notarisation issues. See Code Signing Resources and Notarisation Resources.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
General:
Apple Platform Security support document
Security Overview
Cryptography:
DevForums tags: Security, Apple CryptoKit
Security framework documentation
Apple CryptoKit framework documentation
Common Crypto man pages — For the full list of pages, run:
% man -k 3cc
For more information about man pages, see Reading UNIX Manual Pages.
On Cryptographic Key Formats DevForums post
SecItem attributes for keys DevForums post
CryptoCompatibility sample code
Keychain:
DevForums tags: Security
Security > Keychain Items documentation
TN3137 On Mac keychain APIs and implementations
SecItem Fundamentals DevForums post
SecItem Pitfalls and Best Practices DevForums post
Investigating hard-to-reproduce keychain problems DevForums post
Smart cards and other secure tokens:
DevForums tag: CryptoTokenKit
CryptoTokenKit framework documentation
Mac-specific frameworks:
DevForums tags: Security Foundation, Security Interface
Security Foundation framework documentation
Security Interface framework documentation
Related:
Networking Resources — This covers high-level network security, including HTTPS and TLS.
Network Extension Resources — This covers low-level network security, including VPN and content filters.
Code Signing Resources
Notarisation Resources
Trusted Execution Resources — This includes Gatekeeper.
App Sandbox Resources
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
In iOS 16, UIDevice.name has changed to only return the model of the device, not the user specified name.
There is an entitlement, com.apple.developer.device-information.user-assigned-device-name that can be requested to keep the old behaviour, but I can't find any info on how to request that entitlement.
Anyone able to help?
The revoke tokens endpoint (/auth/revoke) is the only way to programmatically invalidate user tokens associated to your developer account without user interaction. This endpoint requires either a valid refresh token or access token for invalidation, as Sign in with Apple expects all apps to securely transmit and store these tokens for validation and user identity verification while managing user sessions.
If you don’t have the user’s refresh token, access token, or authorization code, you must still fulfill the user’s account deletion request and meet the account deletion requirement. You'll need to follow this workaround to manually revoke the user credentials:
Delete the user’s account data from your systems.
Direct the user to manually revoke access for your client.
Respond to the credential revoked notification to revert the client to an unauthenticated state
Important: If the manual token revocation isn’t completed, the next time the user authenticates with your client using Sign in with Apple, they won’t be presented with the initial authorization flow to enter their full name, email address, or both. This is because the user credential state managed by Sign in with Apple remains unchanged and returns the.authorizedcredential state, which may also result in the system auth UI displaying the “Continue with Apple” button.
Respond to the credential revoked notification
Once the user’s credentials are revoked by Apple, your client will receive a notification signaling the revocation event:
For apps using the Authentication Services framework to implement Sign in with Apple, register to observe the notification named credentialRevokedNotification.
For web services, if an endpoint is registered for server-to-server notifications, Apple broadcasts a notification to the specified endpoint with the consent-revokedevent type.
When receiving either notification, ensure you’ve already performed the following operations to meet the requirements of account deletion:
Deleted all user-related account data, including:
The token used for token revocation;
Any user-related data stored in your app servers; and
Any user-related data store in the Keychain or securely on disk in the native app or locally on web client.
Reverted the client to an unauthenticated state.
Securely store user tokens for account creations
For all new user account creations, follow the expected authorization flow below:
Securely transmit the identity token and authorization code to your app server.
Verify the identity token and validate the authorization code using the /auth/token endpoint.
Once the authorization code is validated, securely store the token response — including the identity token, refresh token, and access token.
Validate the refresh token up to once per day with Apple servers (to manage the lifetime of your user session and for future token revocation requests), and obtain access tokens (for future token revocation, app transfer, or user migration requests).
For information about verifying an identity token and validating tokens, visit Verifying a user and Generate and validate tokens.
If you have questions about implementing these flows, including client authorization, token validation, or token revocation, please submit a Technical Support Incident.
Hi, I am trying to implement encryption and decryption with EC signing keys. in the process I am getting the following error while creating a SecKey from the private key.
Error in creating a secKey Optional(Swift.Unmanaged<__C.CFErrorRef>(_value: Error Domain=NSOSStatusErrorDomain Code=-50 "EC private key creation from data failed" (paramErr: error in user parameter list) UserInfo={numberOfErrorsDeep=0, NSDescription=EC private key creation from data failed}))
Code snippet for decryption
func decrypt(data: Data, key: SecureEnclave.P256.Signing.PrivateKey) throws -> Data? {
var error: Unmanaged<CFError>?
let privateKeyData: CFData = key.dataRepresentation as CFData
let privateKeyAttributes = [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass: kSecAttrKeyClassPrivate] as CFDictionary
guard let SecKey = SecKeyCreateWithData(privateKeyData, privateKeyAttributes as CFDictionary, &error)
else {
print("Error in creating a secKey", error)
return nil
}
guard SecKeyIsAlgorithmSupported(SecKey, .decrypt, EncryptAndDecryptAlogrithm)
else {
print("Decryption algorithm is not supported", error)
return nil
}
guard let decryptedData = SecKeyCreateDecryptedData(SecKey, EncryptAndDecryptAlogrithm, data as CFData, &error) else {
print("Error in decryption", error)
return nil
}
return decryptedData as Data
}
let data = Data(base64Encoded: "BNtHrb1cZuflSDZz+E3PnIkLtYUQuBDW+ONlzuAypZcQa+5oKv0L0wSIBMMseMr0roloexPwTaVV26ddewTP0+vRt9v6uLOg366cElMo6P5nh2K7xKi1PMcRyBVel+Kq9WQWT/EkRIuUkHdq2KLXy/Q=")!
let alice = try SecureEnclave.P256.Signing.PrivateKey()
let decryptedData = try decrypt(data: data, key:alice)
Thank you in advance.
I am trying to use SecItemUpdate in order to change the kSecAttrAccessControl value on a private key protected by the Secure Enclave as well as an .applicationPassword - which I want to change. I have been unsuccessful getting the query and attributesToUpdate dictionaries right though, with SecItemUpdate returning either errSecParam, errSecNoSuchAttr or errSecAuthFailed. Am I on the right track here or am I trying to do something that is not possible?
I regularly see folks confused by the difference in behaviour of app groups between macOS and iOS. One day I’ll have time to write this up for the official docs (r. 92322409) but, in the meantime, here’s a quick overview.
[Well, it was a quick overview. Things have got considerably more complicated in recent years.]
If you have questions or comments, start a new thread with the details. Put it in the Privacy & Security > General topic area and tag it with Code Signing and Entitlements. Oh, and if this is about app group container protection, also include Files and Storage.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
App Groups: macOS vs iOS: Fight!
The app groups mechanism works differently on macOS and iOS. On iOS:
App group IDs start with the group. prefix.
To use an app group ID, first allocate it on the Developer website. This associates the app group ID with your team.
Then claim the app group ID in your app’s App Groups entitlement (com.apple.security.application-groups) entitlement.
Like all entitlements on iOS, that claim must be authorised by a provisioning profile. A profile will only authorise an app group ID that’s allocated by your team.
For more background on provisioning profiles, see TN3125 Inside Code Signing: Provisioning Profiles.
In contrast, on macOS:
App group IDs typically start with your Team ID.
They can’t be explicitly allocated on the Developer website.
Code that isn’t sandboxed doesn’t need to claim the app group ID in the App Groups entitlement. [1]
To use an app group, claim the app group ID in the App Groups entitlement.
The App Groups entitlement is not restricted, meaning that this claim doesn’t need to be authorised by a provisioning profile.
The App Store submission process checks that your app group IDs make sense.
IMPORTANT In this context I’m using macOS to refer to a standard macOS app. In Mac Catalyst things behave as they do on iOS. Likewise for iOS Apps on Mac. Also, anything I say about iOS also applies to tvOS, watchOS, and visionOS.
This difference is a product of the way that each platform protects app group content. On iOS the Developer website enforces group uniqueness, that is, the site prevents team B from using an app group ID that’s assigned to team A. In contrast, on macOS:
App group IDs are prefixed with the Team ID solely to prevent collisions.
The Mac App Store prevents you from publishing an app that uses an app group ID that’s used by another team.
In macOS 15 and later, all apps are subject to app group container protection.
[1] This was true prior to macOS 15. It may still technically be true in macOS 15 and later, but the most important thing, access to the app group container, requires the entitlement because of app group container protection.
Crossing the Streams
[… and mixing my pop culture metaphors!]
In some circumstances you might need to share an app group between iOS and macOS code. For example, you might have a Mac app that needs to share an app group with:
A Mac Catalyst app
An iOS app that runs on macOS via iOS Apps on Mac
The solution is to use an iOS-style app group ID in your Mac app. To do this:
Confirm that the app group ID is registered to your team on the Developer website.
Claim the app group ID in the App Groups entitlement.
If you submit that app to the Mac App Store, the submission process checks that your app group ID claims make sense, that is, they either follow the macOS convention (use a prefix of the Team ID) or the iOS convention (allocate a group ID, with the group. prefix, on the Developer website).
IMPORTANT Due to app group container protection, this approach is only viable for Mac App Store apps. For more details, see App Group Container Protection, below.
App Groups and the Keychain
The differences described above explain an oddity associated with keychain access. Consider this quote from Sharing Access to Keychain Items Among a Collection of Apps:
Application groups
When you collect related apps into an application group using the App
Groups entitlement, they share access to a group container,
and gain the ability to message each other in certain ways. Starting
in iOS 8, the array of strings given by this entitlement also extends
the list of keychain access groups.
There are three things to note here:
Using an app group ID as a keychain access group only works on iOS; it’s not supported on macOS [1] because doing so would be insecure.
The App Groups entitlement must be authorised by a provisioning profile on iOS, and that process is what protects the keychain from unauthorised access.
The required group. prefix means that these keychain access groups can’t collide with other keychain access groups, which all start with an App ID prefix (there’s also Apple-only keychain access groups that start with other prefixes, like apple).
In contrast, standard keychain access groups are protected the same way on both platforms, using the Keychain Access Groups entitlement (keychain-access-groups).
[1] Except for iOS Apps on Mac.
Not Entirely Unsatisfied
When you launch a Mac app that uses app groups you might see this log entry:
type: error
time: 10:41:35.858009+0000
process: taskgated-helper
subsystem: com.apple.ManagedClient
category: ProvisioningProfiles
message: com.example.apple-samplecode.Test92322409: Unsatisfied entitlements: com.apple.security.application-groups
Note The exact format of that log entry, and the circumstances under which it’s generated, varies by platform. On macOS 13.0.1 I was able to generate it by running a sandboxed app that claims the App Group entitlement and also claims some other restricted entitlement.
This looks kinda worrying and can be the source of problems. You see this error when you have a sandboxed app that uses an app group. In a sandboxed app your use of the app group must be authorised by the App Groups entitlement. This message is telling you that your use of the App Groups entitlement is not authorised by your provisioning profile.
On iOS this would be a show stopper. The trusted execution system would prevent your app from launching at all.
On macOS that’s not the case. The trusted execution system knows that there’s no way to get a Mac provisioning profile that authorises the App Groups entitlement, and thus it allows the app to launch anyway.
However, that’s not the end of the story. You might run into problems with:
macOS 15’s app group container protection
The entitlements validated flag
App Group Container Protection
macOS 15 introduced app group container protection. To access an app group container without user intervention:
Claim access to the app group by listing its ID in the App Groups entitlement.
Locate the container by calling the containerURL(forSecurityApplicationGroupIdentifier:) method.
Ensure that at least one of the following criteria are met:
Your app is deployed via the Mac App Store (A).
Or via TestFlight when running on macOS 15.1 or later (B).
Or the app group ID starts with your app’s Team ID (C).
Or your app’s claim to the app group is authorised by a provisioning profile embedded in the app (D) [1].
If your app doesn’t follow these rules, the system prompts the user to approve its access to the container. If granted, that consent applies only for the duration of that app instance.
For more on this, see:
The System Integrity Protection section of the macOS Sequoia 15 Release Notes
The System Integrity Protection section of the macOS Sequoia 15.1 Release Notes
WWDC 2024 Session 10123 What’s new in privacy, starting at 12:23
The above criteria mean that you rarely run into the app group authorisation prompt when your app is deployed. If you encounter a case where that happens, feel free to start a thread here on DevForums. See the top of this post for info on the topic and tags to use.
However, you might run into some issues during development:
If you have a multiplatform app built from a single target — for example, if you created the project from the Multiplatform > App template — Xcode’s Signing & Capabilities editor doesn’t understand all of these app group nuances. To work around this, conditionalise the entitlements file build setting. See this thread for more.
If you use an iOS-style app group ID in a macOS app, you might run into the authorisation prompt during day-to-day development. One way around this is to use a macOS-style app group ID during development and switch to the iOS-style app group ID for production.
[1] This is what allows Mac Catalyst and iOS Apps on Mac to work.
Entitlements Validated Flag
If your app claims the app group entitlement but that claim isn’t authorised by a provisioning profile, the trusted execution system allows the app to launch but it clears its entitlements validated flag. Some subsystems that rely on entitlements will fail in this case. The most notable example of this is the data protection keychain.
Note If you’re curious about this flag, use the procinfo subcommand of launchctl to view it. For example:
% sudo launchctl procinfo `pgrep Test20230126`
…
code signing info = valid
…
entitlements validated
…
If the flag has been cleared, this line will be missing from the code signing info section.
The practical impact of this is that, for a sandboxed app on macOS, you can either use app groups or use the data protection keychain, but not both. Needless to say, this is less than ideal (r. 104859788).
IMPORTANT This doesn’t stop you using the keychain in general. You can still use the file-based keychain. For more information about these terms, see TN3137 On Mac keychain APIs and implementations.
One place this often crops up is with Network Extension (NE) framework system extensions. These must be sandboxed and often use an app group as part of their IPC story. Specifically, they might want to publish an XPC named endpoint and, when doing that, the name listed in NEMachServiceName must be a ‘child’ of an app group.
Fortunately, system extensions are effectively daemons and so can’t use the data protection keychain anyway. So, if you’re building an NE system extension, this message is probably nothing to be worried about.
If you’re building some other program that’s affected by this, open a thread here on DevForums and let’s talk. See the top of this post for info on the topic and tags to use.
Revision History
2024-11-05 Further clarified app group container protection. Reworked some other sections to account for this new reality.
2024-10-29 Clarified the points in App Group Container Protection.
2024-10-23 Fleshed out the discussion of app group container protection on macOS 15.
2024-09-04 Added information about app group container protection on macOS 15.
2023-01-31 Renamed the Not Entirely Unsatisfactory section to Not Entirely Unsatisfied. Updated it to describe the real impact of that log message.
2022-12-12 First posted.
There is frequently a delay of a few seconds before a DeviceActivityReport renders its view generated from the DeviceActivityReportExtension. It will also sometimes flash with zero data before hydrating with the real activity data (tested with extension code taken directly from XCode boilerplate)
Is there a way to be notified when the DeviceActivityReport renders successfully or is still processing, i.e. so a loading indicator can be presented while the extension runs?
Thanks!
If you’re on macOS and targeting the file-based keychain, kSecMatchLimitAll always defaults to kSecMatchLimitOne
I regularly help developers with keychain problems, both here on DevForums and for my Day Job™ in DTS. Over the years I’ve learnt a lot about the API, including many pitfalls and best practices. This post is my attempt to collect that experience in one place.
If you have questions or comments about any of this, put them in a new thread and apply the Security tag so that I see it.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
SecItem: Pitfalls and Best Practices
It’s just four functions, how hard can it be?
The SecItem API seems very simple. After all, it only has four function calls, how hard can it be? In reality, things are not that easy. Various factors contribute to making this API much trickier than it might seem at first glance.
This post explains some of the keychain’s pitfalls and then goes on to explain various best practices. Before reading this, make sure you understand the fundamentals by reading its companion post, SecItem: Fundamentals.
Pitfalls
Lets start with some common pitfalls.
Queries and Uniqueness Constraints
The relationship between query dictionaries and uniqueness constraints is a major source of problems with the keychain API. Consider code like this:
var copyResult: CFTypeRef? = nil
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "AYS",
kSecAttrAccount: "mrgumby",
kSecAttrGeneric: Data("SecItemHints".utf8),
] as NSMutableDictionary
let err = SecItemCopyMatching(query, ©Result)
if err == errSecItemNotFound {
query[kSecValueData] = Data("opendoor".utf8)
let err2 = SecItemAdd(query, nil)
if err2 == errSecDuplicateItem {
fatalError("… can you get here? …")
}
}
Can you get to the fatal error?
At first glance this might not seem possible because you’ve run your query and it’s returned errSecItemNotFound. However, the fatal error is possible because the query contains an attribute, kSecAttrGeneric, that does not contribute to the uniqueness. If the keychain contains a generic password whose service (kSecAttrService) and account (kSecAttrAccount) attributes match those supplied but who’s generic (kSecAttrGeneric) attribute does not, the SecItemCopyMatching calls will return errSecItemNotFound. However, for a generic password item, of the attributes shown here, only the service and account attributes are included in the uniqueness constraint. If you try to add an item where those attributes match an existing item, the add will fail with errSecDuplicateItem even though the value of the generic attribute is different.
The take-home point is that that you should study the attributes that contribute to uniqueness and use them in a way that’s aligned with your view of uniqueness. See the Uniqueness section of SecItem: Fundamentals for a link to the relevant documentation.
Erroneous Attributes
Each keychain item class supports its own specific set of attributes. For information about the attributes supported by a given class, see SecItem: Fundamentals.
I regularly see folks use attributes that aren’t supported by the class they’re working with. For example, the kSecAttrApplicationTag attribute is only supported for key items (kSecClassKey). Using it with a certificate item (kSecClassCertificate) will cause, at best, a runtime error and, at worst, mysterious bugs.
This is an easy mistake to make because:
The ‘parameter block’ nature of the SecItem API means that the compiler won’t complain if you use an erroneous attribute.
On macOS, the shim that connects to the file-based keychain ignores unsupported attributes.
Imagine you want to store a certificate for a particular user. You might write code like this:
let err = SecItemAdd([
kSecClass: kSecClassCertificate,
kSecAttrApplicationTag: Data(name.utf8),
kSecValueRef: cert,
] as NSDictionary, nil)
The goal is to store the user’s name in the kSecAttrApplicationTag attribute so that you can get back their certificate with code like this:
let err = SecItemCopyMatching([
kSecClass: kSecClassCertificate,
kSecAttrApplicationTag: Data(name.utf8),
kSecReturnRef: true,
] as NSDictionary, ©Result)
On iOS, and with the data protection keychain on macOS, both calls will fail with errSecNoSuchAttr. That makes sense, because the kSecAttrApplicationTag attribute is not supported for certificate items. Unfortunately, the macOS shim that connects the SecItem API to the file-based keychain ignores extraneous attributes. This results in some very bad behaviour:
SecItemAdd works, ignoring kSecAttrApplicationTag.
SecItemCopyMatching ignores kSecAttrApplicationTag, returning the first certificate that it finds.
If you only test with a single user, everything seems to work. But, later on, when you try your code with multiple users, you might get back the wrong result depending on the which certificate the SecItemCopyMatching call happens to discover first.
Ouch!
Context Matters
Some properties change behaviour based on the context. The value type properties are the biggest offender here, as discussed in the Value Type Subtleties section of SecItem: Fundamentals. However, there are others.
The one that’s bitten me is kSecMatchLimit:
In a query and return dictionary its default value is kSecMatchLimitOne. If you don’t supply a value for kSecMatchLimit, SecItemCopyMatching returns at most one item that matches your query.
In a pure query dictionary its default value is kSecMatchLimitAll. For example, if you don’t supply a value for kSecMatchLimit, SecItemDelete will delete all items that match your query. This is a lesson that, once learnt, is never forgotten!
Note Although this only applies to the data-protection keychain. If you’re on macOS and targeting the file-based keychain, kSecMatchLimit always defaults to kSecMatchLimitOne (r. 105800863). Fun times!
Digital Identities Aren’t Real
A digital identity is the combination of a certificate and the private key that matches the public key within that certificate. The SecItem API has a digital identity keychain item class, namely kSecClassIdentity. However, the keychain does not store digital identities. When you add a digital identity to the keychain, the system stores its components, the certificate and the private key, separately, using kSecClassCertificate and kSecClassKey respectively.
This has a number of non-obvious effects:
Adding a certificate can ‘add’ a digital identity. If the new certificate happens to match a private key that’s already in the keychain, the keychain treats that pair as a digital identity.
Likewise when you add a private key.
Similarly, removing a certificate or private key can ‘remove’ a digital identity.
Adding a digital identity will either add a private key, or a certificate, or both, depending on what’s already in the keychain.
Removing a digital identity removes its certificate. It might also remove the private key, depending on whether that private key is used by a different digital identity.
The system forms a digital identity by matching the kSecAttrApplicationLabel (klbl) attribute of the private key with the kSecAttrPublicKeyHash (pkhh) attribute of the certificate. If you add both items to the keychain and the system doesn’t form an identity, check the value of these attributes.
For more information the key attributes, see SecItem attributes for keys.
Keys Aren’t Stored in the Secure Enclave
Apple platforms let you protect a key with the Secure Enclave (SE). The key is then hardware bound. It can only be used by that specific SE [1].
Earlier versions of the Protecting keys with the Secure Enclave article implied that SE-protected keys were stored in the SE itself. This is not true, and it’s caused a lot of confusion. For example, I once asked the keychain team “How much space does the SE have available to store keys?”, a question that’s complete nonsense once you understand how this works.
In reality, SE-protected keys are stored in the standard keychain database alongside all your other keychain items. The difference is that the key is wrapped in such a way that only the SE can use it. So, the key is protected by the SE, not stored in the SE.
A while back we updated the docs to clarify this point but the confusion persists.
[1] Technically it’s that specific iteration of that specific SE. If you erase the device then the key material needed to use the key is erased and so the key becomes permanently useless. This is the sort of thing you’ll find explained in Apple Platform Security.
Careful With that Shim, Mac Developer
As explained in TN3137 On Mac keychain APIs and implementations, macOS has a shim that connects the SecItem API to either the data protection keychain or the file-based keychain depending on the nature of the request. That shim has limitations. Some of those are architectural but others are simply bugs in the shim. For some great examples, see the Investigating Complex Attributes section below.
The best way to avoid problems like this is to target the data protection keychain. If you can’t do that, try to avoid exploring the outer reaches of the SecItem API. If you encounter a case that doesn’t make sense, try that same case with the data protection keychain. If it works there but fails with the file-based keychain, please do file a bug against the shim. It’ll be in good company.
Add-only Attributes
Some attributes can only be set when you add an item. These attributes are usually associated with the scope of the item. For example, to protect an item with the Secure Enclave, supply the kSecAttrAccessControl attribute to the SecItemAdd call. Once you do that, however, you can’t change the attribute. Calling SecItemUpdate with a new kSecAttrAccessControl won’t work.
Lost Keychain Items
A common complaint from developers is that a seemingly minor update to their app has caused it to lose all of its keychain items. Usually this is caused by one of two problems:
Entitlement changes
Query dictionary confusion
Access to keychain items is mediated by various entitlements, as described in Sharing access to keychain items among a collection of apps. If the two versions of your app have different entitlements, one version may not be able to ‘see’ items created by the other.
Imagine you have an app with an App ID of SKMME9E2Y8.com.example.waffle-varnisher. Version 1 of your app is signed with the keychain-access-groups entitlement set to [ SKMME9E2Y8.groupA, SKMME9E2Y8.groupB ]. That makes its keychain access group list [ SKMME9E2Y8.groupA, SKMME9E2Y8.groupB, SKMME9E2Y8.com.example.waffle-varnisher ]. If this app creates a new keychain item without specifying kSecAttrAccessGroup, the system places the item into SKMME9E2Y8.groupA. If version 2 of your app removes SKMME9E2Y8.groupA from the keychain-access-groups, it’ll no longer be able to see the keychain items created by version 1.
You’ll also see this problem if you change your App ID prefix, as described in App ID Prefix Change and Keychain Access.
IMPORTANT When checking for this problem, don’t rely on your .entitlements file. There are many steps between it and your app’s actual entitlements. Rather, run codesign to dump the entitlements of your built app:
% codesign -d --entitlements - /path/to/your.app
Lost Keychain Items, Redux
Another common cause of lost keychain items is confusion about query dictionaries, something discussed in detail in this post and SecItem: Fundamentals. If SecItemCopyMatching isn’t returning the expected item, add some test code to get all the items and their attributes. For example, to dump all the generic password items, run code like this:
func dumpGenericPasswords() throws {
let itemDicts = try secCall {
SecItemCopyMatching([
kSecClass: kSecClassGenericPassword,
kSecMatchLimit: kSecMatchLimitAll,
kSecReturnAttributes: true,
] as NSDictionary, $0)
} as! [[String: Any]]
print(itemDicts)
}
Then compare each item’s attributes against the attributes you’re looking for to see why there was no match.
Best Practices
With the pitfalls out of the way, let’s talk about best practices.
Less Painful Dictionaries
I look at a lot of keychain code and it’s amazing how much of it is way more painful than it needs to be. The biggest offender here is the dictionaries. Here are two tips to minimise the pain.
First, don’t use CFDictionary. It’s seriously ugly. While the SecItem API is defined in terms of CFDictionary, you don’t have to work with CFDictionary directly. Rather, use NSDictionary and take advantage of the toll-free bridge.
For example, consider this CFDictionary code:
CFTypeRef keys[4] = {
kSecClass,
kSecAttrService,
kSecMatchLimit,
kSecReturnAttributes,
};
static const int kTen = 10;
CFNumberRef ten = CFNumberCreate(NULL, kCFNumberIntType, &kTen);
CFAutorelease(ten);
CFTypeRef values[4] = {
kSecClassGenericPassword,
CFSTR("AYS"),
ten,
kCFBooleanTrue,
};
CFDictionaryRef query = CFDictionaryCreate(
NULL,
keys,
values,
4,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks
);
Note This might seem rather extreme but I’ve literally seen code like this, and worse, while helping developers.
Contrast this to the equivalent NSDictionary code:
NSDictionary * query = @{
(__bridge NSString *) kSecClass: (__bridge NSString *) kSecClassGenericPassword,
(__bridge NSString *) kSecAttrService: @"AYS",
(__bridge NSString *) kSecMatchLimit: @10,
(__bridge NSString *) kSecReturnAttributes: @YES,
};
Wow, that’s so much better.
Second, if you’re working in Swift, take advantage of its awesome ability to create NSDictionary values from Swift dictionary literals. Here’s the equivalent code in Swift:
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "AYS",
kSecMatchLimit: 10,
kSecReturnAttributes: true,
] as NSDictionary
Nice!
Avoid Reusing Dictionaries
I regularly see folks reuse dictionaries for different SecItem calls. For example, they might have code like this:
var copyResult: CFTypeRef? = nil
let dict = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "AYS",
kSecAttrAccount: "mrgumby",
kSecReturnData: true,
] as NSMutableDictionary
var err = SecItemCopyMatching(dict, ©Result)
if err == errSecItemNotFound {
dict[kSecValueData] = Data("opendoor".utf8)
err = SecItemAdd(dict, nil)
}
This specific example will work, but it’s easy to spot the logic error. kSecReturnData is a return type property and it makes no sense to pass it to a SecItemAdd call whose second parameter is nil.
I’m not sure why folks do this. I think it’s because they think that constructing dictionaries is expensive. Regardless, this pattern can lead to all sorts of weird problems. For example, it’s the leading cause of the issue described in the Queries and the Uniqueness Constraints section, above.
My advice is that you use a new dictionary for each call. That prevents state from one call accidentally leaking into a subsequent call. For example, I’d rewrite the above as:
var copyResult: CFTypeRef? = nil
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "AYS",
kSecAttrAccount: "mrgumby",
kSecReturnData: true,
] as NSMutableDictionary
var err = SecItemCopyMatching(query, ©Result)
if err == errSecItemNotFound {
let add = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "AYS",
kSecAttrAccount: "mrgumby",
kSecValueData: Data("opendoor".utf8),
] as NSMutableDictionary
err = SecItemAdd(add, nil)
}
It’s a bit longer, but it’s much easier to track the flow. And if you want to eliminate the repetition, use a helper function:
func makeDict() -> NSMutableDictionary {
[
kSecClass: kSecClassGenericPassword,
kSecAttrService: "AYS",
kSecAttrAccount: "mrgumby",
] as NSMutableDictionary
}
var copyResult: CFTypeRef? = nil
let query = makeDict()
query[kSecReturnData] = true
var err = SecItemCopyMatching(query, ©Result)
if err == errSecItemNotFound {
let add = makeDict()
query[kSecValueData] = Data("opendoor".utf8)
err = SecItemAdd(add, nil)
}
Think Before Wrapping
A lot of folks look at the SecItem API and immediately reach for a wrapper library. A keychain wrapper library might seem like a good idea but there are some serious downsides:
It adds another dependency to your project.
Different subsystems within your project may use different wrappers.
The wrapper can obscure the underlying API. Indeed, its entire raison d’être is to obscure the underlying API. This is problematic if things go wrong. I regularly talk to folks with hard-to-debug keychain problems and the conversation goes something like this:
Quinn: What attributes do you use in the query dictionary?
J R Developer: What’s a query dictionary?
Quinn: OK, so what error are you getting back?
J R Developer: It throws WrapperKeychainFailedError.
That’s not helpful )-:
If you do use a wrapper, make sure it has diagnostic support that includes the values passed to and from the SecItem API. Also make sure that, when it fails, it returns an error that includes the underlying keychain error code. These benefits will be particularly useful if you encounter a keychain problem that only shows up in the field.
Wrappers must choose whether to be general or specific. A general wrapper may be harder to understand than the equivalent SecItem calls, and it’ll certainly contain a lot of complex code. On the other hand, a specific wrapper may have a model of the keychain that doesn’t align with your requirements.
I recommend that you think twice before using a keychain wrapper. Personally I find the SecItem API relatively easy to call, assuming that:
I use the techniques shown in Less Painful Dictionaries, above, to avoid having to deal with CFDictionary.
I use my secCall(…) helpers to simplify error handling. For the code, see Calling Security Framework from Swift.
If you’re not prepared to take the SecItem API neat, consider writing your own wrapper, one that’s tightly focused on the requirements of your project. For example, in my VPN apps I use the wrapper from this post, which does exactly what I need in about 100 lines of code.
Prefer to Update
Of the four SecItem functions, SecItemUpdate is the most neglected. Rather than calling SecItemUpdate I regularly see folks delete and then re-add the item. This is a shame because SecItemUpdate has some important benefits:
It preserves persistent references. If you delete and then re-add the item, you get a new item with a new persistent reference.
It’s well aligned with the fundamental database nature of the keychain. It forces you to think about which attributes uniquely identify your item and which items can be updated without changing the item’s identity.
Understand These Key Attributes
Key items have a number of attributes that are similarly named, and it’s important to keep them straight. I created a cheat sheet for this, namely, SecItem attributes for keys. You wouldn’t believe how often I consult this!
Investigating Complex Attributes
Some attributes have values where the format is not obvious. For example, the kSecAttrIssuer attributed is documented as:
The corresponding value is of type CFData and contains the X.500
issuer name of a certificate.
What exactly does that mean? If I want to search the keychain for all certificates issued by a specific certificate authority, what value should I supply?
One way to figure this out is to add a certificate to the keychain, read the attributes back, and then dump the kSecAttrIssuer value. For example:
let cert: SecCertificate = …
let attrs = try secCall { SecItemAdd([
kSecValueRef: cert,
kSecReturnAttributes: true,
] as NSDictionary, $0) } as! [String: Any]
let issuer = attrs[kSecAttrIssuer as String] as! NSData
print((issuer as NSData).debugDescription)
// prints: <3110300e 06035504 030c074d 6f757365 4341310b 30090603 55040613 024742>
Those bytes represent the contents of a X.509 Name ASN.1 structure with DER encoding. This is without the outer SEQUENCE element, so if you dump it as ASN.1 you’ll get a nice dump of the first SET and then a warning about extra stuff at the end of the file:
% xxd issuer.asn1
00000000: 3110 300e 0603 5504 030c 074d 6f75 7365 1.0...U....Mouse
00000010: 4341 310b 3009 0603 5504 0613 0247 42 CA1.0...U....GB
% dumpasn1 -p issuer.asn1
SET {
SEQUENCE {
OBJECT IDENTIFIER commonName (2 5 4 3)
UTF8String 'MouseCA'
}
}
Warning: Further data follows ASN.1 data at position 18.
Note For details on the Name structure, see section 4.1.2.4 of RFC 5280.
Amusingly, if you run the same test against the file-based keychain you’ll… crash. OK, that’s not amusing. It turns out that the code above doesn’t work when targeting the file-based keychain because SecItemAdd doesn’t return a dictionary but rather an array of dictionaries (r. 21111543). Once you get past that, however, you’ll see it print:
<301f3110 300e0603 5504030c 074d6f75 73654341 310b3009 06035504 06130247 42>
Which is different! Dumping it as ASN.1 shows that it’s the full Name structure, including the outer SEQUENCE element:
% xxd issuer-file-based.asn1
00000000: 301f 3110 300e 0603 5504 030c 074d 6f75 0.1.0...U....Mou
00000010: 7365 4341 310b 3009 0603 5504 0613 0247 seCA1.0...U....G
00000020: 42 B
% dumpasn1 -p issuer-file-based.asn1
SEQUENCE {
SET {
SEQUENCE {
OBJECT IDENTIFIER commonName (2 5 4 3)
UTF8String 'MouseCA'
}
}
SET {
SEQUENCE {
OBJECT IDENTIFIER countryName (2 5 4 6)
PrintableString 'GB'
}
}
}
This difference in behaviour between the data protection and file-based keychains is a known bug (r. 26391756) but in this case it’s handy because the file-based keychain behaviour makes it easier to understand the data protection keychain behaviour.
App Groups on the Mac
Sharing access to keychain items among a collection of apps explains that three entitlements determine your keychain access:
keychain-access-groups
application-identifier (com.apple.application-identifier on macOS)
com.apple.security.application-groups
In the discussion of com.apple.security.application-groups it says:
Starting in iOS 8, the array of strings given by this
entitlement also extends the list of keychain access groups.
That’s true, but it’s also potentially misleading. This affordance only works on iOS and its child platforms. It doesn’t work on macOS.
That’s because app groups work very differently on macOS than they do on iOS. For all the details, see App Groups: macOS vs iOS: Fight!. However, the take-home point is that, when you use the data protection keychain on macOS, your keychain access group list is built from keychain-access-groups and com.apple.application-identifier.
Revision History
2024-08-29 Added a discussion of identity formation to the Digital Identities Aren’t Real section.
2024-04-11 Added the App Groups on the Mac section.
2023-10-25 Added the Lost Keychain Items and Lost Keychain Items, Redux sections.
2023-09-22 Made minor editorial changes.
2023-09-12 Fixed various bugs in the revision history. Added the Erroneous Attributes section.
2023-02-22 Fixed the link to the VPNKeychain post. Corrected the name of the Context Matters section. Added the Investigating Complex Attributes section.
2023-01-28 First posted.
We have used Forticlient AV/VPN clients in our environment and I find that after using their client uninstaller there are items still listed in the privacy settings for Full Disk Access. The files they reference have been removed but the TCC.db still contains the "Allowed" setting. I have run into this issue in Big Sur, Monterey and Ventura.
Using the tccutil reset SystemPolicyAllFiles removes them from the GUI (and everything else unfortunately from Full Disk Access) but I find that when querying the TCC.db they still exist with Full Disk Access. Trying to target by bundleID just generates an error. (i.e. tccutil reset SystemPolicyAllFiles com.forticlient.FortiClient returns tccutil: No such bundle identifier "com.fortinet.FortiClientAgent": The operation couldn’t be completed. (OSStatus error -10814.)
With the handy script from Mac-Nerd I can see that there are still entries.
Mac-TCC-DB-Translator.sh output (excerpted)
How can I clear these entries from the TCC.db other than deleting the file? That would be excessively destructive in our environment and troublesome for users.
Please excuse my lack of understanding of what are probably fundamental concepts in iOS/iPadOS development but I have searched far and wide for documentation and haven't had much luck so far. I am not sure that what I want to do is even possible with an iPad iPadOS app.
Goals: Develop a Swift iPadOS app that can digitally sign a
file using a PIV SmartCard/Token (Personal Identity Verification Card):
Insert a PIV SmartCard/Token (such as a Yubikey 5Ci) into the lightning port of an iPadOS device iPad (NOT MacOS)
Interface with the SmartCard/Token to access the user's PIV certificate/signature and "use it" to sign a file
Question 1: How to get the PIV Certificate from
SmartCard/Token/Yubikey into iPadOS keychain?
* Do we need to get the PIV certificate into the
iOS keychain? Is there another way to interact with a SmartCard directly?
* This should prompt the user for their PIN?
Question 2: How to get our Swift app to hook into the event
that the SmartCard/Token is inserted into the device and then interface with
the user's certificate?
* When is the user prompted to enter their PIN for
SmartCard/Token/Yubikey?
* Do we need to use CyrptoTokenKit to interface with
a smartcard inserted into the lightning port of an iOS device?
Hi,
I need to make a "Sign with apple" button.
I followed all the instructions and I managed to get apple's response with id_token.
I have to continue testing the application but I no longer get the "user" object (name, lastname).
How can I get the user object back to continue development?
I downloaded the ES sys-ext sample project. I built the 'NOTIFY' extension, and I was able to install it. However, it doesn't seem to work (or - it doesn't report anything).
This is what I did:
I download the project
I renamed the bundle IDs
I disabled SIP
I tried both signing options - let 'Xcode automatically manage signing', and I also tried to use my 'Developer ID'
I moved the app to the Applications folder
I grant the 'Full Disk Access' permission to the extension
I verified that the extension is running
I did not get the needed entitlement yet, but since SIP is disabled, I don't think it's a problem
I did get the message 'Successfully installed the extension ✅'
At the terminal, I tried to capture relevant logs:
log stream --style compact --predicate 'sender == "myBundleId"'
(I tried it with the app bundleID, and with the extension's bundleId)
And yet, 'ps' triggers no logs.
*At the Console, I get those messages:
"Unsatisfied entitlements: com.apple.developer.endpoint-security.client"
Disallowing: myBundleId
amfid: Restricted entitlements not validated, bailing out. Error: Error Domain=AppleMobileFileIntegrityError Code=-413 "No matching profile found" UserInfo={NSURL=, unsatisfiedEntitlements=, NSLocalizedDescription=No matching profile found}
Any idea where's the problem at?