Security

RSS for tag

Secure the data your app manages and control access to your app using the Security framework.

Security Documentation

Pinned Posts

Posts under Security tag

263 Posts
Sort by:
Post not yet marked as solved
0 Replies
99 Views
My organization is using mutual TLS authentication for HTTPS, with PIV cards storing the certs. We observe that some OS X devices send only the leaf certs when establishing the mTLS connection, whereas others send the entire chain. We cannot validate the leaf cert without the intermediate cert, so those clients are rejected. What drives the decision whether to send the whole chain, vs the leaf cert only? For more details, and some things we observed. The PIV cards are US DoD CAC cards: https://www.cac.mil/common-access-card/ The client cert chain on the card looks like this: Leaf client cert, CN=LastName.Name Intermediate cert, CN=DOD ID CA-70 "Root" cert, CN=DoD Root CA 6 through 8.: Additional interoperability certs. Our system is set up to trust the "root" cert CN=DoD Root CA 6. Neither the leaf cert, nor other certs in the chain are trusted by Apple Keychain Trust Store by default. We find that most laptops will send the entire chain, 1 through 8, when establishing the mTLS connection with our servers. This allows us to validate them correctly. On a subset of OS X devices, Google Chrome will only send the leaf chain. This happens even when we use exact same PIV card, and exact same PIV reader as on working laptops. Safari will not send any cert at all. We found that if we explicitly add the CN=DoD Root CA 6 to the Apple Trust Store, Google Chrome and Safari will start sending a short chain, containing only certs 1 through 3. This allows the server to validate them. When we remove it from Trust Store, Chrome is back to sending only leaf, but Safari will not even send the leaf. Again, this only happens on some laptops; on most of the laptops, both Safari and Google Chrome will send the entire chain, regardless of whatever is set up in Trust Store. My suspicion is that for some reason, on those laptops, Safari will not send the client certs that OS X doesn't trust. This makes sense, but this is not the behavior we want. We want the same behavior on the working laptops, which is to send the whole chain. All of our laptops are on OS X 14.4
Posted
by
Post not yet marked as solved
3 Replies
199 Views
We generated key pair and imported associated cert into keychain. We stored value of kSecAttrPublicKeyHash and use it to find matching certificate and private key later. However we have some legacy code that requests to get SecIdentityRef. We can retrieve SecCertificateRef with matched value of kSecAttrPublicKeyHash and private key with kSecAttrApplicationLabel. But we don't know how to generate/query SecIdentityRef with SecCertificateRef and SecKeyRef. API SecIdentityCreateWithCertificate is only available on macOS. Is there any equivalent API on iOS? Thanks, Ying
Posted
by
Post not yet marked as solved
3 Replies
160 Views
I am new to iOS development, and recently I was trying to build an application, which will create a key inside the secure element, and after - I will sing something with it. While developing I've encountered an issue: the key generation fails if there is a flag .biometryAny or .biometryCurrentSet The authentication itself is triggered, but the function still throws a mistake. My setup - Xcode iPhone15 simulator, FaceID enrolled and the animation of it is working. Ive created the same post on overflow, in case somebody will have the same issues: https://stackoverflow.com/questions/78175858/secure-enclave-key-generation-failure I've tried deleting the flag, while keeping the manual authorisation, and this approach works, but I still would like have maximum security. THIS WORKS: func authenticateUser(completion: @escaping (Bool, Error?) -> Void) { let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { let reason = "Biometric authentication is needed to access your secure data." context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in DispatchQueue.main.async { completion(success, authenticationError) } } } else { // Biometry is not available or not enrolled. DispatchQueue.main.async { completion(false, error) } } } @objc func encryptAction() { authenticateUser { [weak self] (success, error) in guard success else { self?.outputLabel.text = "Authentication failed: \(error?.localizedDescription ?? "Unknown error")" return } guard let randomNumber = self?.inputTextField.text, !randomNumber.isEmpty, let dataToSign = randomNumber.data(using: .utf8), let privateKey = self?.generatePrivateKey() else { self?.outputLabel.text = "Error: Could not generate private key." return } if let signature = self?.signData(privateKey: privateKey, data: dataToSign) { self?.outputLabel.text = "Signature: \(signature.base64EncodedString())" } else { self?.outputLabel.text = "Error: Could not sign data." } } } func generatePrivateKey() -> SecKey? { // 1. Create Keys Access Control guard let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], nil) else { fatalError("cannot set access control") } // 2. Create Key Attributes guard let tag = "com.example.keys.mykey".data(using: .utf8) else { fatalError("cannot set tag") } let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits as String: 256, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: tag, kSecAttrAccessControl as String: accessControl ] ] // 3. Generate Key Pairs var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { if let error = error?.takeRetainedValue() { print("Error creating a key: \(error)") } return nil } return privateKey } func signData(privateKey: SecKey, data: Data) -> Data? { let digest = sha256(data: data) var error: Unmanaged<CFError>? guard let signature = SecKeyCreateSignature(privateKey, .ecdsaSignatureMessageX962SHA256, digest as CFData, &error) as Data? else { print(error!.takeRetainedValue() as Error) return nil } return signature } } THIS DOESN'T guard let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage, .biometryCurrentSet], nil) else { info.something file is updated and there is a privacy FaceID field included. the error is triggered at this part: var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { if let error = error?.takeRetainedValue() { print("Error creating a key: \(error)") } return nil } The error itself: Error creating a key: Error Domain=NSOSStatusErrorDomain Code=-25293 "Key generation failed, error -25293" UserInfo={numberOfErrorsDeep=0, NSDescription=Key generation failed, error -25293}
Posted
by
Post marked as solved
2 Replies
153 Views
Is it possible? The original key was generated and stored in the Keychain using the following code: func generateSecureEnclaveProtectedSecKey(withTag tag: Data) throws -> SecKey { var error: Unmanaged<CFError>? let accessControl = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], &error )! let attributes = [ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits as String: 256, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecPrivateKeyAttrs as String: [ kSecAttrCanSign as String: true, kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: tag, kSecAttrAccessControl as String: accessControl, ] as [String: Any], ] as [String: Any] let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)! return privateKey } Then I wanted to use the strongly typed interface of CryptoKit, so I naively tried to get a hold of the existing key as follows (querying for kSecReturnPersistentRef and not kSecReturnRef): func getSecureEnclaveProtectedCryptoKitKey(fromSecureEnclaveProtectedSecKeyWithTag tag: Data) throws -> SecureEnclave.P256.Signing.PrivateKey { let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag, kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, kSecReturnPersistentRef as String: true, ] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) let keyData = item as! CFData return try SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: keyData as Data) } But that resulted in: Error Domain=CryptoTokenKit Code=-3 "corrupted objectID detected" UserInfo={NSLocalizedDescription=corrupted objectID detected} Since this is a Secure Enclave protected key, it is not possible to use SecKeyCopyExternalRepresentation (or query for kSecReturnData), but perhaps there is another way to convert a SecKey object to a SecureEnclave.P256.Signing.PrivateKey? The other way around seem to be possible using the answers to this blog post: https://developer.apple.com/forums/thread/728314
Posted
by
Post not yet marked as solved
1 Replies
84 Views
Hi, I am trying to create a secKey from a .p8 file which I will then use to sign some data. I'm targeting an iOS application. I understand I cannot use the p8 key directly with iOS APIs and will have to unwrap the p8 file before I can feed it to SecKeyCreateWithData I'm unsure how to go about doing it though and any help will be appreciated. For context it is the APNS p8 file.
Posted
by
Post marked as solved
5 Replies
453 Views
We generate key pair and import cert with access flag kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly in our application. We then have notification extension that access shared keychain to retrieve the identity when device is locked. We use SecItemCopyMatching to get all SecIdentity with its attributes as below: let queryDict: [String: Any] = [ kSecClass as String: kSecClassIdentity, kSecReturnAttributes as String: true, kSecReturnRef as String: true, kSecMatchLimit as String: kSecMatchLimitAll ] var oss = SecItemCopyMatching(queryDict as CFDictionary, &amp;attrs) When the code runs on notification extension with locked device, it failed on some devices (not on all devices) with error -25308. The sysdiagnose shows: default securityd 2024-03-14 10:49:13.844996 -0400 129 0x110115 SecDbKeychainItemV7: cannot decrypt metadata key because the keychain is locked (-25308) default securityd 2024-03-14 10:49:13.845036 -0400 129 0x110091 NSExtension[2222]/1#7 LF=0 copy_matching Error Domain=NSOSStatusErrorDomain Code=-25308 "ks_crypt: e00002e2 failed to 'od' item (class 6, bag: 0) Access to item attempted while keychain is locked." UserInfo={numberOfErrorsDeep=0, NSDescription=ks_crypt: e00002e2 failed to 'od' item (class 6, bag: 0) Access to item attempted while keychain is locked.} We also try to use access flag kSecAttrAccessibleAlwaysThisDeviceOnly, but it doesn't make difference. Since securityd reports "cannot decrypt metadata key because keychain is locked". I wonder if it is because we query attributes for all identity. Is it a bug on Security framework? Thanks, Ying
Posted
by
Post not yet marked as solved
0 Replies
191 Views
Keychain is a bit of a ‘call driver’ for DTS. I’ve sent instructions like this to many developers over the years. Today I decided to write it down for everyone’s benefit. If you have questions or comments, put them in a new thread here on DevForums. Tag it with Security so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Investigating hard-to-reproduce keychain problems SecItem is the primary API for the keychain on all Apple platforms. If you’re getting started with that API, I have two DevForums posts that you’ll find useful: SecItem: Fundamentals SecItem: Pitfalls and Best Practices Also, if you’re on macOS, make sure to read TN3137 On Mac keychain APIs and implementations. Every now and again folks encounter a hard-to-reproduce keychain problem. For example, you might have code that works most of the time but fails for some users in the field. This failure might be seen regularly by some users, or it might happen sporadically across all your users. Problems like this are often caused by a bug in the system itself, but the SecItem API is sufficiently tricky that I’ve seen cases of this where the actual problem was a bug in the developer’s code. This post outlines a process for investigating such problems. I’ve helped a number of developers use it to good effect, and I figured I should share it for the enjoyment of all (hey hey!). This process depends on the system log. If you’re not best friends with the system log, you should be! See Your Friend the System Log. This process is a special case of the process I describe in Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem. Read that before continuing. Unwrap the wrapper Many developers use a wrapper around the SecItem API. Such wrappers make it hard to investigate problems like this. For example, you might call a wrapper routine that returns a password string or nil if there’s a failure. That means you can’t distinguish between an expected nil, where the password hasn’t been saved yet, or a weird error. If you need to debug a hard-to-reproduce keychain problem, look through the wrapper to find the calls to Apple’s SecItem APIs. It’s fine to leave your wrapper in place, but do this debugging at the SecItem level. Add before and after log points The basic strategy here is: Add a log point before the call to the SecItem API, including the parameters that you’ll pass in. Add a log point after the call to the SecItem API, including the returned error code and any response you got. This is trickier than it might seem due to the way that the SecItem API is structured. Consider this example: func copyAccountPassword(_ userName: String) -> String? { var copyResult: CFTypeRef? = nil let err = SecItemCopyMatching([ kSecClass: kSecClassGenericPassword, kSecAttrService: "WaffleVarnish", kSecAttrAccount: userName, kSecReturnData: true, ] as NSDictionary, &copyResult) guard err == errSecSuccess, let result = String(data: copyResult! as! Data, encoding: .utf8) else { return nil } return result } There are a bunch of issues here: The query dictionary is created inline, so it’s not easy to log it. The query dictionary contains the kSecAttrAccount property, which is likely to hold private data. The query can fail, leaving copyResult set to nil. The query can work but return invalid data. In this case the function will return nil but there’s no error. The password is obviously private data. WARNING Be careful when logging keychain data. The whole point of the keychain is to protect the user’s secrets. It would be bad to then go and log those secrets to the system log. Here’s an updated version of that routine: func copyAccountPasswordWithLogging(_ userName: String) -> String? { let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "WaffleVarnish", kSecAttrAccount: userName, kSecReturnData: true, ] as NSDictionary var copyResult: CFTypeRef? = nil log.log("will copy account password, query: \(query)") let err = SecItemCopyMatching(query, &copyResult) guard err == errSecSuccess else { log.log("did not copy account password, err: \(err)") return nil } guard let result = String(data: copyResult! as! Data, encoding: .utf8) else { log.log("did not copy account password, malformed") return nil } log.log("did copy account password") return result } This example assumes that log is a value of type Logger. Redact private data This new code isn’t perfect: The query value is considered private data and not recorded in the log. It logs nothing about the resulting password. Addressing the second problem is a challenge. You could do something creative like log a salted hash of the password but, honestly, I think it’s best to err on the side of caution here. You might try to fix the first problem with code like this: !!! DO NOT DO THIS !!! log.log("will copy account password, query: \(query, privacy: .public)") !!! DO NOT DO THIS !!! However, that’s problematic because it logs the account value (kSecAttrAccount) when it should be redacted. What you do about this depends on the scope of your deployment: If you’re targeting an internal test harness, you might choose to leave it as is. The machines on the test harness don’t have any truly private data. If this code is going to be used by actual humans, you must further redact your logging. For example, you might write a helper like this: func redactedQuery(_ query: NSDictionary) -> String { let query = query.mutableCopy() as! NSMutableDictionary if query.object(forKey: kSecAttrAccount as NSString) != nil { query.setObject("REDACTED", forKey: kSecAttrAccount as NSString) } return "\(query)" } You’ll have to customise this code for your specific use case. For example, your code might put information that’s not private into kSecAttrAccount — in many of my projects, I use a fixed string for this — and so redacting that might be pointless. OTOH, your code might put private information into other properties. If you call a SecItem API that returns a dictionary, you’ll need a similar redactedResponse(_:) routine for that response. Test your logging The next step is to test your logging. Make sure that the stuff you want logged is logged and the stuff you don’t want logged is not. Test outside of Xcode, because Xcode automatically captures private data. Look in the log When you get a sysdiagnose log back from a user having this problem, unpack and open the system log snapshot. Find all the log entry pairs created by your before and after log points. Then look for the most recent one that illustrates the problem you’re investigating. To learn more about the cause of this problem, look for other log entries of interest between those two points. When looking at keychain-related log entries, keep in mind that most of the Security framework is open source. If you’re curious what a log entry means, search the source to see the context. Note Darwin open source won’t always exactly match the source in the corresponding OS version. Also, the Security framework is only part of the story here, and some other subsystems that are relevant to the keychain aren’t open source. However, my experience is that looking at the source is super useful. If you find that log entries are missing, remember that the system log purges older log entries to make room for newer ones. That’s why it’s important to take your sysdiagnose as soon as possible after encountering the problem. If you escalate this problem to Apple — via a bug report, a DTS tech support incident, or whatever — make sure to include: Your sysdiagnose log The exact timestamps of your before and after log entries We may not need this level of information, but in many cases it really helps.
Posted
by
Post not yet marked as solved
0 Replies
107 Views
If on iOS an app protects a keychain item with an access control list that specifies .biometryCurrentSet in its SecAccessControlCreateFlags the app loses access to the item if the set of currently enrolled fingers (for Touch ID) or the currently enrolled user (for Face ID) changes - which corresponds to a change of the evaluatedPolicyDomainState. We have users reporting loss of such items even though - as they assure us - they have not touched (no pun intended) anything under "[Touch|Face] ID & Code" in Preferences.app. Is there another reason why an app may lose access to such items?
Posted
by
Post not yet marked as solved
14 Replies
1.7k Views
Hi, An existing app on AppStore have issue with SHA256 Hash only since iOS17.4. And only Pro and Pro Max seems affected by this issue. NSData is read from downloaded file. Hash is calculated with unsigned char *CC_SHA256(const void *data, CC_LONG len, unsigned char *md) And compared with original HASH It appears different only on iOS17.4 and only Pro and Pro Max. But i can't reproduce this issue. Do you have an idea ? Thank you
Posted
by
Post not yet marked as solved
1 Replies
165 Views
I have an Endpoint system extension that, in theory, receives XProtect alerts. I regularly see XProtectPluginService starting programs like XProtecteRemediatorSheepSwap on my Mac. I would love to be able to put one or more files/bundles on my Mac that triggers the detectors, so I can see the alerts go from the Endpoint system extension through to the UI. Does Apple have or recommend a way (short of being infected) for triggering the XProtect detectors for testing?
Posted
by
Post not yet marked as solved
0 Replies
151 Views
Can someone share how secure is the communication between iOS app and its safari mobile extension. Is it encrypted? Are there any references to best practices to follow? If a user has opened multiple tabs and has multiple extensions can there be security issues during their communication like one extension able to read other extensions memory?
Posted
by
Post not yet marked as solved
3 Replies
205 Views
I'm setting up unit tests for my application using Xcode. When I tried to access default keychain on MacOS using SecKeychainCopyDefault, I got error OSStatus -25307, which means "A default keychain could not be found." The tests worked locally, and the issue only happened with Github Actions. Would anyone have any insight on this issue, or point me to some reading I can refer to? Thanks in advance! Here is some more tests I've done here. I tried to run "security default-keychain" on GithubAction, and I got &gt; security default-keychain "/Users/runner/Library/Keychains/login.keychain-db" However, when I tried to start a shell to run the same security command in my unit test, I got SecKeychainCopyDefault: A default keychain could not be found. Here is my test calling security command from shell: static func run_shell(_ command: String) -&gt; String { let task = Process() let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe task.arguments = ["-c", command] task.launchPath = "/bin/zsh" task.standardInput = nil task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8)! return output } func testSecurityDefaultKeychain() throws { print(TLSContextTests.run_shell("security default-keychain")); }
Posted
by
Post marked as solved
2 Replies
227 Views
I wrote a Python script that iterates over all the "Junk.mbox" folders in my account (/Users/Bob/Library/Mail). My account, Bob, is an admin account. Even if I run my script with sudo, the script fails with a permissions error. I manually checked ALL of the folders (recursively) in /Users/Library/Bob/Mail and they all have read/write access for my account. I wondered if it was SIP related so I disabled it. And, yes, I can access that folder programmatically with SIP disabled. So I re-enabled SIP and simply copied the contents of /Users/Bob/Library/Mail to ~/tmp. Now my Python script runs without issue when pointing to that folder. Please tell me that I don't have to disable SIP or copy gigabytes worth of data to another location just to access my own email data. Any suggestions would be greatly appreciated!
Posted
by
Post not yet marked as solved
1 Replies
162 Views
I am trying to communicate with a Java server over TCP, but I'm having issues trying to make the data secure in transit using RSA and AES. The server creates an AES key, encodes it in utf8, and sends it to the IOS Client, where it should be decoded back into a byte array as a Data object. Then, Using the Cryptokit framework, I try to create a SecKey object from it. I am stumped when trying to do so, though: func createSecKeyFromAESKeyData(aesKeyData: Data) -&gt; SecKey? { // Define the key attributes let keyAttributes: [CFString: Any] = [ kSecAttrKeyClass: kSecAttrKeyClassSymmetric, kSecAttrKeySizeInBits: 128, kSecAttrIsPermanent: false ] // Convert the AES key data into a SecKey object var error: Unmanaged&lt;CFError&gt;? guard let key = SecKeyCreateWithData(aesKeyData as CFData, keyAttributes as CFDictionary, &amp;error) else { if let error = error { print("Error creating SecKey: \(error.takeRetainedValue() as Error)") } else { print("Unknown error creating SecKey") } return nil } return key } Despite setting up my key attribute dictionary with the correct information (AES_128_GCM_SHA256, 128 bits, impermanent) based on how I generate it in the Java code, I keep getting a runtime error at the SecKeyCreateWithData call stating "Unsupported symmetric key type: 4865". I am unsure what this means and how to fix it as there doesn't seem to be any information on it online. If it helps, the Java code is using AES GCM with no padding, and we have confirmed that the data being sent is indeed 128 bits. How can I take this byte array and create a SecKey from it properly so we can pass secure data? Similarly, I have also tried using RSA encryption for some data, but with this method, I generate the key pair on the iOS client and send the parts of the public key to the Java server where it (seemingly correctly) created the cipher from the passed data. However, trying to send anything encrypted back resulted in "RSAdecrypt wrong input (err -27)" when decrypting: func decryptAESKey(encryptedKeyData: Data, privateKey: SecKey) -&gt; Data? { // Decrypt the received AES key using the private key var error: Unmanaged&lt;CFError&gt;? guard let decryptedKeyData = SecKeyCreateDecryptedData(privateKey, .rsaEncryptionOAEPSHA256, encryptedKeyData as CFData, &amp;error) as Data? else { print("Error decrypting AES key:", error!.takeRetainedValue() as Error) return nil } return decryptedKeyData } Any assistance in figuring out how to properly use SecKeys in these ways would be greatly appreciated. Additionally, the relevant Java code can be provided if necessary.
Posted
by
Post not yet marked as solved
1 Replies
160 Views
I had a couple of questions someone could help me with, as I’m trying to do a little research for my team before they do a bunch of coding. We’re building an anywhere-to-anywhere encrypted file transfer app and wrestling currently with our share functionality. Currently, our users log into their app on their phone, and: within the application use a file picker to select files/photos and then select a destination that they pick from a permission-ed list. However, we really would like to also add the workflow where the user opens the photos or files app, finds a picture/file and then uses the share functionality to pass the object into the application, then The application launches, allowing the user to log into the app, then they pick from a list of permission-ed drop targets within the app and send the object In doing some research this morning regarding sharing files/pictures within our application, I found this thread Eskimo had more or less advised against what we intended to do: https://developer.apple.com/forums/thread/114485 We don’t want to save session tokens because of security concerns; we’re concerned primarily with third-party actors (governments primarily) having access to encryption keys. Is there a best-practice way for my team to open the app we're building and then passing the files/photos into our application? Does anyone have any advice on how to securely launch the application and pass the file/pic into the app given the constraints? Thanks!
Posted
by