Prioritize user privacy and data security in your app. Discuss best practices for data handling, user consent, and security measures to protect user information.

All subtopics
Posts under Privacy & Security topic

Post

Replies

Boosts

Views

Activity

Unexpected native popup during auth login/signout flow
We are implementing authentication login in our iOS mobile application, and during the sign-in/sign-out process, a native system popup appears with the following message: "This allows the app and website to share information about you." This popup interrupts the user experience, and we are concerned it may cause confusion for end users and negatively impact the adoption of our login flow. We would like clarification on the following points: What triggers this popup during the authentication process? Are there any recommended configurations or approaches to suppress or avoid this dialog? If the popup cannot be avoided, what best practices are suggested to ensure a clear and seamless user experience? Our objective is to provide a smooth, user-friendly authentication flow without unexpected system interruptions.
0
0
142
Aug ’25
Sudden Increase of Hard Bounce with privaterelay emails
We have been sending emails through Sparkpost via Braze inc. to the Apple Private Relay users with "@privaterelay.appleid.com" starting from around June 20th or so. Upon August 9th 06:00 UTC, we have noticed a sudden increase of "Hard Bounce" for nearly 20,000 users using the Apple's private relay email address, rendering the email sending useless for these customers. We have been constantly been able to send them emails, including just before this timeframe (e.g. August 9th 03:00 UTC), so it was a very sudden purge of the user data that has been done without our consent. From a business perspective, this hurts a lot for the un-sendable users since we have no way of contacting them if not for the private address. We are desperate to know what has happened for these customers that has been "hard bounced". We are suspecting that it should be tied to the private email and the users primary email (or user data's) tie in the Apple server being gone, but not sure enough since there is no such documentation nor any way to acknowledge what has happened anywhere. We will provide any information possible for resolving. Thank you.
0
0
309
Aug ’25
Certificates, Identifiers, and Provisioning Profiles
Confirmation on "Sign in with Apple JS" Web Implementation Compatibility Hello Developers We are trying to implement "Sign in with Apple JS" on our e-commerce website, which is built on a SaaS platform called Ticimax in Turkey. Our platform provider (Ticimax) claims that a web-based implementation of "Sign in with Apple" is not currently possible. They state this is due to "Apple's browser security policies" that prevent consistent and secure support across all major browsers, particularly Safari with its privacy features. Could you please confirm if there are any fundamental security policies or technical restrictions imposed by Apple that would prevent a standard, secure implementation of "Sign in with Apple JS" on a typical e-commerce website? We know many global websites use this feature successfully. We need to know if our provider's claim has a technical basis from Apple's perspective, or if this is a standard implementation challenge that developers are expected to handle (e.g., using pop-ups instead of redirects to comply with ITP). Any official clarification or documentation you can provide on this matter would be greatly appreciated. Thank you.
0
0
187
Aug ’25
Implementing Script Attachment in a Sandboxed App
Script attachment enables advanced users to create powerful workflows that start in your app. NSUserScriptTask lets you implement script attachment even if your app is sandboxed. This post explains how to set that up. IMPORTANT Most sandboxed apps are sandboxed because they ship on the Mac App Store [1]. While I don’t work for App Review, and thus can’t make definitive statements on their behalf, I want to be clear that NSUserScriptTask is intended to be used to implement script attachment, not as a general-purpose sandbox bypass mechanism. If you have questions or comments, please put them in a new thread. Place it in the Privacy & Security > General subtopic, and tag it with App Sandbox. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] Most but not all. There are good reasons to sandbox your app even if you distribute it directly. See The Case for Sandboxing a Directly Distributed App. Implementing Script Attachment in a Sandboxed App Some apps support script attachment, that is, they allow a user to configure the app to run a script when a particular event occurs. For example: A productivity app might let a user automate repetitive tasks by configuring a toolbar button to run a script. A mail client might let a user add a script that processes incoming mail. When adding script attachment to your app, consider whether your scripting mechanism is internal or external: An internal script is one that only affects the state of the app. A user script is one that operates as the user, that is, it can change the state of other apps or the system as a whole. Supporting user scripts in a sandboxed app is a conundrum. The App Sandbox prevents your app from changing the state of other apps, but that’s exactly what your app needs to do to support user scripts. NSUserScriptTask resolves this conundrum. Use it to run scripts that the user has placed in your app’s Script folder. Because these scripts were specifically installed by the user, their presence indicates user intent and the system runs them outside of your app’s sandbox. Provide easy access to your app’s Script folder Your application’s Scripts folder is hidden within ~/Library. To make it easier for the user to add scripts, add a button or menu item that uses NSWorkspace to show it in the Finder: let scriptsDir = try FileManager.default.url(for: .applicationScriptsDirectory, in: .userDomainMask, appropriateFor: nil, create: true) NSWorkspace.shared.activateFileViewerSelecting([scriptsDir]) Enumerate the available scripts To show a list of scripts to the user, enumerate the Scripts folder: let scriptsDir = try FileManager.default.url(for: .applicationScriptsDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let scriptURLs = try FileManager.default.contentsOfDirectory(at: scriptsDir, includingPropertiesForKeys: [.localizedNameKey]) let scriptNames = try scriptURLs.map { url in return try url.resourceValues(forKeys: [.localizedNameKey]).localizedName! } This uses .localizedNameKey to get the name to display to the user. This takes care of various edge cases, for example, it removes the file name extension if it’s hidden. Run a script To run a script, instantiate an NSUserScriptTask object and call its execute() method: let script = try NSUserScriptTask(url: url) try await script.execute() Run a script with arguments NSUserScriptTask has three subclasses that support additional functionality depending on the type of the script. Use the NSUserUnixTask subsclass to run a Unix script and: Supply command-line arguments. Connect pipes to stdin, stdout, and stderr. Get the termination status. Use the NSUserAppleScriptTask subclass to run an AppleScript, executing either the run handler or a custom Apple event. Use the NSUserAutomatorTask subclass to run an Automator workflow, supplying an optional input. To determine what type of script you have, try casting it to each of the subclasses: let script: NSUserScriptTask = … switch script { case let script as NSUserUnixTask: … use Unix-specific functionality … case let script as NSUserAppleScriptTask: … use AppleScript-specific functionality … case let script as NSUserAutomatorTask: … use Automatic-specific functionality … default: … use generic functionality … }
0
0
827
Aug ’25
Need support downloading key
I’ve been running into an issue for over a day when trying to create a Sign in with Apple key. Each time I attempt to download it, I’m redirected to a page that displays an error and provides no further guidance. I’ve contacted Support and haven’t yet received a reply. I’ve also tried across multiple browsers (Chrome, Safari, Firefox), including incognito modes. Any ideas on how to resolve this? We’re currently stuck and would appreciate guidance.
2
0
102
Apr ’25
App Attest Validation Nonce Not Matched
Greetings, We are struggling to implement device binding according to your documentation. We are generation a nonce value in backend like this: public static String generateNonce(int byteLength) { byte[] randomBytes = new byte[byteLength]; new SecureRandom().nextBytes(randomBytes); return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes); } And our mobile client implement the attestation flow like this: @implementation AppAttestModule - (NSData *)sha256FromString:(NSString *)input { const char *str = [input UTF8String]; unsigned char result[CC_SHA256_DIGEST_LENGTH]; CC_SHA256(str, (CC_LONG)strlen(str), result); return [NSData dataWithBytes:result length:CC_SHA256_DIGEST_LENGTH]; } RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(generateAttestation:(NSString *)nonce resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { if (@available(iOS 14.0, *)) { DCAppAttestService *service = [DCAppAttestService sharedService]; if (![service isSupported]) { reject(@"not_supported", @"App Attest is not supported on this device.", nil); return; } NSData *nonceData = [self sha256FromString:nonce]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *savedKeyId = [defaults stringForKey:@"AppAttestKeyId"]; NSString *savedAttestation = [defaults stringForKey:@"AppAttestAttestationData"]; void (^resolveWithValues)(NSString *keyId, NSData *assertion, NSString *attestationB64) = ^(NSString *keyId, NSData *assertion, NSString *attestationB64) { NSString *assertionB64 = [assertion base64EncodedStringWithOptions:0]; resolve(@{ @"nonce": nonce, @"signature": assertionB64, @"deviceType": @"IOS", @"attestationData": attestationB64 ?: @"", @"keyId": keyId }); }; void (^handleAssertion)(NSString *keyId, NSString *attestationB64) = ^(NSString *keyId, NSString *attestationB64) { [service generateAssertion:keyId clientDataHash:nonceData completionHandler:^(NSData *assertion, NSError *assertError) { if (!assertion) { reject(@"assertion_error", @"Failed to generate assertion", assertError); return; } resolveWithValues(keyId, assertion, attestationB64); }]; }; if (savedKeyId && savedAttestation) { handleAssertion(savedKeyId, savedAttestation); } else { [service generateKeyWithCompletionHandler:^(NSString *keyId, NSError *keyError) { if (!keyId) { reject(@"keygen_error", @"Failed to generate key", keyError); return; } [service attestKey:keyId clientDataHash:nonceData completionHandler:^(NSData *attestation, NSError *attestError) { if (!attestation) { reject(@"attestation_error", @"Failed to generate attestation", attestError); return; } NSString *attestationB64 = [attestation base64EncodedStringWithOptions:0]; [defaults setObject:keyId forKey:@"AppAttestKeyId"]; [defaults setObject:attestationB64 forKey:@"AppAttestAttestationData"]; [defaults synchronize]; handleAssertion(keyId, attestationB64); }]; }]; } } else { reject(@"ios_version", @"App Attest requires iOS 14+", nil); } } @end For validation we are extracting the nonce from the certificate like this: private static byte[] extractNonceFromAttestationCert(X509Certificate certificate) throws IOException { byte[] extensionValue = certificate.getExtensionValue("1.2.840.113635.100.8.2"); if (Objects.isNull(extensionValue)) { throw new IllegalArgumentException("Apple App Attest nonce extension not found in certificate."); } ASN1Primitive extensionPrimitive = ASN1Primitive.fromByteArray(extensionValue); ASN1OctetString outerOctet = ASN1OctetString.getInstance(extensionPrimitive); ASN1Sequence sequence = (ASN1Sequence) ASN1Primitive.fromByteArray(outerOctet.getOctets()); ASN1TaggedObject taggedObject = (ASN1TaggedObject) sequence.getObjectAt(0); ASN1OctetString nonceOctet = ASN1OctetString.getInstance(taggedObject.getObject()); return nonceOctet.getOctets(); } And for the verification we are using this method: private OptionalMethodResult<Void> verifyNonce(X509Certificate certificate, String expectedNonce, byte[] authData) { byte[] expectedNonceHash; try { byte[] nonceBytes = MessageDigest.getInstance("SHA-256").digest(expectedNonce.getBytes()); byte[] combined = ByteBuffer.allocate(authData.length + nonceBytes.length).put(authData).put(nonceBytes).array(); expectedNonceHash = MessageDigest.getInstance("SHA-256").digest(combined); } catch (NoSuchAlgorithmException e) { log.error("Error while validations iOS attestation: {}", e.getMessage(), e); return OptionalMethodResult.ofError(deviceBindError.getChallengeNotMatchedError()); } byte[] actualNonceFromCert; try { actualNonceFromCert = extractNonceFromAttestationCert(certificate); } catch (Exception e) { log.error("Error while extracting nonce from certificate: {}", e.getMessage(), e); return OptionalMethodResult.ofError(deviceBindError.getChallengeNotMatchedError()); } if (!Arrays.equals(expectedNonceHash, actualNonceFromCert)) { return OptionalMethodResult.ofError(deviceBindError.getChallengeNotMatchedError()); } return OptionalMethodResult.empty(); } But the values did not matched. What are we doing wrong here? Thanks.
1
0
928
Sep ’25
App Sandbox Resources
General: Forums subtopic: Privacy & Security > General Forums tag: App Sandbox App Sandbox documentation App Sandbox Design Guide documentation — This is no longer available from Apple. There’s still some info in there that isn’t covered by the current docs but, with the latest updates, it’s pretty minimal (r. 110052019). Still, if you’re curious, you can consult an old copy [1]. App Sandbox Temporary Exception Entitlements archived documentation — To better understand the role of temporary exception entitlements, see this post. Embedding a command-line tool in a sandboxed app documentation Discovering and diagnosing App Sandbox violations (replaces the Viewing Sandbox Violation Reports forums post) Resolving App Sandbox Inheritance Problems forums post The Case for Sandboxing a Directly Distributed App forums post Implementing Script Attachment in a Sandboxed App forums post Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] For example, this one archived by the Wayback Machine.
0
0
2.7k
Jul ’25
Phone heating problem after update iOS 26.0 (23A5297m)
As I had mentioned earlier, I was facing two issues after the initial update, but I’m happy to inform you that both of those issues have now been resolved. However, after updating to iOS 26.0 (23A5297m), I’ve started experiencing a new issue related to overheating. Since yesterday, my iPhone has been getting extremely hot while charging. It also became very hot after clicking just a few photos. The same heating issue occurred again today during charging. This problem only started after the latest update. Kindly look into this issue and advise on how to resolve it.
0
0
83
Jul ’25
Conditional create on iPhone + Safari + Passwords violates the WebAuthn spec
WebAuthn Level 3 § 5.1.3 Step 22 Item 4 states the steps a user agent MUST follow when "conditional" mediation is used in conjunction with required user verification: Let userVerification be the effective user verification requirement for credential creation, a Boolean value, as follows. If pkOptions.authenticatorSelection.userVerification is set to required If options.mediation is set to conditional and user verification cannot be collected during the ceremony, throw a ConstraintError DOMException. Let userVerification be true. On my iPhone 15 Pro Max running iOS 18.5, Safari + Passwords does not exhibit this behavior; instead an error is not reported and user verification is not performed (i.e., the UV bit is 0). Per the spec this results in a registration ceremony failure on the server which is made all the more "annoying" since the credential was created in Passwords forcing a user to then delete the credential. : If the Relying Party requires user verification for this registration, verify that the UV bit of the flags in authData is set. In contrast when I use Google Password Manager + Chrome on a Samsung Galaxy S24 running Android 15, user verification is enforced and the UV bit is 1. Either the UV bit should be 1 after enforcing user verification or an error should be thrown since user verification cannot be performed.
1
0
492
Jul ’25
App Attest Issue in Production - Attestation Object Size Increased
Hi Apple Team and Community, We encountered a sudden and widespread failure related to the App Attest service on Friday, July 25, starting at around 9:22 AM UTC. After an extended investigation, our network engineers noted that the size of the attestation objects received from the attestKey call grew in size notably starting at that time. As a result, our firewall began blocking the requests from our app made to our servers with the Base64-encoded attestation objects in the payload, as these requests began triggering our firewall's max request length rule. Could Apple engineers please confirm whether there was any change rolled out by Apple at or around that time that would cause the attestation object size to increase? Can anyone else confirm seeing this? Any insights from Apple or others would be appreciated to ensure continued stability. Thanks!
3
0
193
Jul ’25
Received my first Digital Services Act spam
Like many/most developers, I gave Connect the info required to comply with the DSA. Perhaps unlike most, I always give unique email addresses so that I can easily track the source of abuse. Yesterday I finally had a phish come in to my DSA address claiming "Message blocked" and doing the standard click-to-login-for-details FOMO bait. So, yep, DSA just becomes yet another public database that malicious actors can use to target you. It would be really nice if Apple provided a way to supply our contact info only for legitimate business purposes. Mail Privacy Protection (or similar) for this would be a start.
1
0
62
Apr ’25
Importing a PEM-based RSA Private Key and its Certificate
This post is an extension to Importing Cryptographic Keys that covers one specific common case: importing a PEM-based RSA private key and its certificate to form a digital identity. If you have questions or comments, start a new thread in Privacy & Security > General. Tag your thread with Security so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Importing a PEM-based RSA Private Key and its Certificate I regularly see folks struggle to import an RSA private key and its corresponding certificate. Importing Cryptographic Keys outlines various options for importing keys, but in this post I want to cover one specific case, namely, a PEM-based RSA private key and its corresponding certificate. Together these form a digital identity, represented as a SecIdentity object. IMPORTANT If you can repackage your digital identity as a PKCS#12, please do. It’s easy to import that using SecPKCS12Import. If you can switch to an elliptic curve (EC) private key, please do. It’s generally better and Apple CryptoKit has direct support for importing an EC PEM. Assuming that’s not the case, let’s explore how to import a PEM-base RSA private key and its corresponding certificate to form a digital identity. Note The code below was built with Xcode 16.2 and tested on the iOS 18.2 simulator. It uses the helper routines from Calling Security Framework from Swift. This code assumes the data protection keychain. If you’re targeting macOS, add kSecUseDataProtectionKeychain to all the keychain calls. See TN3137 On Mac keychain APIs and implementations for more background to that. Unwrap the PEM To start, you need to get the data out of the PEM: /// Extracts the data from a PEM. /// /// As PEM files can contain a large range of data types, you must supply the /// expected prefix and suffix strings. For example, for a certificate these /// are `"-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`. /// /// - important: This assumes the simplest possible PEM format. It does not /// handle metadata at the top of the PEM or PEMs with multiple items in them. func dataFromPEM(_ pem: String, _ expectedPrefix: String, _ expectedSuffix: String) -> Data? { let lines = pem.split(separator: "\n") guard let first = lines.first, first == expectedPrefix, let last = lines.last, last == expectedSuffix else { return nil } let base64 = lines.dropFirst().dropLast().joined() guard let data = Data(base64Encoded: base64) else { return nil } return data } IMPORTANT Read the doc comment to learn about some important limitations with this code. Import a Certificate When adding a digital identity to the keychain, it’s best to import the certificate and the key separately and then add them to the keychain. That makes it easier to track down problems you encounter. To import a PEM-based certificate, extract the data from the PEM and call SecCertificateCreateWithData: /// Import a certificate in PEM format. /// /// - important: See ``dataFromPEM(_:_:_:)`` for some important limitations. func importCertificatePEM(_ pem: String) throws -> SecCertificate { guard let data = dataFromPEM(pem, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----"), let cert = SecCertificateCreateWithData(nil, data as NSData) else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil) } return cert } Here’s an example that shows this in action: let benjyCertificatePEM = """ -----BEGIN CERTIFICATE----- MIIC4TCCAcmgAwIBAgIBCzANBgkqhkiG9w0BAQsFADAfMRAwDgYDVQQDDAdNb3Vz ZUNBMQswCQYDVQQGEwJHQjAeFw0xOTA5MzAxNDI0NDFaFw0yOTA5MjcxNDI0NDFa MB0xDjAMBgNVBAMMBUJlbmp5MQswCQYDVQQGEwJHQjCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAOQe5ai68FQhTVIgpsDK+UOPIrgKzqJcW+wwLnJRp6GV V9EmifJq7wjrXeqmP1XgcNtu7cVhDx+/ONKl/8hscak54HTQrgwE6mK628RThld9 BmZoOjaWWCkoU5bH7ZIYgrKF1tAO5uTAmVJB9v7DQQvKERwjQ10ZbFOW6v8j2gDL esZQbFIC7f/viDXLsPq8dUZuyyb9BXrpEJpXpFDi/wzCV3C1wmtOUrU27xz4gBzi 3o9O6U4QmaF91xxaTk0Ot+/RLI70mR7TYa+u6q7UW/KK9q1+8LeTVs1x24VA5csx HCAQf+xvMoKlocmUxCDBYkTFkmtyhmGRN52XucHgu0kCAwEAAaMqMCgwDgYDVR0P AQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUA A4IBAQAyrArH7+IyHTyEOrv/kZr3s3h4HWczSVeiO9qWD03/fVew84J524DiSBK4 mtAy3V/hqXrzrQEbsfyT7ZhQ6EqB/W0flpVYbku10cSVgoeSfjgBJLqgJRZKFonv OQPjTf9HEDo5A1bQdnUF1y6SwdFaY16lH9mZ5B8AI57mduSg90c6Ao1GvtbAciNk W8y4OTQp4drh18hpHegrgTIbuoWwgy8V4MX6W39XhkCUNhrQUUJk3mEfbC/yqfIG YNds0NRI3QCTJCUbuXvDrLEn4iqRfbzq5cbulQBxBCUtLZFFjKE4M42fJh6D6oRR yZSx4Ac3c+xYqTCjf0UdcUGxaxF/ -----END CERTIFICATE----- """ print(try? importCertificatePEM(benjyCertificatePEM)) If you run this it prints: Optional(<cert(0x11e304c10) s: Benjy i: MouseCA>) Import a Private Key To import a PEM-base RSA private key, extract the data from the PEM and call SecKeyCreateWithData: /// Import an 2048-bit RSA private key in PEM format. /// /// Don’t use this code if: /// /// * If you can switch to an EC key. EC keys are generally better and, for /// this specific case, there’s support for importing them in Apple CryptoKit. /// /// * You can switch to using a PKCS#12. In that case, use the system’s /// `SecPKCS12Import` routine instead. /// /// - important: See ``dataFromPEM(_:_:_:)`` for some important limitations. func importRSA2048PrivateKeyPEM(_ pem: String) throws -> SecKey { // Most private key PEMs are in PKCS#8 format. There’s no way to import // that directly. Instead you need to strip the header to get to the // `RSAPrivateKey` data structure encapsulated within the PKCS#8. Doing that // in the general case is hard. In the specific case of an 2048-bit RSA // key, the following hack works. let rsaPrefix: [UInt8] = [ 0x30, 0x82, 0x04, 0xBE, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x04, 0xA8, ] guard let pkcs8 = dataFromPEM(pem, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----"), pkcs8.starts(with: rsaPrefix) else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil) } let rsaPrivateKey = pkcs8.dropFirst(rsaPrefix.count) return try secCall { SecKeyCreateWithData(rsaPrivateKey as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPrivate, ] as NSDictionary, $0) } } IMPORTANT This code only works with 2048-bit RSA private keys. The comments explain more about that limitation. Here’s an example that shows this in action: let benjyPrivateKeyPEM = """ -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkHuWouvBUIU1S IKbAyvlDjyK4Cs6iXFvsMC5yUaehlVfRJonyau8I613qpj9V4HDbbu3FYQ8fvzjS pf/IbHGpOeB00K4MBOpiutvEU4ZXfQZmaDo2llgpKFOWx+2SGIKyhdbQDubkwJlS Qfb+w0ELyhEcI0NdGWxTlur/I9oAy3rGUGxSAu3/74g1y7D6vHVGbssm/QV66RCa V6RQ4v8MwldwtcJrTlK1Nu8c+IAc4t6PTulOEJmhfdccWk5NDrfv0SyO9Jke02Gv ruqu1FvyivatfvC3k1bNcduFQOXLMRwgEH/sbzKCpaHJlMQgwWJExZJrcoZhkTed l7nB4LtJAgMBAAECggEBAKOPF6ED776SZgrliEog/dmXrhABB6jXybytyw+CRkuP dXhrRmr+isZ9Y0gTzMN4+dILVgW4EozzoP0/sgZ04oWwDqQS30eU2qzRRzMbo+3k oYsZXeu3nhxcYppwXIDsfAEd/ygMFzaadRPKYhrFykR2rA/dpLYCvW2tfm5SuULp RxnKykFlVi8yVT64AovVm0XGOy/QTO5BBbUdftvZY9QCjGn/IEL8QFEz0rxZsb2L s0HgVMUcB1My38RksZQRKLMWCtqLqWnez3oCnPka+dxFQj5RU//vNtRoVh1ExbmW txHz48v00AKQvaudC4ujIspZlY8+UPdYQT0TNjhsfoUCgYEA+7yEvyCgRtYwUNm6 jHTg67LoSldHwENOry63qGZp3rCkWBkPXle7ulgRtuw+e11g4MoMMAgkIGyIGB/Z 6YvnQGmJCTMw+HHIyw3k/OvL1iz4DM+QlxDuD79Zu2j2UIL4maDG0ZDskiJujVAf sFOy4r36TvYedmd7qgh9pgpsFl8CgYEA5/v8PZDs2I1wSDGllGfTr6aeQcxvw98I p8l/8EV/lYpdKQMFndeFZI+dnJCcTeBbeXMmPNTAdL5gOTwDReXamIAdr93k7/x6 iKMHzBrpQZUMEhepSd8zdR1+vLvyszvUU6lvNXcfjwbu7gJQkwbA6kSoXRN+C1Cv i5/w66t0f1cCgYBt02FWwTUrsmaB33uzq4o1SmhthoaXKsY5R3h4z7WAojAQ/13l GwGb2rBfzdG0oJiTeZK3odWhD7iQTdUUPyU0xNY0XVEQExQ3AmjUr0rOte/CJww9 2/UAicrsKG7N0VYEMFCNPVz4pGz22e35T4rLwXZi3J2NqrgZBntK5WEioQKBgEyx L4ii+sn0qGQVlankUUVGjhcuoNxeRZxCrzsdnrovTfEbAKZX88908yQpYqMUQul5 ufBuXVm6/lCtmF9pR8UWxbm4X9E+5Lt7Oj6tvuNhhOYOUHcNhRN4tsdqUygR5XXr E8rXIOXF4wNoXH7ewrQwEoECyq6u8/ny3FDtE8xtAoGBALNFxRGikbQMXhUXj7FA lLwWlNydCxCc7/YwlHfmekDaJRv59+z7SWAR15azhbjqS9oXWJUQ9uvpKF75opE7 MT0GzblkKAYu/3uhTENCjQg+9RFfu5w37E5RTWHD2hANV0YqXUlmH3d+f5uO0xN7 7bpqwYuYzSv1hBfU/yprDco6 -----END PRIVATE KEY----- """ print(try? importRSA2048PrivateKeyPEM(benjyPrivateKeyPEM)) If you run this it prints: Optional(<SecKeyRef algorithm id: 1, key type: RSAPrivateKey, version: 4, 2048 bits (block size: 256), addr: 0x600000c5ce50>) Form a Digital Identity There are two common ways to form a digital identity: SecPKCSImport SecItemCopyMatching SecPKCSImport is the most flexible because it gives you an in-memory digital identity. You can then choose to add it to the keychain or not. However, it requires a PKCS#12 as input. If you’re starting out with separate private key and certificate PEMs, you have to use SecItemCopyMatching. Note macOS also has SecIdentityCreateWithCertificate, but it has some seriously limitations. First, it’s only available on macOS. Second, it requires the key to be in the keychain. If you’re going to add the key to the keychain anyway, you might as well use SecItemCopyMatching. To form a digital identity from a separate private key and certificate: Add the certificate to the keychain. Add the private key to the keychain. Call SecItemCopyMatching to get back a digital identity. Here’s an example of that in action: /// Imports a digital identity composed of separate certificate and private key PEMs. /// /// - important: See ``dataFromPEM(_:_:_:)`` for some important limitations. /// See ``importRSA2048PrivateKeyPEM(_:)`` for alternative strategies that are /// much easier to deploy. func addRSA2048DigitalIdentityPEMToKeychain(certificate: String, privateKey: String) throws -> SecIdentity { // First import the certificate and private key. This has the advantage in // that it triggers an early failure if the data is in the wrong format. let certificate = try importCertificatePEM(certificate) let privateKey = try importRSA2048PrivateKeyPEM(privateKey) // Check that the private key matches the public key in the certificate. If // not, someone has given you bogus credentials. let certificatePublicKey = try secCall { SecCertificateCopyKey(certificate) } let publicKey = try secCall { SecKeyCopyPublicKey(privateKey) } guard CFEqual(certificatePublicKey, publicKey) else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecPublicKeyInconsistent)) } // Add the certificate first. If that fails — and the most likely error is // `errSecDuplicateItem` — we want to stop immediately. try secCall { SecItemAdd([ kSecValueRef: certificate, ] as NSDictionary, nil) } // The add the private key. do { try secCall { SecItemAdd([ kSecValueRef: privateKey, ] as NSDictionary, nil) } } catch let error as NSError { // We ignore a `errSecDuplicateItem` error when adding the key. It’s // possible to have multiple digital identities that share the same key, // so if you try to add the key and it’s already in the keychain then // that’s fine. guard error.domain == NSOSStatusErrorDomain, error.code == errSecDuplicateItem else { throw error } } // Finally, search for the resulting identity. // // I originally tried querying for the identity based on the certificate’s // attributes — the ones that contribute to uniqueness, namely // `kSecAttrCertificateType`, `kSecAttrIssuer`, and `kSecAttrSerialNumber` — // but that failed for reasons I don't fully understand (r. 144152660). So // now I get all digital identities and find the one with our certificate. let identities = try secCall { SecItemCopyMatching([ kSecClass: kSecClassIdentity, kSecMatchLimit: kSecMatchLimitAll, kSecReturnRef: true, ] as NSDictionary, $0) } as! [SecIdentity] let identityQ = try identities.first { i in try secCall { SecIdentityCopyCertificate(i, $0) } == certificate } return try secCall(Int(errSecItemNotFound)) { identityQ } } IMPORTANT This code is quite subtle. Read the comments for an explanation as to why it works the way it does. Further reading For more information about the APIs and techniques used above, see: Importing Cryptographic Keys On Cryptographic Keys Formats SecItem: Fundamentals SecItem: Pitfalls and Best Practices Calling Security Framework from Swift TN3137 On Mac keychain APIs and implementations Finally, for links to documentation and other resources, see Security Resources. Revision History 2025-02-13 Added code to check for mismatched private key and certificate. 2025-02-04 First posted.
0
0
581
Feb ’25
Cannot revoke the account of Sign in with Apple
Our service has ended and the app has been removed from the App store. This app supported Sign in with Apple, but even if I try to revoke the account from the iOS settings or account.apple.com on the web, but can't delete it and no error is displayed. Does anyone know the cause of this problem or have encountered it? I'm not sure if it's related, but this app was previously transferred from another organization.
0
0
262
Jan ’25
Detecting iOS screen sharing
Hello, Is there any way to detect if the iOS screen is currently being shared via FaceTime or iPhone Mirroring? Our application relies on this information to help ensure that users are not accessing it from one location while physically being in another.
1
0
198
Jul ’25
Privacy & Security Resources
General: Forums topic: Privacy & Security Privacy Resources Security Resources Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
0
0
323
Jul ’25
Trusted Execution Resources
Trusted execution is a generic name for a Gatekeeper and other technologies that aim to protect users from malicious code. General: Forums topic: Code Signing Forums 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 forums post Resolving Trusted Execution Problems forums post App Translocation Notes forums 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"
0
0
3.2k
Jul ’25
Should ATT come before a 3rd party CMP? Does the order matter?
When presenting a cookie banner for GDPR purposes, should ATT precede the cookie banner? It seems that showing a Cookie Banner and then showing the ATT permission prompt afterwards (if a user elects to allow cookies/tracking) would be more appropriate. Related question: Should the “Allow Tracking” toggle for an app in system settings serve as a master switch for any granular tracking that might be managed by a 3rd party Consent Management Platform? If ATT is intended to serve as a master switch for tracking consent, if the ATT prompt is presented before a cookie banner, should the banner even appear if a user declines tracking consent? I’m not finding any good resources that describe this flow in detail and I’m seeing implementations all over the place on this. Help! Thanks!!!
0
0
185
Jul ’25