I regularly help developers with keychain problems, both here on DevForums and for my Day Job™ in DTS. Many of these problems are caused by a fundamental misunderstanding of how the keychain works. This post is my attempt to explain that. I wrote it primarily so that Future Quinn™ can direct folks here rather than explain everything from scratch (-:
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: Fundamentals
or How I Learned to Stop Worrying and Love the SecItem API
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 the fundamental underpinnings of the keychain. For information about specific issues, see its companion post, SecItem: Pitfalls and Best Practices.
Keychain Documentation
Your basic starting point should be Keychain Items.
If your code runs on the Mac, also read TN3137 On Mac keychain APIs and implementations.
Read the doc comments in <Security/SecItem.h>. In many cases those doc comments contain critical tidbits.
When you read keychain documentation [1] and doc comments, keep in mind that statements specific to iOS typically apply to iPadOS, tvOS, and watchOS as well (r. 102786959). Also, they typically apply to macOS when you target the data protection keychain. Conversely, statements specific to macOS may not apply when you target the data protection keychain.
[1] Except TN3137, which is very clear about this (-:
Caveat Mac Developer
macOS supports two different keychain implementations: the original file-based keychain and the iOS-style data protection keychain.
IMPORTANT If you’re able to use the data protection keychain, do so. It’ll make your life easier. See the Careful With that Shim, Mac Developer section of SecItem: Pitfalls and Best Practices for more about this.
TN3137 On Mac keychain APIs and implementations explains this distinction. It also says:
The file-based keychain is on the road to deprecation.
This is talking about the implementation, not any specific API. The SecItem API can’t be deprecated because it works with both the data protection keychain and the file-based keychain. However, Apple has deprecated many APIs that are specific to the file-based keychain, for example, SecKeychainCreate.
TN3137 also notes that some programs, like launchd daemons, can’t use the file-based keychain. If you’re working on such a program then you don’t have to worry about the deprecation of these file-based keychain APIs. You’re already stuck with the file-based keychain implementation, so using a deprecated file-based keychain API doesn’t make things worse.
The Four Freedoms^H^H^H^H^H^H^H^H Functions
The SecItem API contains just four functions:
SecItemAdd(_:_:)
SecItemCopyMatching(_:_:)
SecItemUpdate(_:_:)
SecItemDelete(_:)
These directly map to standard SQL database operations:
SecItemAdd(_:_:) maps to INSERT.
SecItemCopyMatching(_:_:) maps to SELECT.
SecItemUpdate(_:_:) maps to UPDATE.
SecItemDelete(_:) maps to DELETE.
You can think of each keychain item class (generic password, certificate, and so on) as a separate SQL table within the database. The rows of that table are the individual keychain items for that class and the columns are the attributes of those items.
Note Except for the digital identity class, kSecClassIdentity, where the values are split across the certificate and key tables. See Digital Identities Aren’t Real in SecItem: Pitfalls and Best Practices.
This is not an accident. The data protection keychain is actually implemented as an SQLite database. If you’re curious about its structure, examine it on the Mac by pointing your favourite SQLite inspection tool — for example, the sqlite3 command-line tool — at the keychain database in ~/Library/Keychains/UUU/keychain-2.db, where UUU is a UUID.
WARNING Do not depend on the location and structure of this file. These have changed in the past and are likely to change again in the future. If you embed knowledge of them into a shipping product, it’s likely that your product will have binary compatibility problems at some point in the future. The only reason I’m mentioning them here is because I find it helpful to poke around in the file to get a better understanding of how the API works.
For information about which attributes are supported by each keychain item class — that is, what columns are in each table — see the Note box at the top of Item Attribute Keys and Values. Alternatively, look at the Attribute Key Constants doc comment in <Security/SecItem.h>.
Uniqueness
A critical part of the keychain model is uniqueness. How does the keychain determine if item A is the same as item B? It turns out that this is class dependent. For each keychain item class there is a set of attributes that form the uniqueness constraint for items of that class. That is, if you try to add item A where all of its attributes are the same as item B, the add fails with errSecDuplicateItem. For more information, see the errSecDuplicateItem page. It has lists of attributes that make up this uniqueness constraint, one for each class.
These uniqueness constraints are a major source of confusion, as discussed in the Queries and the Uniqueness Constraints section of SecItem: Pitfalls and Best Practices.
Parameter Blocks Understanding
The SecItem API is a classic ‘parameter block’ API. All of its inputs are dictionaries, and you have to know which properties to set in each dictionary to achieve your desired result. Likewise for when you read properties in output dictionaries.
There are five different property groups:
The item class property, kSecClass, determines the class of item you’re operating on: kSecClassGenericPassword, kSecClassCertificate, and so on.
The item attribute properties, like kSecAttrAccessGroup, map directly to keychain item attributes.
The search properties, like kSecMatchLimit, control how the system runs a query.
The return type properties, like kSecReturnAttributes, determine what values the query returns.
The value type properties, like kSecValueRef perform multiple duties, as explained below.
There are other properties that perform a variety of specific functions. For example, kSecUseDataProtectionKeychain tells macOS to use the data protection keychain instead of the file-based keychain. These properties are hard to describe in general; for the details, see the documentation for each such property.
Inputs
Each of the four SecItem functions take dictionary input parameters of the same type, CFDictionary, but these dictionaries are not the same. Different dictionaries support different property groups:
The first parameter of SecItemAdd(_:_:) is an add dictionary. It supports all property groups except the search properties.
The first parameter of SecItemCopyMatching(_:_:) is a query and return dictionary. It supports all property groups.
The first parameter of SecItemUpdate(_:_:) is a pure query dictionary. It supports all property groups except the return type properties.
Likewise for the only parameter of SecItemDelete(_:).
The second parameter of SecItemUpdate(_:_:) is an update dictionary. It supports the item attribute and value type property groups.
Outputs
Two of the SecItem functions, SecItemAdd(_:_:) and SecItemCopyMatching(_:_:), return values. These output parameters are of type CFTypeRef because the type of value you get back depends on the return type properties you supply in the input dictionary:
If you supply a single return type property, except kSecReturnAttributes, you get back a value appropriate for that return type.
If you supply multiple return type properties or kSecReturnAttributes, you get back a dictionary. This supports the item attribute and value type property groups. To get a non-attribute value from this dictionary, use the value type property that corresponds to its return type property. For example, if you set kSecReturnPersistentRef in the input dictionary, use kSecValuePersistentRef to get the persistent reference from the output dictionary.
In the single item case, the type of value you get back depends on the return type property and the keychain item class:
For kSecReturnData you get back the keychain item’s data. This makes most sense for password items, where the data holds the password. It also works for certificate items, where you get back the DER-encoded certificate. Using this for key items is kinda sketchy. If you want to export a key, called SecKeyCopyExternalRepresentation. Using this for digital identity items is nonsensical.
For kSecReturnRef you get back an object reference. This only works for keychain item classes that have an object representation, namely certificates, keys, and digital identities. You get back a SecCertificate, a SecKey, or a SecIdentity, respectively.
For kSecReturnPersistentRef you get back a data value that holds the persistent reference.
Value Type Subtleties
There are three properties in the value type property group:
kSecValueData
kSecValueRef
kSecValuePersistentRef
Their semantics vary based on the dictionary type.
For kSecValueData:
In an add dictionary, this is the value of the item to add. For example, when adding a generic password item (kSecClassGenericPassword), the value of this key is a Data value containing the password.
This is not supported in a query dictionary.
In an update dictionary, this is the new value for the item.
For kSecValueRef:
In add and query dictionaries, the system infers the class property and attribute properties from the supplied object. For example, if you supply a certificate object (SecCertificate, created using SecCertificateCreateWithData), the system will infer a kSecClass value of kSecClassCertificate and various attribute values, like kSecAttrSerialNumber, from that certificate object.
This is not supported in an update dictionary.
For kSecValuePersistentRef:
For query dictionaries, this uniquely identifies the item to operate on.
This is not supported in add and update dictionaries.
Revision History
2025-05-28 Expanded the Caveat Mac Developer section to cover some subtleties associated with the deprecation of the file-based keychain.
2023-09-12 Fixed various bugs in the revision history. Added a paragraph explaining how to determine which attributes are supported by each keychain item class.
2023-02-22 Made minor editorial changes.
2023-01-28 First posted.
Security
RSS for tagSecure the data your app manages and control access to your app using the Security framework.
Posts under Security tag
192 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi,
I’ve developed a custom Authorization Plugin and placed it under:
/Library/Security/SecurityAgentPlugins/AuthPlugin.bundle
I also updated the corresponding right in the authorization database (authorizationdb) to point to my plugin’s mechanism.
However, when I invoke the right, my plugin does not get loaded. The system log shows the following errors:
AuthorizationHostHelper: Init: unable to load bundle executable for plugin: AuthPlugin.bundle
AuthorizationHostHelper: Processing request: Failed to create agent mechanism AuthPlugin:auth.startup.authenticate, failing authentication!
Here’s what I’ve verified so far:
The plugin bundle and its executable are signed and notarized successfully.
The executable inside the bundle is universal (arm64 + x86_64).
The bundle structure looks correct (Contents/Info.plist, Contents/MacOS/..., etc.).
Despite that, the plugin fails to load at runtime.
Could anyone provide advice on how to debug or trace why the SecurityAgent cannot load the bundle executable?
Are there any entitlements, permissions, or SIP-related restrictions that might prevent custom authorization plugins from being loaded on modern macOS versions?
Thanks in advance for any insights!
Hello,
Thanks for the new video on Memory Integrity Enforcement!
Is the presented app's sample code available (so that we can play with it and find & fix the bug on our own, using Soft Mode)?
Thanks in advance!
I have an swift command line tool that changes proxy settings in system preferences via SystemConfiguration framework, does some stuff, and in the end reverts proxy settings back to original.
Here is simplified code:
var authorization: AuthorizationRef?
let status = AuthorizationCreate(nil, nil, [], &authorization)
let prefs = SCPreferencesCreateWithAuthorization(nil, "myapp" as CFString, nil, authorization)
// change proxy setttings
// do some stuff
let prefs2 = SCPreferencesCreateWithAuthorization(nil, "myapp" as CFString, nil, authorization)
// change proxy settings back to original
When I try to change settings for the first time, the system dialog appears requesting permission to change network settings. If I try to change settings again within а short period of time, the dialog does not appear again. However, if it takes more than several minutes after first change, the dialog does appear again. Is there a way to create authorization, so that the dialog appears only once per app launch, no matter how much time passed since the first dialog?
I have an app (currently not released on App Store) which runs on both iOS and macOS. The app has widgets for both iOS and macOS which uses user preference (set in app) into account while showing data. Before upgrading to macOS 15 (until Sonoma) widgets were working fine and app was launching correctly, but after upgrading to macOS 15 Sequoia, every time I launch the app it give popup saying '“Kontest” would like to access data from other apps. Keeping app data separate makes it easier to manage your privacy and security.' and also widgets do not get user preferences and throw the same type of error on Console application when using logging. My App group for both iOS and macOS is 'group.com.xxxxxx.yyyyy'. I am calling it as 'UserDefaults(suiteName: Constants.userDefaultsGroupID)!.bool(forKey: "shouldFetchAllEventsFromCalendar")'. Can anyone tell, what am I doing wrong here?
Hi,
I am just wondering if there is any option to protect my endpoints that will be used by Message Filtering Extension?
According to the documentation our API has 2 endpoints:
/.well-known/apple-app-site-association
/[endpoint setup in the ILMessageFilterExtensionNetworkURL value of the Info.plist file] that the deferQueryRequestToNetwork will request on every message
Since all requests to these 2 endpoints are made by iOS itself (deferQueryRequestToNetwork), I don't understand how I can protect these endpoints on my side, like API key, or maybe mTLS.
The only way that I found is white list for Apple IP range.
Is there other methods for it?
Hi everyone. Since the update to iOS 26, we are no longer able to tap the person's name and view the certificate of a signed email and choose to install the certificate or remove it. This has always worked just fine but seems to be broken on iOS 26 and I have verified that it does not work on iOS 26.1 beta as well. The part that is strange is it does work just fine on an iPad running iPad OS 26.
This makes it impossible to send encrypted emails to someone via the mail app on an iPhone. I have found a temporary workaround which is to install Outlook for iOS and install the certificates through that app which then allows me to send encrypted emails via Outlook.
This appears to be a bug just with the iPhone as I have also seen a few other people online talking about the same problem. Has anyone found a solution to this?
I recently turned on the enhanced security options for my macOS app in Xcode 26.0.1 by adding the Enhanced Security capability in the Signing and Capabilities tab. Then, Xcode adds the following key-value sets (with some other key-values) to my app's entitlements file.
<key>com.apple.security.hardened-process.enhanced-security-version</key>
<integer>1</integer>
<key>com.apple.security.hardened-process.platform-restrictions</key>
<integer>2</integer>
These values appear following the documentation about the enhanced security feature (Enabling enhanced security for your app) and the app works without any issues.
However, when I submitted a new version to the Mac App Store, my submission was rejected, and I received the following message from the App Review team via the App Store Connect.
Guideline 2.4.5(i) - Performance
Your app incorrectly implements sandboxing, or it contains one or more entitlements with invalid values. Please review the included entitlements and sandboxing documentation and resolve this issue before resubmitting a new binary.
Entitlement "com.apple.security.hardened-process.enhanced-security-version" value must be boolean and true.
Entitlement "com.apple.security.hardened-process.platform-restrictions" value must be boolean and true.
When I changed those values directly in the entitlements file based on this message, the app appears to still work. However, these settings are against the description in the documentation I mentioned above and against the settings Xcode inserted after changing the GUI setting view.
So, my question is, which settings are actually correct to enable the Enhanced Security and the Additional Runtime Platform Restrictions?
Hi,
We are running into issues with iOS app prewarming, where the system launches our app before the user has entered their passcode.
In our case, the app stores flags, counters, and session data in UserDefaults and the Keychain. During prewarm launches:
UserDefaults only returns default values (nil, 0, false). We have no way of knowing whether this information is valid or just a placeholder caused by prewarming.
Keychain items with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly are inaccessible, which can lead to broken business logic (the app can assume no session exists).
No special launch options or environment variables appear to be set.
We can reproduce this 100% of the time by starting a Live Activity in the app before reboot.
Here’s an example of the workaround we tried, following older recommendations:
__attribute__((constructor))
static void ModuleInitializer(void) {
char* isPrewarm = getenv("ActivePrewarm");
if (isPrewarm != NULL && isPrewarm[0] == '1') {
exit(0); // prevent prewarm launch from proceeding
}
}
On iOS 16+, the ActivePrewarm environment variable doesn’t seem to exist anymore (though older docs and SDKs such as Sentry reference it).
We also tried listening for UIApplication.protectedDataDidBecomeAvailableNotification, but this is not specific to prewarming (it also fires when the device gets unlocked) and can cause watchdog termination if we delay work too long.
Questions:
Is there a supported way to opt out of app prewarming?
What is the correct way to detect when an app is being prewarmed?
Is the ActivePrewarm environment variable still supported in iOS 16+?
Ideally, the UserDefaults API itself should indicate whether it is returning valid stored values or defaults due to the app being launched in a prewarm session.
We understand opting out may impact performance, but data security and integrity are our priority. Any guidance would be greatly appreciated.
Hey devs,
I have a really weird issue and at this point I cannot determine is it a Big Sur 11.1 or M1 issue or just some macOS settings issue.
Short description
programatically (from node, electron) I'd like to store x509 cert to keychain. I got the following error message:
SecTrustSettingsSetTrustSettings: The authorization was denied since no user interaction was possible. (1) I could reproduce this issue on: a brand new mac mini with M1 chip and Big Sur 11.1
another brand new mac mini with M1 chip and Big Sur 11.1
a 2018 MacBook pro with Intel chip and Big Sur 11.1
I couldn't reproduce this issue on: 2020 MacBook pro with intel i9 chip and Big Sur 11.1
2020 MacBook pro with intel i9 chip and Big Sur 11.0
How am I trying to store the cert
node test.js
test.js
const { exec } = require('child_process')
exec(
	`osascript -e 'do shell script "security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /Users/kotapeter/ssl/testsite.local.crt" with prompt "Test APP wants to store SSL certification to keychain." with administrator privileges'`,
	(error, stdout, stderr) => {
		if (error) {
			console.log(error.stack)
			console.log(`Error code: ${error.code}`)
			console.log(`Signal received: ${error.signal}`)
		}
		console.log(`STDOUT: ${stdout}`)
		console.log(`STDERR: ${stderr}`)
		process.exit(1)
	}
)
testsite.local.crt:
----BEGIN CERTIFICATE
MIIDUzCCAjugAwIBAgIUD9xMnL73y7fuida5TXgmklLswsowDQYJKoZIhvcNAQEL
BQAwGTEXMBUGA1UEAwwOdGVzdHNpdGUubG9jYWwwHhcNMjEwMTE3MTExODU1WhcN
NDEwMTEyMTExODU1WjAZMRcwFQYDVQQDDA50ZXN0c2l0ZS5sb2NhbDCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBANM08SDi06dvnyU1A6//BeEFd8mXsOpD
QCbYEHX/Pz4jqaBYwVjD5pG7FkvDeUKZnEVyrsofjZ4Y1WAT8jxPMUi+jDlgNTiF
jPVc4rA6hcGX6b70HjsCACmc8bZd+EU7gm4b5eL6exTsVzHc+lFz4eQFXgutYTL7
guDQE/gFHwqPkLvnfg3rgY31p3Hm/snL8NuD154iE9O1WuSxEjik65uOQaewZmJ9
ejJEuuEhMA8O9dXveJ71TMV5lqA//svDxBu3zXIxMqRy2LdzfROd+guLP6ZD3jUy
cWi7GpF4yN0+rD/0aXFJVHzV6TpS9oqb14jynvn1AyVfBB9+VQVNwTsCAwEAAaOB
kjCBjzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIC9DA7BgNVHSUENDAyBggrBgEFBQcD
AQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwHQYDVR0O
BBYEFDjAC2ObSbB59XyLW1YaD7bgY8ddMBkGA1UdEQQSMBCCDnRlc3RzaXRlLmxv
Y2FsMA0GCSqGSIb3DQEBCwUAA4IBAQBsU6OA4LrXQIZDXSIZPsDhtA7YZWzbrpqP
ceXPwBd1k9Yd9T83EdA00N6eoOWFzwnQqwqKxtYdl3x9JQ7ewhY2huH9DRtCGjiT
m/GVU/WnNm4tUTuGU4FyjSTRi8bNUxTSF5PZ0U2/vFZ0d7T43NbLQAiFSxyfC1r6
qjKQCYDL92XeU61zJxesxy5hxVNrbDpbPnCUZpx4hhL0RHgG+tZBOlBuW4eq249O
0Ql+3ShcPom4hzfh975385bfwfUT2s/ovng67IuM9bLSWWe7U+6HbOEvzMIiqK94
YYPmOC62cdhOaZIJmro6lL7eFLqlYfLU4H52ICuntBxvOx0UBExn----END CERTIFICATE
testsite.local.key:
----BEGIN RSA PRIVATE KEY
MIIEpQIBAAKCAQEA0zTxIOLTp2+fJTUDr/8F4QV3yZew6kNAJtgQdf8/PiOpoFjB
WMPmkbsWS8N5QpmcRXKuyh+NnhjVYBPyPE8xSL6MOWA1OIWM9VzisDqFwZfpvvQe
OwIAKZzxtl34RTuCbhvl4vp7FOxXMdz6UXPh5AVeC61hMvuC4NAT+AUfCo+Qu+d+
DeuBjfWnceb+ycvw24PXniIT07Va5LESOKTrm45Bp7BmYn16MkS64SEwDw711e94
nvVMxXmWoD/+y8PEG7fNcjEypHLYt3N9E536C4s/pkPeNTJxaLsakXjI3T6sP/Rp
cUlUfNXpOlL2ipvXiPKe+fUDJV8EH35VBU3BOwIDAQABAoIBAQDDGLJsiFqu3gMK
IZCIcHCDzcM7Kq43l2uY9hkuhltrERJNle70CfHgSAtubOCETtT1qdwfxUnR8mqX
15T5dMW3xpxNG7vNvD/bHrQfyc9oZuV6iJGsPEreJaV5qg/+E9yFzatrIam0SCS7
YL6xovPU58hZzQxuRbo95LetcT2dSBY33+ttY7ayV/Lx7k6nh0xU6RmTPHyyr8m7
yHpoJoSxdT/xv5iBSZ8mM9/2Vzhr14SWipVuwVVhDSfbn8ngHpIoQDkaJLMpWr+m
4z3PqfftAwR6s6i96HnhYLnRir618TQh4B9IEngeEwCMn4XAzE3L+VTaKU1hg9el
aMfXzPERAoGBAPa+sJ2p9eQsv0vCUUL8KeRWvwjDZRTd+YAIfpLMWrb0tMmrBM4V
V0L2joF76kdDxt1SAlHoYCT/3Rn8EPmK0TN3MEskiXQ7v57iv+LZOZcpe0ppG/4A
ZihF9+wUjFCDw4ymnRQD463535O6BgZV+rcZksFRD2AwvEjt1nYm93VXAoGBANsh
AYM+FPmMnzebUMB0oGIkNkE9nVb9MPbQYZjEeOeHJqmt1Nl6xLuYBWTmWwCy7J4e
QPtnuMCdO6C1kuOGjQPBFIpeyFMzll+E3hKzicumgCpt5U8nTZoKc/jZckRD7n3p
lbYYgHOR3A/3GCDK5L3rwziWpSRAGMSCQylvkOC9AoGBAKLfZL3t/r3LO8rKTdGl
mhF7oUYrlIGdtJ/q+4HzGr5B8URdeyJ9u8gb8B1Qqmi4OIDHLXjbpvtFWbFZTesq
0sTiHCK9z23GMsqyam9XbEh3vUZ082FK6iQTa3+OYMCU+XPSV0Vq+9NPaWGeHXP5
NTG/07t/wmKASQjq1fHP7vCpAoGBAK4254T4bqSYcF09Vk4savab46aq3dSzJ6KS
uYVDbvxkLxDn6zmcqZybmG5H1kIP/p8XXoKCTBiW6Tk0IrxR1PsPHs2D3bCIax01
/XjQ1NTcYzlYdd8gWEoH1XwbJQWxHINummBTyowXguYOhVhM9t8n+eWbn1/atdZF
2i+vS3fhAoGAYKw6rkJfTSEswgBKlQFJImxVA+bgKsEwUti1aBaIA2vyIYWDeV10
G8hlUDlxvVkfwCJoy5zz6joGGO/REhqOkMbFRPseA50u2NQVuK5C+avUXdcILJHN
zp0nC5eZpP1TC++uCboJxo5TIdbLL7GRwQfffgALRBpK12Vijs195cc=----END RSA PRIVATE KEY
What I've already found
If I run the following command from terminal It asks my password first in terminal and after that It asks my password again in OS password prompt.
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /Users/kotapeter/ssl/testsite.local.crt
It looks like I'm getting the above error message because osascript hides the second password asking dialog.
The cert always gets stored in keychain but when I get the error message the cert "Trust" value is not "Always Trust".
References
StackOverflow question: https://stackoverflow.com/questions/65699160/electron-import-x509-cert-to-local-keychain-macos-the-authorization-was-deni
opened issue on sudo-prompt electron package: https://github.com/jorangreef/sudo-prompt/issues/137
CLLocation.sourceInformation.isSimulatedBySoftware not detecting third-party location spoofing tools
Summary
CLLocationSourceInformation.isSimulatedBySoftware (iOS 15+) fails to detect location spoofing when using third-party tools like LocaChange, despite Apple's documentation stating it should detect simulated locations.
Environment
iOS 18.0 (tested and confirmed)
Physical device with Developer Mode enabled
Third-party location spoofing tools (e.g., LocaChange etc.)
Expected Behavior
According to Apple's documentation, isSimulatedBySoftware should return true when:
"if the system generated the location using on-device software simulation. "
Actual Behavior
Tested on iOS 18.0:
When using LocaChange
sourceInformation.isSimulatedBySoftware returns false
This occurs even though the location is clearly being simulated.
Steps to Reproduce
Enable Developer Mode on iOS 18 device
Connect device to Mac via USB
Use LocaChange to spoof location to a different city/country
In your app, request location updates and check CLLocation.sourceInformation?.isSimulatedBySoftware
Observe that it returns false or sourceInformation is nil
Compare with direct Xcode location simulation (Debug → Simulate Location) which correctly returns true
Topic:
App & System Services
SubTopic:
Maps & Location
Tags:
Security
Core Location
Maps and Location
Hi,is there an option to mark the file or folder or item stored in user defaults ... not to be backed up when doing unencrypted backup in iTunes?We are developing iOS app that contains sensitive data. But even if we enable Data Protection for the iOS app it can be backed up on mac unencrypted using iTunes. Is there a way to allow backing up content only if the backup is encrypted?
The header documentation for the (deprecated) LAContext.evaluatedPolicyDomainState property contains the following:
@warning Please note that the value returned by this property can change exceptionally between major OS versions even if the state of biometry has not changed.
I noticed that the documentation for the new LAContext.domainState property does not contain a similar warning. I also found this related thread from 2016/17.
Is the domainState property not susceptible to changes between major OS versions? Or is this generally not an issue anymore?
Title's basically the whole question. I'm writing an SMS/Call Reporting extension (ILClassificationUIExtensionViewController). My goal is to keep everything on device and not use the built-in SMS/network reporting. To that end, I'm trying to write to a file in the App Group container from the classificationResponse handler. I'm getting Error 513: "You don’t have permission to save the file “classification_log.txt” in the [app group container folder]".
I haven't been able to find much in the documentation on whether this behavior is enforced in classificationResponse handlers. Apple's barebones page on "SMS and Call Spam Reporting"[1] says "the system always deletes your extension’s container after your extension terminates," but that doesn't answer whether you can write to an App Group container. I haven't been able to find that answer elsewhere. ChatGPT and Gemini are both very sure it can be done. Any thoughts?
[1] https://developer.apple.com/documentation/identitylookup/sms-and-call-spam-reporting
Topic:
App & System Services
SubTopic:
Core OS
Tags:
SMS and Call Reporting
Files and Storage
Security
Command: com.apple.WebKit.Networking
Path: /private/preboot/Cryptexes/OS/System/Library/ExtensionKit/Extensions/NetworkingExtension.appex/com.apple.WebKit.Networking
Identifier: com.apple.WebKit.Networking
Version: ??? (8621.3.11.10.3)
Resource Coalition: "com.apple.mobilesafari"(1005)
Architecture: arm64e
Parent: launchd [1]
PID: 1708
Hi guys, I need to configure a VPN to work only for specific apps. I already have a supervised iPhone, and I’ve successfully configured the VPN, but right now it applies to the whole phone. I need it to work just for some apps.
I tried using both Apple Configurator and iMazing, but I can’t find this option there.
Macbook OS Version: macOS 14.7.3 (23H417)
Mobile OS: iOS
Mobile OS Version: iOS 18.6.2
Mobile Manufacturer: Apple
Mobile Model: iPhone 12 Pro Max
Page Type: vue
vue Version: vue2
Packaging Method: Cloud Packaging
Project Creation Method: HBuilderX
Steps:
The backend server is deployed on AWS in Japan with a Japanese IP.
Packaging the APP in HBuilderX and publishing it to the Apple App Store were both successful.
In a subsequent version, we planned to add a push notification feature and selected uniPush V2.
Due to the separation of frontend and backend, the frontend APP implements functions such as registration, login, password change, page content display, and product lists through the server's RESTful APIs.
Test colleagues reported that the APP could not load pages when used in Japan; however, it worked normally in China.
In China:
Pinging the server IP and domain from a MacBook was successful.
Testing the API with Postman on a MacBook was successful.
In Japan:
Pinging the server IP and domain from a MacBook was successful.
Testing the API with Postman on a MacBook failed with the error:
HandshakeException: Connection terminated during handshake
This appears to be an SSL communication failure.
We tested the SSL certificate using www.ssllabs.com/ssltest and received an A+ rating. The certificate should not be an issue.
we deselected uniPush V2, repackaged the APP, and uploaded it to TestFlight.
The result remained the same: the APP content failed to load in Japan, while it worked normally in China.
Expected Result:
Access to the Japanese server APIs should work normally both in China and Japan.
Actual Result:
The APP content fails to load when used in Japan, but works normally in China.
Our app server is having some TLS related issue with the new iOS 26 (It works with iOS 18 and below).
When opening the domain url in iPhone Safari browser with iOS 26, it showing the error as below:
We followed the instructions from this link (https://support.apple.com/en-sg/122756), to run the following command: nscurl --tls-diagnostics https://test.example in Terminal app. It shows TLS failed with error: -9808
Could anyone please help explain what exactly the issue is with our server certificate, and how we should fix it? Thanks so much!
With Let's Encrypt having completely dropped support for OCSP recently [1], I wanted to ask if macOS has a means of keeping up to date with their CRLs and if so, roughly how often this occurs?
I first observed an issue where a revoked-certificate test site, "revoked.badssl.com" (cert signed by Let's Encrypt), was not getting blocked on any browser, when a revocation policy was set up using the SecPolicyCreateRevocation API, in tandem with the kSecRevocationUseAnyAvailableMethod and kSecRevocationPreferCRL flags.
After further investigation, I noticed that even on a fresh install of macOS, Safari does not block this test website, while Chrome and Firefox (usually) do, due to its revoked certificate. Chrome and Firefox both have their own means of dealing with CRLs, while I assume Safari uses the system Keychain and APIs.
I checked cert info for the site here [2]. It was issued on 2025-07-01 20:00 and revoked an hour later.
[1] https://letsencrypt.org/2024/12/05/ending-ocsp/
[2] https://www.ssllabs.com/ssltest/analyze.html?d=revoked.badssl.com
Hi! I'm trying to prototype a macOS app related to wifi features. The main hiccup I've encountered is "Connect to a saved network without re-entering the network password".
So far I've been unsuccessful in this without
entering the password manually each time
asking the user for authentication to access the saved network in keychain
I read somewhere on the internet that CWInterface.associate would use saved credentials automatically if you gave a nil password, but my attempts have proven that to be false.
Is this not currently available because it raises security concerns, or it just hasn't been considered? Or am I missing a way to do this? I don't need access to the credentials, just for the system to connect for me.
I am working on implementing mTLS authentication in my iOS app (Apple Inhouse & intune MAM managed app). The SCEP client certificate is deployed on the device via Intune MDM. When I try accessing the protected endpoint via SFSafariViewController/ASWebAuthenticationSession, the certificate picker appears and the request succeeds. However, from within my app (using URLSessionDelegate), the certificate is not found (errSecItemNotFound).
The didReceive challenge method is called, but my SCEP certificate is not found in the app. The certificate is visible under Settings > Device Management > SCEP Certificate.
How can I make my iOS app access and use the SCEP certificate (installed via Intune MDM) for mTLS requests?
Do I need a special entitlement, keychain access group, or configuration in Intune or Developer account to allow my app to use the certificate?
Here is the sample code I am using:
final class KeychainCertificateDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate else {
completionHandler(.performDefaultHandling, nil)
return
}
// Get the DNs the server will accept
guard let expectedDNs = challenge.protectionSpace.distinguishedNames else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
var identityRefs: CFTypeRef? = nil
let err = SecItemCopyMatching([
kSecClass: kSecClassIdentity,
kSecMatchLimit: kSecMatchLimitAll,
kSecMatchIssuers: expectedDNs,
kSecReturnRef: true,
] as NSDictionary, &identityRefs)
if err != errSecSuccess {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
guard let identities = identityRefs as? [SecIdentity],
let identity = identities.first
else {
print("Identity list is empty")
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let credential = URLCredential(identity: identity, certificates: nil, persistence: .forSession)
completionHandler(.useCredential, credential)
}
}
func perform_mTLSRequest() {
guard let url = URL(string: "https://sample.com/api/endpoint") else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
let delegate = KeychainCertificateDelegate()
let session = URLSession(configuration: .ephemeral, delegate: delegate, delegateQueue: nil)
let task = session.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
print("Bad response")
return
}
if let data = data {
print(String(data: data, encoding: .utf8)!)
}
}
task.resume()
}