Security

RSS for tag

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

Posts under Security tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Security Resources
General: Apple Platform Security support document Security Overview Cryptography: DevForums tags: Security, Apple CryptoKit Security framework documentation Apple CryptoKit framework documentation Common ****** man pages — For the full list of pages, run: % man -k 3cc For more information about man pages, see Reading UNIX Manual Pages. On Cryptographic Key Formats DevForums post SecItem attributes for keys DevForums post CryptoCompatibility sample code Keychain: DevForums tags: Security Security > Keychain Items documentation TN3137 On Mac keychain APIs and implementations SecItem Fundamentals DevForums post SecItem Pitfalls and Best Practices DevForums post Investigating hard-to-reproduce keychain problems DevForums post Smart cards and other secure tokens: DevForums tag: CryptoTokenKit CryptoTokenKit framework documentation Mac-specific resources: DevForums tags: Security Foundation, Security Interface Security Foundation framework documentation Security Interface framework documentation BSD Privilege Escalation on macOS Related: Networking Resources — This covers high-level network security, including HTTPS and TLS. Network Extension Resources — This covers low-level network security, including VPN and content filters. Code Signing Resources Notarisation Resources Trusted Execution Resources — This includes Gatekeeper. App Sandbox Resources Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
0
0
3.1k
Jan ’25
SecItem: Fundamentals
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.
0
0
3.7k
May ’25
Importing Cryptographic Keys
I regularly see folks having problems importing cryptographic keys, so I thought I’d write down some hints and tips on how to recognise and import the various key formats. This post describes how to import each type of key. A companion post, On Cryptographic Keys Formats, discusses how to recognise the format of the data you have. If you have questions about any of this stuff, put them a new thread in Privacy & Security > General. Tag your thread with Security or Apple CrytoKit, or both!, so that I see it. Finally, if you want to see a specific example of these techniques in action, see Importing a PEM-based RSA Private Key and its Certificate. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Importing Cryptographic Keys Apple platforms support 5 different key types: RSA (Security framework only) SECG secp256r1, aka NIST P-256 (Security framework and Apple CryptoKit) SECG secp384r1, aka NIST P-384 (Security framework and Apple CryptoKit) SECG secp521r1, aka NIST P-521 (Security framework and Apple CryptoKit) Curve 25519 (Apple CryptoKit only) This post explains how to import each type of key. If you’re not sure what type of key you have, or how its encoded, or you run into weird problems and suspect that you might be using the wrong key type or encoding, read On Cryptographic Keys Formats. Note This post focuses on APIs available on all Apple platforms. Some Mac-specific APIs can import other formats. The Security framework uses the SecKey type for all key types that it supports. Apple CryptoKit has a different approach: It uses different types for different key types, which helps catch common programming mistakes at compile time. There are 4 top-level enums: P256, for SECG secp256r1 P384, for SECG secp384r1 P521, for SECG secp521r1 Curve25519, for Curve 25519 Each of those enums contains a KeyAgreement enum and a Signing enum, where you express the intended purpose for your key. In this post I always use Signing but the code will work the same if you choose KeyAgreement. Finally, in each of those enums you’ll find both Public and Private types; these are structs that represent a specific public or private key. Undo PEM Encoding Writing a full-featured PEM parser is tricky. It is, however, relatively straightforward to undo the PEM encoding of a known simple PEM file. For example, if you have this file: % cat p256-private-key.pem -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmGp6kcu19PgWNuga r/CDWncdxmhlxAeo6ERpz2q4pHehRANCAASXR+mBqrjqcaJVzZoVYoWMQGAG8eQY Jg0x4ad/bCs1qaMTLyMtsANR2dgANIfU7lKEeZAxPap8ch+I1LtW2pHH -----END PRIVATE KEY----- Decode it like so: let u = URL(fileURLWithPath: "p256-private-key.pem") guard let pem = try? String(contentsOf: u) else { … handle error … } let pemBase64 = pem .split(separator: "\n") .dropFirst() .dropLast() .joined() guard let pemData = Data(base64Encoded: String(pemBase64)) else { … handle error … } debugPrint(pemData as NSData) // prints: // <30818702 01003013 06072a86 48ce3d02 0106082a 8648ce3d … d4bb56da 91c7> Import RSA Keys Use SecKeyCreateWithData to import an RSA key. If you have an RSAPublicKey structure like this: % xxd -p rsa-public-key.der 3082010a0282010100cf243c324b262470131648614b62ee9c52af43319c 2498a7c16ba9790bb3a881f960f7b0303f8f49e86fedd6813be5fa888393 55d04426df0050dbb771eb683773b7dd929949695093f910c8dcdb633674 de986ada8d643e0e819b7cd5ab3bde4372103797472dc843a2711699e21a 4afddeed9f62810316903457342c345a35ebb2f06da019fed2afa56e7856 6e75a0d712849ae255155d9304348318930611b3b4f1153d77ee5970f076 299c548c8afff53157205048ade26d40930af2ecc96d4f77e8591523b767 fa3cdbc45a8a210339c4a556cea2e0dfa3ee819b62e463f75d87a53c2fbd 1bbcb8ec8fe2e8000ce37235fa903113c7b37d9c2a8b39c54b0203010001 % % dumpasn1 -p -a rsa-public-key.der SEQUENCE { INTEGER 00 CF 24 3C 32 4B 26 24 70 13 16 48 61 4B 62 EE 9C 52 AF 43 31 9C 24 98 A7 C1 6B A9 79 0B B3 A8 81 F9 60 F7 B0 30 3F 8F 49 E8 6F ED D6 81 3B E5 FA 88 83 93 55 D0 44 26 DF 00 50 DB B7 71 EB 68 37 73 B7 DD 92 99 49 69 50 93 F9 10 C8 DC DB 63 36 74 DE 98 6A DA 8D 64 3E 0E 81 9B 7C D5 AB 3B DE 43 72 10 37 97 47 2D C8 43 A2 71 16 99 E2 1A 4A FD DE ED 9F 62 81 03 16 90 34 57 34 2C 34 5A 35 EB B2 F0 6D A0 19 FE D2 AF A5 6E 78 56 6E 75 A0 D7 12 84 9A E2 55 15 5D 93 04 34 83 18 93 06 11 B3 B4 F1 15 3D 77 EE 59 70 F0 76 29 9C 54 8C 8A FF F5 31 57 20 50 48 AD E2 6D 40 93 0A F2 EC C9 6D 4F 77 E8 59 15 23 B7 67 FA 3C DB C4 5A 8A 21 03 39 C4 A5 56 CE A2 E0 DF A3 EE 81 9B 62 E4 63 F7 5D 87 A5 3C 2F BD 1B BC B8 EC 8F E2 E8 00 0C E3 72 35 FA 90 31 13 C7 B3 7D 9C 2A 8B 39 C5 4B INTEGER 65537 } Import it with this code: let u = URL(fileURLWithPath: "rsa-public-key.der") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = SecKeyCreateWithData(keyBytes as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPublic, ] as NSDictionary, nil) else { … handle error … } print(privateKey) // prints: // <SecKeyRef algorithm id: 1, key type: RSAPublicKey, version: 4, block size: 2048 bits, exponent: {hex: 10001, decimal: 65537}, modulus: …, addr: …> Note You don’t need to include any other attributes in the dictionary you pass to SecKeyCreateWithData. Specifically, many folks think that they need to pass in the kSecAttrKeySizeInBits attribute. This isn’t the case; SecKeyCreateWithData will work out the key size from the key data. If you have an RSAPrivateKey structure like this: % xxd -p rsa-private-key.der 308204a30201000282010100cf243c324b262470131648614b62ee9c52af 43319c2498a7c16ba9790bb3a881f960f7b0303f8f49e86fedd6813be5fa 88839355d04426df0050dbb771eb683773b7dd929949695093f910c8dcdb 633674de986ada8d643e0e819b7cd5ab3bde4372103797472dc843a27116 99e21a4afddeed9f62810316903457342c345a35ebb2f06da019fed2afa5 6e78566e75a0d712849ae255155d9304348318930611b3b4f1153d77ee59 70f076299c548c8afff53157205048ade26d40930af2ecc96d4f77e85915 23b767fa3cdbc45a8a210339c4a556cea2e0dfa3ee819b62e463f75d87a5 3c2fbd1bbcb8ec8fe2e8000ce37235fa903113c7b37d9c2a8b39c54b0203 0100010282010044b694716a946089fd0aeb3fbb2e3a5108ecb2b186466d 8d58904a4ba92213c7e9ddcccc5974fc275c3fa4f9ff2ccb816c3f996462 0df9870827ca7af4034f32f5e40c505121151a71bbb161b041e68b6e0159 363901a63b1fbcc6c3866da3127bf51e84125ebe452c8a7a513102dc0dfc 61331a2826fbcb4452d88aaa0f43ccfe436e1554f95bdd883c41e7e8529f acd7556ba539af3e083e7143ddf8637f67b59eea494b02396ff5089a1964 48dc8f7eb236d2f92a3358d0d6f5af1443205400bbd2758d3ec7cb208c11 7d78d68409f987fd6e43a93a26961c10c05f85458821594d242f8106856c 393f3b971cae1bfc20319e37147b22d2d2179ed5844e8102818100f27c96 e84d6ff814c56996a0e143fa85106d74e2eaa848347d8681bbcc396d85fc b51d318f543ad25090fe087e0e1ee0202f2ee8674e58609c22cc56e305c5 c55b016d0ca45c847ac88b59dd8a597388b09d7d5f86e2cdf60cb7660d94 a5e4e6f539506a6aacdf67fb9458b016a63d72392129eff5faa210a1739d 948ef0453b02818100daaf65e651382baed753222ab53dfb2f79ef96c6bd ec1c2822e5b8405900cf9203b2a0e015d12042cc9e686bbf3e5d2d732ed7 45e2a1cc1787637b8f14727dd5da11261d3a7cbe3521296f269cdf2a16ea 2974a710b14f3e61484d2580fef9c5bf4965a7a9ee6055a8c27867609408 7ef1643e81ab17307ca40b79166b693f310281803ed463719ba6f87bc14f 039579e8d83fa42b084f478804f57cd4de469fbafd92eb10ae98c9cf8452 3c47e55aa3f6daaf2e07abbad211adba929a3da201bedc28afd4e5c191d0 db0ec969ba063a33c548d4a269fad7836ae467151a1f48b5d762b4857e3d a4985866a3fc2322b52babde2dc95709730dd6f2423327d0775cf0430281 8100c4f14336c99c6992bb2e8e4da20de0c21ff14a7b4f9d6cba24bb7754 d412ebdc96e1ef09fffbe72ee172239e2d8c2f83f8008e34cce663942904 c9c8d0644fb920fb62b4ddf06ba813666a487eec67ce5d31da717e920048 b079d9a855e4caf270d3dbedc416fec1060ba53d8c77a4b31617ee46fedb 127a9d8e0b8dca4bed710281800c2fe643bfc8c81b39f1a574c751d2c5ee 0ce836a772197350f2f0a6a4d5248790a0cdf0c25a69a8834d645ea3c96e e740d95adeea689259ac4ce36a7310c86c9c35441fdd96ff8cec89a65f8c 8666bbc2a42cd2a58e70b1e8b2269ed6307c5a2143cbd41de4682dea4a38 8a7c8d2f4088e9a2008fa986f9b0e92fa517ecc77b % % dumpasn1 -p -a rsa-private-key.der SEQUENCE { INTEGER 0 INTEGER 00 CF 24 3C 32 4B 26 24 70 13 16 48 61 4B 62 EE 9C 52 AF 43 31 9C 24 98 A7 C1 6B A9 79 0B B3 A8 81 F9 60 F7 B0 30 3F 8F 49 E8 6F ED D6 81 3B E5 FA 88 83 93 55 D0 44 26 DF 00 50 DB B7 71 EB 68 37 73 B7 DD 92 99 49 69 50 93 F9 10 C8 DC DB 63 36 74 DE 98 6A DA 8D 64 3E 0E 81 9B 7C D5 AB 3B DE 43 72 10 37 97 47 2D C8 43 A2 71 16 99 E2 1A 4A FD DE ED 9F 62 81 03 16 90 34 57 34 2C 34 5A 35 EB B2 F0 6D A0 19 FE D2 AF A5 6E 78 56 6E 75 A0 D7 12 84 9A E2 55 15 5D 93 04 34 83 18 93 06 11 B3 B4 F1 15 3D 77 EE 59 70 F0 76 29 9C 54 8C 8A FF F5 31 57 20 50 48 AD E2 6D 40 93 0A F2 EC C9 6D 4F 77 E8 59 15 23 B7 67 FA 3C DB C4 5A 8A 21 03 39 C4 A5 56 CE A2 E0 DF A3 EE 81 9B 62 E4 63 F7 5D 87 A5 3C 2F BD 1B BC B8 EC 8F E2 E8 00 0C E3 72 35 FA 90 31 13 C7 B3 7D 9C 2A 8B 39 C5 4B INTEGER 65537 INTEGER 44 B6 94 71 6A 94 60 89 FD 0A EB 3F BB 2E 3A 51 08 EC B2 B1 86 46 6D 8D 58 90 4A 4B A9 22 13 C7 E9 DD CC CC 59 74 FC 27 5C 3F A4 F9 FF 2C CB 81 6C 3F 99 64 62 0D F9 87 08 27 CA 7A F4 03 4F 32 F5 E4 0C 50 51 21 15 1A 71 BB B1 61 B0 41 E6 8B 6E 01 59 36 39 01 A6 3B 1F BC C6 C3 86 6D A3 12 7B F5 1E 84 12 5E BE 45 2C 8A 7A 51 31 02 DC 0D FC 61 33 1A 28 26 FB CB 44 52 D8 8A AA 0F 43 CC FE 43 6E 15 54 F9 5B DD 88 3C 41 E7 E8 52 9F AC D7 55 6B A5 39 AF 3E 08 3E 71 43 DD F8 63 7F 67 B5 9E EA 49 4B 02 39 6F F5 08 9A 19 64 48 DC 8F 7E B2 36 D2 F9 2A 33 58 D0 D6 F5 AF 14 43 20 54 00 BB D2 75 8D 3E C7 CB 20 8C 11 7D 78 D6 84 09 F9 87 FD 6E 43 A9 3A 26 96 1C 10 C0 5F 85 45 88 21 59 4D 24 2F 81 06 85 6C 39 3F 3B 97 1C AE 1B FC 20 31 9E 37 14 7B 22 D2 D2 17 9E D5 84 4E 81 INTEGER 00 F2 7C 96 E8 4D 6F F8 14 C5 69 96 A0 E1 43 FA 85 10 6D 74 E2 EA A8 48 34 7D 86 81 BB CC 39 6D 85 FC B5 1D 31 8F 54 3A D2 50 90 FE 08 7E 0E 1E E0 20 2F 2E E8 67 4E 58 60 9C 22 CC 56 E3 05 C5 C5 5B 01 6D 0C A4 5C 84 7A C8 8B 59 DD 8A 59 73 88 B0 9D 7D 5F 86 E2 CD F6 0C B7 66 0D 94 A5 E4 E6 F5 39 50 6A 6A AC DF 67 FB 94 58 B0 16 A6 3D 72 39 21 29 EF F5 FA A2 10 A1 73 9D 94 8E F0 45 3B INTEGER 00 DA AF 65 E6 51 38 2B AE D7 53 22 2A B5 3D FB 2F 79 EF 96 C6 BD EC 1C 28 22 E5 B8 40 59 00 CF 92 03 B2 A0 E0 15 D1 20 42 CC 9E 68 6B BF 3E 5D 2D 73 2E D7 45 E2 A1 CC 17 87 63 7B 8F 14 72 7D D5 DA 11 26 1D 3A 7C BE 35 21 29 6F 26 9C DF 2A 16 EA 29 74 A7 10 B1 4F 3E 61 48 4D 25 80 FE F9 C5 BF 49 65 A7 A9 EE 60 55 A8 C2 78 67 60 94 08 7E F1 64 3E 81 AB 17 30 7C A4 0B 79 16 6B 69 3F 31 INTEGER 3E D4 63 71 9B A6 F8 7B C1 4F 03 95 79 E8 D8 3F A4 2B 08 4F 47 88 04 F5 7C D4 DE 46 9F BA FD 92 EB 10 AE 98 C9 CF 84 52 3C 47 E5 5A A3 F6 DA AF 2E 07 AB BA D2 11 AD BA 92 9A 3D A2 01 BE DC 28 AF D4 E5 C1 91 D0 DB 0E C9 69 BA 06 3A 33 C5 48 D4 A2 69 FA D7 83 6A E4 67 15 1A 1F 48 B5 D7 62 B4 85 7E 3D A4 98 58 66 A3 FC 23 22 B5 2B AB DE 2D C9 57 09 73 0D D6 F2 42 33 27 D0 77 5C F0 43 INTEGER 00 C4 F1 43 36 C9 9C 69 92 BB 2E 8E 4D A2 0D E0 C2 1F F1 4A 7B 4F 9D 6C BA 24 BB 77 54 D4 12 EB DC 96 E1 EF 09 FF FB E7 2E E1 72 23 9E 2D 8C 2F 83 F8 00 8E 34 CC E6 63 94 29 04 C9 C8 D0 64 4F B9 20 FB 62 B4 DD F0 6B A8 13 66 6A 48 7E EC 67 CE 5D 31 DA 71 7E 92 00 48 B0 79 D9 A8 55 E4 CA F2 70 D3 DB ED C4 16 FE C1 06 0B A5 3D 8C 77 A4 B3 16 17 EE 46 FE DB 12 7A 9D 8E 0B 8D CA 4B ED 71 INTEGER 0C 2F E6 43 BF C8 C8 1B 39 F1 A5 74 C7 51 D2 C5 EE 0C E8 36 A7 72 19 73 50 F2 F0 A6 A4 D5 24 87 90 A0 CD F0 C2 5A 69 A8 83 4D 64 5E A3 C9 6E E7 40 D9 5A DE EA 68 92 59 AC 4C E3 6A 73 10 C8 6C 9C 35 44 1F DD 96 FF 8C EC 89 A6 5F 8C 86 66 BB C2 A4 2C D2 A5 8E 70 B1 E8 B2 26 9E D6 30 7C 5A 21 43 CB D4 1D E4 68 2D EA 4A 38 8A 7C 8D 2F 40 88 E9 A2 00 8F A9 86 F9 B0 E9 2F A5 17 EC C7 7B } Import it with this code: let u = URL(fileURLWithPath: "rsa-private-key.der") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = SecKeyCreateWithData(keyBytes as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPrivate, ] as NSDictionary, nil) else { … handle error … } print(privateKey) // prints: // <SecKeyRef algorithm id: 1, key type: RSAPrivateKey, version: 4, block size: 2048 bits, addr: …> Finally, an oft-forgotten feature of SecKeyCreateWithData is that it can undo a SubjectPublicKeyInfo wrapper. So, if you have an RSA public key wrapped in a SubjectPublicKeyInfo like this: % xxd -p public-key-rsa.der 30820122300d06092a864886f70d01010105000382010f003082010a0282 010100bce736006d9b0a2a49508f32e8d66f2b26236263a476f5a2eaf6af 34f0055b12b3bea5f5a62f3aab82274c3e3b21d15cc741100c670dd7687d 9c7e5c012d95bf5177993087df441c9944d10dff0767abfd6e412df279e4 e518b905e5582f967b6b2a64eeaeef712c594268fbff9cc2e63833ebffb7 f00c61fd7224ae2328047e13bbb904899e9ad5c9f44cfff5cd9a2df5a5b6 29bec605d6ecdce5dacba40cb119695f7c3dbd19e6fcd86a13700dfe6818 d1894aca9172a1e857540641971f7d7c9533aee2047c16c1c4f125e830b2 7d5e80d445c2fe09fa5586ee0bb105800fd1e8489e44b2f123eeef1cceeb eb1ba2d094923944181c513208c1f37fca31e50203010001 % % dumpasn1 -p -a public-key-rsa.der SEQUENCE { SEQUENCE { OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) NULL } BIT STRING, encapsulates { SEQUENCE { INTEGER 00 BC E7 36 00 6D 9B 0A 2A 49 50 8F 32 E8 D6 6F 2B 26 23 62 63 A4 76 F5 A2 EA F6 AF 34 F0 05 5B 12 B3 BE A5 F5 A6 2F 3A AB 82 27 4C 3E 3B 21 D1 5C C7 41 10 0C 67 0D D7 68 7D 9C 7E 5C 01 2D 95 BF 51 77 99 30 87 DF 44 1C 99 44 D1 0D FF 07 67 AB FD 6E 41 2D F2 79 E4 E5 18 B9 05 E5 58 2F 96 7B 6B 2A 64 EE AE EF 71 2C 59 42 68 FB FF 9C C2 E6 38 33 EB FF B7 F0 0C 61 FD 72 24 AE 23 28 04 7E 13 BB B9 04 89 9E 9A D5 C9 F4 4C FF F5 CD 9A 2D F5 A5 B6 29 BE C6 05 D6 EC DC E5 DA CB A4 0C B1 19 69 5F 7C 3D BD 19 E6 FC D8 6A 13 70 0D FE 68 18 D1 89 4A CA 91 72 A1 E8 57 54 06 41 97 1F 7D 7C 95 33 AE E2 04 7C 16 C1 C4 F1 25 E8 30 B2 7D 5E 80 D4 45 C2 FE 09 FA 55 86 EE 0B B1 05 80 0F D1 E8 48 9E 44 B2 F1 23 EE EF 1C CE EB EB 1B A2 D0 94 92 39 44 18 1C 51 32 08 C1 F3 7F CA 31 E5 INTEGER 65537 } } } Import it with this code: let u = URL(fileURLWithPath: "public-key-rsa.der") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = SecKeyCreateWithData(keyBytes as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPublic, ] as NSDictionary, nil) else { … handle error … } print(privateKey) // prints: // <SecKeyRef algorithm id: 1, key type: RSAPublicKey, version: 4, block size: 2048 bits, exponent: {hex: 10001, decimal: 65537}, modulus: …, addr: …> Import SECG Keys with Security Framework If you’re working with Security framework, use SecKeyCreateWithData to import an SECG key. If you have a secp256r1 public key in X9.63 format: % xxd p256-public-key.dat 00000000: 0497 47e9 81aa b8ea 71a2 55cd 9a15 6285 ..G.....q.U...b. 00000010: 8c40 6006 f1e4 1826 0d31 e1a7 7f6c 2b35 .@`....&.1...l+5 00000020: a9a3 132f 232d b003 51d9 d800 3487 d4ee .../#-..Q...4... 00000030: 5284 7990 313d aa7c 721f 88d4 bb56 da91 R.y.1=.|r....V.. 00000040: c7 . Import it with this code: let u = URL(fileURLWithPath: "p256-public-key.dat") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = SecKeyCreateWithData(keyBytes as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPublic, ] as NSDictionary, nil) else { … handle error … } print(privateKey) // prints: // <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 256 bits, y: …, x: …, addr: …> Note I’m using secp256r1 as an example. The code in this section will work for the other SECG key types, secp384r1 and secp521r1. And if you have a secp256r1 private key in X9.63 format: % xxd p256-private-key.dat 00000000: 0497 47e9 81aa b8ea 71a2 55cd 9a15 6285 ..G.....q.U...b. 00000010: 8c40 6006 f1e4 1826 0d31 e1a7 7f6c 2b35 .@`....&.1...l+5 00000020: a9a3 132f 232d b003 51d9 d800 3487 d4ee .../#-..Q...4... 00000030: 5284 7990 313d aa7c 721f 88d4 bb56 da91 R.y.1=.|r....V.. 00000040: c798 6a7a 91cb b5f4 f816 36e8 1aaf f083 ..jz......6..... 00000050: 5a77 1dc6 6865 c407 a8e8 4469 cf6a b8a4 Zw..he....Di.j.. 00000060: 77 w Import it with this code: let u = URL(fileURLWithPath: "p256-private-key.dat") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = SecKeyCreateWithData(keyBytes as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPrivate, ] as NSDictionary, nil) else { … handle error … } print(privateKey) // prints: // <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPrivateKey, version: 4, block size: 256 bits, addr: …> Import SECG Keys with Apple CryptoKit Apple CryptoKit can import SECG keys in three different ways: X9.63 raw key bytes DER encoding PEM encoding If you have a secp256r1 public key in X9.63 format, import it with this code: let u = URL(fileURLWithPath: "p256-public-key.dat") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let publicKey = try? P256.Signing.PublicKey(x963Representation: keyBytes) else { … handle error … } print(publicKey) // prints: // PublicKey(impl: CryptoKit.CoreCryptoNISTCurvePublicKeyImpl<CryptoKit.P256.CurveDetails>(keyBytes: […]])) Note I’m using secp256r1 as an example. The code in this section will work for the other SECG key types, secp384r1 and secp521r1. If you have a secp256r1 private key in X9.63 format import it with this code: let u = URL(fileURLWithPath: "p256-private-key.dat") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = try? P256.Signing.PrivateKey(x963Representation: keyBytes) else { … handle error … } print(privateKey) // prints: // PrivateKey(impl: CryptoKit.CoreCryptoNISTCurvePrivateKeyImpl<CryptoKit.P256.CurveDetails>(key: CryptoKit.SecureBytes(backing: CryptoKit.SecureBytes.Backing))) CryptoKit can also import a DER-encoded SECG key. For example, it can import the following using the init(derRepresentation:) initialiser: % xxd -p public-key-p256.der 3059301306072a8648ce3d020106082a8648ce3d030107034200042c21f3 7049d4464afbf01813c51a4e1ef7a8101d2aa12b6a889635bc7c37e9011b fdd54006fdebdaef0d86a6d662561347982c95276013d1c1cd2d7865aff0 23 % % dumpasn1 -p -a public-key-p256.der SEQUENCE { SEQUENCE { OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) } BIT STRING 04 2C 21 F3 70 49 D4 46 4A FB F0 18 13 C5 1A 4E 1E F7 A8 10 1D 2A A1 2B 6A 88 96 35 BC 7C 37 E9 01 1B FD D5 40 06 FD EB DA EF 0D 86 A6 D6 62 56 13 47 98 2C 95 27 60 13 D1 C1 CD 2D 78 65 AF F0 23 } % % xxd -p private-key-p256.der 308187020100301306072a8648ce3d020106082a8648ce3d030107046d30 6b0201010420986a7a91cbb5f4f81636e81aaff0835a771dc66865c407a8 e84469cf6ab8a477a144034200049747e981aab8ea71a255cd9a1562858c 406006f1e418260d31e1a77f6c2b35a9a3132f232db00351d9d8003487d4 ee52847990313daa7c721f88d4bb56da91c7 % % dumpasn1 -p -a private-key-p256.der SEQUENCE { INTEGER 0 SEQUENCE { OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) } OCTET STRING, encapsulates { SEQUENCE { INTEGER 1 OCTET STRING 98 6A 7A 91 CB B5 F4 F8 16 36 E8 1A AF F0 83 5A 77 1D C6 68 65 C4 07 A8 E8 44 69 CF 6A B8 A4 77 [1] { BIT STRING 04 97 47 E9 81 AA B8 EA 71 A2 55 CD 9A 15 62 85 8C 40 60 06 F1 E4 18 26 0D 31 E1 A7 7F 6C 2B 35 A9 A3 13 2F 23 2D B0 03 51 D9 D8 00 34 87 D4 EE 52 84 79 90 31 3D AA 7C 72 1F 88 D4 BB 56 DA 91 C7 } } } } Finally, CryptoKit can import a PEM-encoded SECG. For example, it can import the following using the init(pemRepresentation:) initialiser: % cat public-key-p256.pem -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELCHzcEnURkr78BgTxRpOHveoEB0q oStqiJY1vHw36QEb/dVABv3r2u8NhqbWYlYTR5gslSdgE9HBzS14Za/wIw== -----END PUBLIC KEY----- % cat private-key-p256.pem -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmGp6kcu19PgWNuga r/CDWncdxmhlxAeo6ERpz2q4pHehRANCAASXR+mBqrjqcaJVzZoVYoWMQGAG8eQY Jg0x4ad/bCs1qaMTLyMtsANR2dgANIfU7lKEeZAxPap8ch+I1LtW2pHH -----END PRIVATE KEY----- Mapping SECG Keys between Apple CryptoKit and Security Framework If you need to map an SECG key from Apple CryptoKit to Security framework, or vice versa, use the X9.63 format. Imagine that you’re working in Security framework but you need to import a PEM key. SecKeyCreateWithData will not accept an SECG key in PEM format; it requires that the key be in X9.63 format. CryptoKit can import a PEM key but you want to continue using your existing Security framework code. Fortunately there’s a way out of this bind: Import the PEM key using Apple CryptoKit. Get the X9.63 representation. Create the Security framework key from that. For example, the following routine imports a PEM secp256r1 private key and returns a SecKey object: func createSecKeyWithPEMSecp256r1Private(_ pem: String) throws -> SecKey { let privateKeyCK = try P256.Signing.PrivateKey(pemRepresentation: pem) let x963Data = privateKeyCK.x963Representation var errorQ: Unmanaged<CFError>? = nil guard let privateKeySF = SecKeyCreateWithData(x963Data as NSData, [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPrivate, ] as NSDictionary, &errorQ) else { throw errorQ!.takeRetainedValue() } return privateKeySF } To go the other way, from Security framework to CryptoKit, call SecKeyCopyExternalRepresentation to get the X9.63 representation of the key and then create a CryptoKit value using the init(x963Representation:) initialiser. Importing Curve 25519 Keys Apple CryptoKit supports Curve 25519 keys. If you have the raw bytes of a Curve 25519 public key: % xxd curve25519-public-key.dat 00000000: 910b f46f 0c0d c836 878f a708 60fd de21 ...o...6....`..! 00000010: 9d5f 6265 0a83 a7c5 923d 2ab7 4b81 76c5 ._be.....=*.K.v. Import it with this code: let u = URL(fileURLWithPath: "curve25519-public-key.dat") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let publicKey = try? Curve25519.Signing.PublicKey(rawRepresentation: keyBytes) else { … handle error … } print(publicKey) // prints: // PublicKey(baseKey: CryptoKit.Curve25519.Signing.CoreCryptoCurve25519PublicKeyImpl(keyBytes: […])) If you have the raw bytes of a Curve 25519 private key: % xxd curve25519-private-key.dat 00000000: 9fd9 0805 255b ae86 a6c3 035b 2de8 37e9 ....%[.....[-.7. 00000010: 29ea 792e a11f d466 e67e d0b2 65c0 a999 ).y....f.~..e... Import it with this code: let u = URL(fileURLWithPath: "curve25519-private-key.dat") guard let keyBytes = try? Data(contentsOf: u) else { … handle error … } guard let privateKey = try? Curve25519.Signing.PrivateKey(rawRepresentation: keyBytes) else { … handle error … } print(privateKey) // prints: // PrivateKey(baseKey: CryptoKit.Curve25519.Signing.CoreCryptoCurve25519PrivateKeyImpl(key: CryptoKit.SecureBytes(backing: CryptoKit.SecureBytes.Backing))) Revision History 2025-02-04 Added a link to Importing a PEM-based RSA Private Key and its Certificate. Made other minor editorial changes. 2021-05-23 First posted.
0
0
4.9k
Feb ’25
Swift AES CBC 256 Encryption With Static 32bit Key and 32bit IV
We have the below Implementation in Android and the same has to be integrated into Swift. Key :- "d95acd54b4a821ff32c52825q931c194" IV :- "687b9509c25a34b8ad076346s8353d67" Here Both the Key and IV are 32 bits and below is the android code. public class AESEncryption { private static final String key = "d95acd54c6a821ff32c52825b931c194"; private static final String initVector = "687b9509c25a14b8ad076346d8353d67"; static byte[] bte = hexToBytes(initVector); public static String encrypt(String strToEncrypt) { try { CommonCode.showLog("log", bte.toString()); IvParameterSpec iv = new IvParameterSpec(bte); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); CommonCode.showLog("IV after logs", iv.toString()); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(strToEncrypt.getBytes()); if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.O) { return Base64.getEncoder().encodeToString(encrypted).trim(); } else { return android.util.Base64.encodeToString(encrypted, android.util.Base64.DEFAULT).trim(); } } catch (Exception e) { CommonCode.showLog("Error while encrypting: ", e.toString()); } return null; } public static String decrypt(String strToDecrypt) { try { IvParameterSpec iv = new IvParameterSpec(bte); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); if (android.os.Build.VERSION.SDK_INT &gt;= android.os.Build.VERSION_CODES.O) { return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt))); } else { return new String(cipher.doFinal(android.util.Base64.decode(strToDecrypt, android.util.Base64.DEFAULT))); } } catch (Exception e) { CommonCode.showLog("Error while decrypting: " , e.toString()); } return null; } } How can we mimic the above in Swift? Here in Android they are using static byte[] bte = hexToBytes(initVector); to convert the 32bit IV into 16 bit Bytes Array I Have Tried the same approach on Swift below are the code snippet [Contents.swift](https://developer.apple.com/forums/content/attachment/60fab4f2-1496-4003-9f37-c195de95e94a)
9
0
1.0.0k
Dec ’24
Certificate Signing Requests Explained
I regularly help developers with code signing problems and I find that a lot of those problems stem from a fundamental misunderstanding of how code signing requests work. This post is my attempt at explaining that. Note After posting this I then went on to write TN3161 Inside Code Signing: Certificates, which covers similar ground in a much more comprehensive way. I think this post still has value, but you should definitely start by reading TN3161. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Certificate Signing Requests Explained I see a lot of folks confused by how code signing requests (CSRs) work, and that causes problems down the line. This is my attempt to explain the process and head off those problems. IMPORTANT This post covers the ‘classic’ certificate creation process described in Developer Account Help > Create certificates > Create a certificate signing request. Things work differently if you use Xcode to create your certificates, and very differently if you use cloud-managed certificates. Here’s a basic outline of the CSR process: You run Keychain Access and choose Certificate Assistant > Request a Certificate from a Certificate Authority. You run through the workflow as described in Developer Account Help > Create certificates > Create a certificate signing request. This does two things: It generates a public / private key pair in your keychain. To see these, run Keychain Access and select “login” on the left and Keys at the top. Look for keys whose names match the Common Name you entered in step 2. It prompts you to save a .certSigningRequest file (CSR). This contains a copy of the public key. You upload the CSR file to the developer web site. The developer web site issues you a certificate. In human terms this certificate says “Apple certifies that the subject of this certificate holds the private key that matches the public key embedded in this certificate.” Note The developer web site sets the subject information in the certificate based on your developer account. It ignores the subject information in the CSR. So, you can enter any information you want in step 2. This is a good way to distinguish between different keys in your keychain. For example, you might set the Common Name field in step 2 to include a unique identifier that allows you to easily identify the public / private key pair generated in step 3. You download the certificate and add it to your keychain. At this point your keychain contains a digital identity, that is, a certificate and the private key that matches the public key embedded in that certificate. To see this in Keychain Access, select “login” on the left and My Certificates at the top. What’s This My Certificates Thing? There’s an industry-wide terminology problem here. Folks use the term certificate to mean two different things: A digital identity, that is, a certificate and its matching private key An actual certificate This industry-wide confusion extends into the Apple ecosystem. For example: The Security framework gets this right, drawing a clear distinction between a digital identity (SecIdentity) and a certificate (SecCertificate). Keychain Access uses My Certificates for digital identities. Other user-facing apps use different terms. For example, Apple Configurator uses signing identity (yay for them!). OTOH, the help for Apple Mail uses the term personal certificate. Xcode and its documentation uses the term signing certificate to denote a digital identity that can be used for code signing. This terminological inexactitude causes all sorts of problems. For example, imagine you’re setting up a new Mac. You download your certificate from the developer web site and then wonder why you can’t sign your code. That’s because the developer web site gives you a certificate, not a digital identity. Indeed, the developer web site can’t give you a digital identity because it never got a copy of your private key [1]. [1] Again, we’re talking about the classic certificate creation process here; this statement is not true for cloud-managed certificates. Digital Identity Formation Apple platforms form a digital identity by: Extracting the public key from the certificate. Calculating a SHA-1 digest of that. Looking for a private key whose kSecAttrApplicationLabel attribute matches that SHA-1 hash. For more background on this, see my SecItem attributes for keys post. Note that it’s perfectly valid for multiple certificates to match against the same private key, yielding a digital identity for each certificate. You regularly see this when you renew a certificate. Looking Inside a CSR A CSR is a PEM file (PEM is short for Privacy-Enhanced Mail) with the CERTIFICATE REQUEST label: % cat CertificateSigningRequest.certSigningRequest -----BEGIN CERTIFICATE REQUEST----- MIICgjCCAWoCAQAwPTEcMBoGCSqGSIb3DQEJARYNZnJvZ0Bmcm9nLmNvbTEQMA4G … Ur9x5voYb6CafUBZMMiYw6aFXcgnsx4ZXxe8VEqNCarrQi+9tqitDD/bCuymT5Da 2+t64DGjpVM2lwtwqvH6Qh6QdPjkUw== -----END CERTIFICATE REQUEST----- To see inside, run the openssl tool as shown below: % openssl req -in CertificateSigningRequest.certSigningRequest -text -noout Certificate Request: Data: Version: 0 (0x0) Subject: emailAddress=mrgumby@opendoor.com, CN=Mr Gumby, C=US Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:b1:b4:a0:15:4d:4a:d7:29:1d:ed:d6:b7:c2:7c: … 28:b9:8a:58:a4:04:63:fe:45:b2:4f:db:bd:93:20: 4e:8b Exponent: 65537 (0x10001) Attributes: a0:00 Signature Algorithm: sha256WithRSAEncryption 80:f9:0e:73:8e:42:d8:3c:e3:e0:06:54:13:d7:48:ef:a8:71: … 2f:74:e1:2e:cf:e7:ed:3e:64:b4:78:85:f4:ac:38:07:b1:15: 6b:3c:39:f9 For even more details, convert the file to DER form and then dump that as ASN.1: % openssl req -in CertificateSigningRequest.certSigningRequest -out CertificateSigningRequest.der -outform der % dumpasn1 -p -a CertificateSigningRequest.der SEQUENCE { SEQUENCE { INTEGER 0 SEQUENCE { SET { SEQUENCE { OBJECT IDENTIFIER emailAddress (1 2 840 113549 1 9 1) IA5String 'mrgumby@opendoor.com' } } SET { SEQUENCE { OBJECT IDENTIFIER commonName (2 5 4 3) UTF8String 'Mr Gumby' } } SET { SEQUENCE { OBJECT IDENTIFIER countryName (2 5 4 6) PrintableString 'US' } } } SEQUENCE { SEQUENCE { OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) NULL } BIT STRING, encapsulates { SEQUENCE { INTEGER 00 B1 B4 A0 15 4D 4A D7 29 1D ED D6 B7 C2 7C 74 … 28 B9 8A 58 A4 04 63 FE 45 B2 4F DB BD 93 20 4E 8B INTEGER 65537 } } } [0] Error: Object has zero length. } SEQUENCE { OBJECT IDENTIFIER sha256WithRSAEncryption (1 2 840 113549 1 1 11) NULL } BIT STRING 80 F9 0E 73 8E 42 D8 3C E3 E0 06 54 13 D7 48 EF … ED 3E 64 B4 78 85 F4 AC 38 07 B1 15 6B 3C 39 F9 } I’m using the dumpasn1 tool, available here. To extract the public key from the CSR, run this command: % openssl req -in CertificateSigningRequest.certSigningRequest -noout -pubkey -out public.pem % cat public.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsbSgFU1K1ykd7da3wnx0 … FymGqUEcwfIISlG1C9VXyMRPzDcMrzjHm4i9qI9NliYouYpYpARj/kWyT9u9kyBO iwIDAQAB -----END PUBLIC KEY----- To further explore that key, use the techniques in my On Cryptographic Key Formats post. Revision History 2024-07-23 Updated to include a link to TN3161 Inside Code Signing: Certificates. 2022-11-03 First posted.
0
0
28k
Jul ’24
How to secure the Safari web extension?
I'm tasked with securing a Safari browser extension that my organization created. It is using WebExtensions and has been launched across various browser extension stores, including Safari. I've searched for many hours on this and find very very little information on how to secure it. (Pretty much just https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_Security_Policy.) Is there anything more extensive/comprehensive? I'll be dealing with Safari Desktop as well as Safari iOS. By comparison, when looking for resources for securing the Firefox version of the extension, I found https://extensionworkshop.com/documentation/develop/build-a-secure-extension/, which I think is a great resource. Some of that content applies to Safari, but it's hard to know to what extent. I'll need to secure Safari web and Safari mobile (i.e. iOS), but for now I'm focused on web. (But if you have anything on mobile, then please let me know also.) Thank you!
2
0
706
Oct ’24
Would YOU use ClamXav on an Apple Mac?
Mac users often ask whether they should install "anti-virus" software. The answer usually given on ASC is "no." The answer is right, but it may give the wrong impression that there is no threat from what are loosely called "viruses." There is a threat, and you need to educate yourself about it. This is a comment on what you should—and should not—do to protect yourself from malicious software ("malware") that circulates on the Internet and gets onto a computer as an unintended consequence of the user's actions. It does not apply to software, such as keystroke loggers, that may be installed deliberately by an intruder who has hands-on access to the computer, or who has been able to log in to it remotely. That threat is in a different category, and there's no easy way to defend against it. The comment is long because the issue is complex. The key points are in sections 5, 6, and 10. OS X now implements three layers of built-in protection specifically against malware, not counting runtime protections such as execute disable, sandboxing, system library randomization, and address space layout randomization that may also guard against other kinds of exploits. 2. All versions of OS X since 10.6.7 have been able to detect known Mac malware in downloaded files, and to block insecure web plugins. This feature is transparent to the user. Internally Apple calls it "XProtect." The malware recognition database used by XProtect is automatically updated; however, you shouldn't rely on it, because the attackers are always at least a day ahead of the defenders. The following caveats apply to XProtect: ☞ It can be bypassed by some third-party networking software, such as BitTorrent clients and Java applets. ☞ It only applies to software downloaded from the network. Software installed from a CD or other media is not checked. As new versions of OS X are released, it's not clear whether Apple will indefinitely continue to maintain the XProtect database of older versions such as 10.6. The security of obsolete system versions may eventually be degraded. Security updates to the code of obsolete systems will stop being released at some point, and that may leave them open to other kinds of attack besides malware. 3. Starting with OS X 10.7.5, there has been a second layer of built-in malware protection, designated "Gatekeeper" by Apple. By default, applications and Installer packages downloaded from the network will only run if they're digitally signed by a developer with a certificate issued by Apple. Software certified in this way hasn't necessarily been tested by Apple, but you can be reasonably sure that it hasn't been modified by anyone other than the developer. His identity is known to Apple, so he could be held legally responsible if he distributed malware. That may not mean much if the developer lives in a country with a weak legal system (see below.) Gatekeeper doesn't depend on a database of known malware. It has, however, the same limitations as XProtect, and in addition the following: ☞ It can easily be disabled or overridden by the user. ☞ A malware attacker could get control of a code-signing certificate under false pretenses, or could simply ignore the consequences of distributing codesigned malware. ☞ An App Store developer could find a way to bypass Apple's oversight, or the oversight could fail due to human error. Apple has so far failed to revoke the codesigning certificates of some known abusers, thereby diluting the value of Gatekeeper and the Developer ID program. These failures don't involve App Store products, however. For the reasons given, App Store products, and—to a lesser extent—other applications recognized by Gatekeeper as signed, are safer than others, but they can't be considered absolutely safe. "Sandboxed" applications may prompt for access to private data, such as your contacts, or for access to the network. Think before granting that access. Sandbox security is based on user input. Never click through any request for authorization without thinking. 4. Starting with OS X 10.8.3, a third layer of protection has been added: a "Malware Removal Tool" (MRT). MRT runs automatically in the background when you update the OS. It checks for, and removes, malware that may have evaded the other protections via a Java exploit (see below.) MRT also runs when you install or update the Apple-supplied Java runtime (but not the Oracle runtime.) Like XProtect, MRT is effective against known threats, but not against unknown ones. It notifies you if it finds malware, but otherwise there's no user interface to MRT. 5. The built-in security features of OS X reduce the risk of malware attack, but they are not, and never will be, complete protection. Malware is a problem of human behavior, and a technological fix is not going to solve it. Trusting software to protect you will only make you more vulnerable. The best defense is always going to be your own intelligence. With the possible exception of Java exploits, all known malware circulating on the Internet that affects a fully-updated installation of OS X 10.6 or later takes the form of so-called "****** horses," which can only have an effect if the victim is duped into running them. The threat therefore amounts to a battle of wits between you and the scam artists. If you're smarter than they think you are, you'll win. That means, in practice, that you always stay within a safe harbor of computing practices. Malware defence By Linc Davis - https://discussions.apple.com/thread/6460085
7
1
3.8k
Feb ’25
SecItem: Pitfalls and Best Practices
I regularly help developers with keychain problems, both here on DevForums and for my Day Job™ in DTS. Over the years I’ve learnt a lot about the API, including many pitfalls and best practices. This post is my attempt to collect that experience in one place. If you have questions or comments about any of this, put them in a new thread and apply the Security tag so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" SecItem: Pitfalls and Best Practices It’s just four functions, how hard can it be? The SecItem API seems very simple. After all, it only has four function calls, how hard can it be? In reality, things are not that easy. Various factors contribute to making this API much trickier than it might seem at first glance. This post explains some of the keychain’s pitfalls and then goes on to explain various best practices. Before reading this, make sure you understand the fundamentals by reading its companion post, SecItem: Fundamentals. Pitfalls Lets start with some common pitfalls. Queries and Uniqueness Constraints The relationship between query dictionaries and uniqueness constraints is a major source of problems with the keychain API. Consider code like this: var copyResult: CFTypeRef? = nil let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecAttrGeneric: Data("SecItemHints".utf8), ] as NSMutableDictionary let err = SecItemCopyMatching(query, &copyResult) if err == errSecItemNotFound { query[kSecValueData] = Data("opendoor".utf8) let err2 = SecItemAdd(query, nil) if err2 == errSecDuplicateItem { fatalError("… can you get here? …") } } Can you get to the fatal error? At first glance this might not seem possible because you’ve run your query and it’s returned errSecItemNotFound. However, the fatal error is possible because the query contains an attribute, kSecAttrGeneric, that does not contribute to the uniqueness. If the keychain contains a generic password whose service (kSecAttrService) and account (kSecAttrAccount) attributes match those supplied but whose generic (kSecAttrGeneric) attribute does not, the SecItemCopyMatching calls will return errSecItemNotFound. However, for a generic password item, of the attributes shown here, only the service and account attributes are included in the uniqueness constraint. If you try to add an item where those attributes match an existing item, the add will fail with errSecDuplicateItem even though the value of the generic attribute is different. The take-home point is that that you should study the attributes that contribute to uniqueness and use them in a way that’s aligned with your view of uniqueness. See the Uniqueness section of SecItem: Fundamentals for a link to the relevant documentation. Erroneous Attributes Each keychain item class supports its own specific set of attributes. For information about the attributes supported by a given class, see SecItem: Fundamentals. I regularly see folks use attributes that aren’t supported by the class they’re working with. For example, the kSecAttrApplicationTag attribute is only supported for key items (kSecClassKey). Using it with a certificate item (kSecClassCertificate) will cause, at best, a runtime error and, at worst, mysterious bugs. This is an easy mistake to make because: The ‘parameter block’ nature of the SecItem API means that the compiler won’t complain if you use an erroneous attribute. On macOS, the shim that connects to the file-based keychain ignores unsupported attributes. Imagine you want to store a certificate for a particular user. You might write code like this: let err = SecItemAdd([ kSecClass: kSecClassCertificate, kSecAttrApplicationTag: Data(name.utf8), kSecValueRef: cert, ] as NSDictionary, nil) The goal is to store the user’s name in the kSecAttrApplicationTag attribute so that you can get back their certificate with code like this: let err = SecItemCopyMatching([ kSecClass: kSecClassCertificate, kSecAttrApplicationTag: Data(name.utf8), kSecReturnRef: true, ] as NSDictionary, &copyResult) On iOS, and with the data protection keychain on macOS, both calls will fail with errSecNoSuchAttr. That makes sense, because the kSecAttrApplicationTag attribute is not supported for certificate items. Unfortunately, the macOS shim that connects the SecItem API to the file-based keychain ignores extraneous attributes. This results in some very bad behaviour: SecItemAdd works, ignoring kSecAttrApplicationTag. SecItemCopyMatching ignores kSecAttrApplicationTag, returning the first certificate that it finds. If you only test with a single user, everything seems to work. But, later on, when you try your code with multiple users, you might get back the wrong result depending on the which certificate the SecItemCopyMatching call happens to discover first. Ouch! Context Matters Some properties change behaviour based on the context. The value type properties are the biggest offender here, as discussed in the Value Type Subtleties section of SecItem: Fundamentals. However, there are others. The one that’s bitten me is kSecMatchLimit: In a query and return dictionary its default value is kSecMatchLimitOne. If you don’t supply a value for kSecMatchLimit, SecItemCopyMatching returns at most one item that matches your query. In a pure query dictionary its default value is kSecMatchLimitAll. For example, if you don’t supply a value for kSecMatchLimit, SecItemDelete will delete all items that match your query. This is a lesson that, once learnt, is never forgotten! Note Although this only applies to the data protection keychain. If you’re on macOS and targeting the file-based keychain, kSecMatchLimit always defaults to kSecMatchLimitOne (r. 105800863). Fun times! Digital Identities Aren’t Real A digital identity is the combination of a certificate and the private key that matches the public key within that certificate. The SecItem API has a digital identity keychain item class, namely kSecClassIdentity. However, the keychain does not store digital identities. When you add a digital identity to the keychain, the system stores its components, the certificate and the private key, separately, using kSecClassCertificate and kSecClassKey respectively. This has a number of non-obvious effects: Adding a certificate can ‘add’ a digital identity. If the new certificate happens to match a private key that’s already in the keychain, the keychain treats that pair as a digital identity. Likewise when you add a private key. Similarly, removing a certificate or private key can ‘remove’ a digital identity. Adding a digital identity will either add a private key, or a certificate, or both, depending on what’s already in the keychain. Removing a digital identity removes its certificate. It might also remove the private key, depending on whether that private key is used by a different digital identity. The system forms a digital identity by matching the kSecAttrApplicationLabel (klbl) attribute of the private key with the kSecAttrPublicKeyHash (pkhh) attribute of the certificate. If you add both items to the keychain and the system doesn’t form an identity, check the value of these attributes. For more information the key attributes, see SecItem attributes for keys. Keys Aren’t Stored in the Secure Enclave Apple platforms let you protect a key with the Secure Enclave (SE). The key is then hardware bound. It can only be used by that specific SE [1]. Earlier versions of the Protecting keys with the Secure Enclave article implied that SE-protected keys were stored in the SE itself. This is not true, and it’s caused a lot of confusion. For example, I once asked the keychain team “How much space does the SE have available to store keys?”, a question that’s complete nonsense once you understand how this works. In reality, SE-protected keys are stored in the standard keychain database alongside all your other keychain items. The difference is that the key is wrapped in such a way that only the SE can use it. So, the key is protected by the SE, not stored in the SE. A while back we updated the docs to clarify this point but the confusion persists. [1] Technically it’s that specific iteration of that specific SE. If you erase the device then the key material needed to use the key is erased and so the key becomes permanently useless. This is the sort of thing you’ll find explained in Apple Platform Security. Careful With that Shim, Mac Developer As explained in TN3137 On Mac keychain APIs and implementations, macOS has a shim that connects the SecItem API to either the data protection keychain or the file-based keychain depending on the nature of the request. That shim has limitations. Some of those are architectural but others are simply bugs in the shim. For some great examples, see the Investigating Complex Attributes section below. The best way to avoid problems like this is to target the data protection keychain. If you can’t do that, try to avoid exploring the outer reaches of the SecItem API. If you encounter a case that doesn’t make sense, try that same case with the data protection keychain. If it works there but fails with the file-based keychain, please do file a bug against the shim. It’ll be in good company. Here’s some known issues with the shim: It ignores unsupported attributes. See Erroneous Attributes, above, for more background on that. The shim can fan out to both the data protection and the file-based keychain. In that case it has to make a policy decision about how to handle errors. This results in some unexpected behaviour (r. 143405965). For example, if you call SecItemCopyMatching while the keychain is locked, the data protection keychain will fail with errSecInteractionNotAllowed (-25308). OTOH, it’s possible to query for the presence of items in the file-based keychain even when it’s locked. If you do that and there’s no matching item, the file-based keychain fails with errSecItemNotFound (-25300). When the shim gets these conflicting errors, it chooses to return the latter. Whether this is right or wrong depends on your perspective, but it’s certainly confusing, especially if you’re coming at this from the iOS side. If you call SecItemDelete without specifying a match limit (kSecMatchLimit), the data protection keychain deletes all matching items, whereas the file-based keychain just deletes a single match (r. 105800863). While these issue have all have bug numbers, there’s no guarantee that any of them will be fixed. Fixing bugs like this is tricky because of binary compatibility concerns. Add-only Attributes Some attributes can only be set when you add an item. These attributes are usually associated with the scope of the item. For example, to protect an item with the Secure Enclave, supply the kSecAttrAccessControl attribute to the SecItemAdd call. Once you do that, however, you can’t change the attribute. Calling SecItemUpdate with a new kSecAttrAccessControl won’t work. Lost Keychain Items A common complaint from developers is that a seemingly minor update to their app has caused it to lose all of its keychain items. Usually this is caused by one of two problems: Entitlement changes Query dictionary confusion Access to keychain items is mediated by various entitlements, as described in Sharing access to keychain items among a collection of apps. If the two versions of your app have different entitlements, one version may not be able to ‘see’ items created by the other. Imagine you have an app with an App ID of SKMME9E2Y8.com.example.waffle-varnisher. Version 1 of your app is signed with the keychain-access-groups entitlement set to [ SKMME9E2Y8.groupA, SKMME9E2Y8.groupB ]. That makes its keychain access group list [ SKMME9E2Y8.groupA, SKMME9E2Y8.groupB, SKMME9E2Y8.com.example.waffle-varnisher ]. If this app creates a new keychain item without specifying kSecAttrAccessGroup, the system places the item into SKMME9E2Y8.groupA. If version 2 of your app removes SKMME9E2Y8.groupA from the keychain-access-groups, it’ll no longer be able to see the keychain items created by version 1. You’ll also see this problem if you change your App ID prefix, as described in App ID Prefix Change and Keychain Access. IMPORTANT When checking for this problem, don’t rely on your .entitlements file. There are many steps between it and your app’s actual entitlements. Rather, run codesign to dump the entitlements of your built app: % codesign -d --entitlements - /path/to/your.app Lost Keychain Items, Redux Another common cause of lost keychain items is confusion about query dictionaries, something discussed in detail in this post and SecItem: Fundamentals. If SecItemCopyMatching isn’t returning the expected item, add some test code to get all the items and their attributes. For example, to dump all the generic password items, run code like this: func dumpGenericPasswords() throws { let itemDicts = try secCall { SecItemCopyMatching([ kSecClass: kSecClassGenericPassword, kSecMatchLimit: kSecMatchLimitAll, kSecReturnAttributes: true, ] as NSDictionary, $0) } as! [[String: Any]] print(itemDicts) } Then compare each item’s attributes against the attributes you’re looking for to see why there was no match. Data Protection and Background Execution Keychain items are subject to data protection. Specifically, an item may or may not be accessible depending on whether specific key material is available. For an in-depth discussion of how this works, see Apple Platform Security. Note This section focuses on iOS but you’ll see similar effects on all Apple platforms. On macOS specifically, the contents of this section only apply to the data protection keychain. The keychain supports three data protection levels: kSecAttrAccessibleWhenUnlocked kSecAttrAccessibleAfterFirstUnlock kSecAttrAccessibleAlways Note There are additional data protection levels, all with the ThisDeviceOnly suffix. Understanding those is not necessary to understanding this pitfall. Each data protection level describes the lifetime of the key material needed to work with items protected in that way. Specifically: The key material needed to work with a kSecAttrAccessibleWhenUnlocked item comes and goes as the user locks and unlocks their device. The key material needed to work with a kSecAttrAccessibleAfterFirstUnlock item becomes available when the device is first unlocked and remains available until the device restarts. The default data protection level is kSecAttrAccessibleWhenUnlocked. If you add an item to the keychain and don’t specify a data protection level, this is what you get [1]. To specify a data protection level when you add an item to the keychain, apply the kSecAttrAccessible attribute. Alternatively, embed the access level within a SecAccessControl object and apply that using the kSecAttrAccessControl attribute. IMPORTANT It’s best practice to set these attributes when you add the item and then never update them. See Add-only Attributes, above, for more on that. If you perform an operation whose data protection is incompatible with the currently available key material, that operation fails with errSecInteractionNotAllowed [2]. There are four fundamental keychain operations, discussed in the SecItem: Fundamentals, and each interacts with data protection in a different way: Copy — If you attempt to access a keychain item whose key material is unavailable, SecItemCopyMatching fails with errSecInteractionNotAllowed. This is an obvious result; the whole point of data protection is to enforce this security policy. Add — If you attempt to add a keychain item whose key material is unavailable, SecItemAdd fails with errSecInteractionNotAllowed. This is less obvious. The reason why this fails is that the system needs the key material to protect (by encryption) the keychain item, and it can’t do that if if that key material isn’t available. Update — If you attempt to update a keychain item whose key material is unavailable, SecItemUpdate fails with errSecInteractionNotAllowed. This result is an obvious consequence of the previous result. Delete — Deleting a keychain item, using SecItemDelete, doesn’t require its key material, and thus a delete will succeed when the item is otherwise unavailable. That last point is a significant pitfall. I regularly see keychain code like this: Read an item holding a critical user credential. If that works, use that credential. If it fails, delete the item and start from a ‘factory reset’ state. The problem is that, if your code ends up running in the background unexpectedly, step 1 fails with errSecInteractionNotAllowed and you turn around and delete the user’s credential. Ouch! Note Even if you didn’t write this code, you might have inherited it from a keychain wrapper library. See *Think Before Wrapping, below. There are two paths forward here: If you don’t expect this code to work in the background, check for the errSecInteractionNotAllowed error and non-destructively cancel the operation in that case. If you expect this code to be running in the background, switch to a different data protection level. WARNING For the second path, the most obvious fix is to move from kSecAttrAccessibleWhenUnlocked to kSecAttrAccessibleAfterFirstUnlock. However, this is not a panacea. It’s possible that your app might end up running before first unlock [3]. So, if you choose the second path, you must also make sure to follow the advice for the first path. You can determine whether the device is unlocked using the isProtectedDataAvailable property and its associated notifications. However, it’s best not to use this property as part of your core code, because such preflighting is fundamentally racy. Rather, perform the operation and handle the error gracefully. It might make sense to use isProtectedDataAvailable property as part of debugging, logging, and diagnostic code. [1] For file data protection there’s an entitlement (com.apple.developer.default-data-protection) that controls the default data protection level. There’s no such entitlement for the keychain. That’s actually a good thing! In my experience the file data protection entitlement is an ongoing source of grief. See this thread if you’re curious. [2] This might seem like an odd error but it’s actually pretty reasonable: The operation needs some key material that’s currently unavailable. Only a user action can provide that key material. But the data protection keychain will never prompt the user to unlock their device. Thus you get an error instead. [3] iOS generally avoids running third-party code before first unlock, but there are circumstances where that can happen. The obvious legitimate example of this is a VoIP app, where the user expects their phone to ring even if they haven’t unlocked it since the last restart. There are also other less legitimate examples of this, including historical bugs that caused apps to launch in the background before first unlock. Best Practices With the pitfalls out of the way, let’s talk about best practices. Less Painful Dictionaries I look at a lot of keychain code and it’s amazing how much of it is way more painful than it needs to be. The biggest offender here is the dictionaries. Here are two tips to minimise the pain. First, don’t use CFDictionary. It’s seriously ugly. While the SecItem API is defined in terms of CFDictionary, you don’t have to work with CFDictionary directly. Rather, use NSDictionary and take advantage of the toll-free bridge. For example, consider this CFDictionary code: CFTypeRef keys[4] = { kSecClass, kSecAttrService, kSecMatchLimit, kSecReturnAttributes, }; static const int kTen = 10; CFNumberRef ten = CFNumberCreate(NULL, kCFNumberIntType, &kTen); CFAutorelease(ten); CFTypeRef values[4] = { kSecClassGenericPassword, CFSTR("AYS"), ten, kCFBooleanTrue, }; CFDictionaryRef query = CFDictionaryCreate( NULL, keys, values, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); Note This might seem rather extreme but I’ve literally seen code like this, and worse, while helping developers. Contrast this to the equivalent NSDictionary code: NSDictionary * query = @{ (__bridge NSString *) kSecClass: (__bridge NSString *) kSecClassGenericPassword, (__bridge NSString *) kSecAttrService: @"AYS", (__bridge NSString *) kSecMatchLimit: @10, (__bridge NSString *) kSecReturnAttributes: @YES, }; Wow, that’s so much better. Second, if you’re working in Swift, take advantage of its awesome ability to create NSDictionary values from Swift dictionary literals. Here’s the equivalent code in Swift: let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecMatchLimit: 10, kSecReturnAttributes: true, ] as NSDictionary Nice! Avoid Reusing Dictionaries I regularly see folks reuse dictionaries for different SecItem calls. For example, they might have code like this: var copyResult: CFTypeRef? = nil let dict = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecReturnData: true, ] as NSMutableDictionary var err = SecItemCopyMatching(dict, &copyResult) if err == errSecItemNotFound { dict[kSecValueData] = Data("opendoor".utf8) err = SecItemAdd(dict, nil) } This specific example will work, but it’s easy to spot the logic error. kSecReturnData is a return type property and it makes no sense to pass it to a SecItemAdd call whose second parameter is nil. I’m not sure why folks do this. I think it’s because they think that constructing dictionaries is expensive. Regardless, this pattern can lead to all sorts of weird problems. For example, it’s the leading cause of the issue described in the Queries and the Uniqueness Constraints section, above. My advice is that you use a new dictionary for each call. That prevents state from one call accidentally leaking into a subsequent call. For example, I’d rewrite the above as: var copyResult: CFTypeRef? = nil let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecReturnData: true, ] as NSMutableDictionary var err = SecItemCopyMatching(query, &copyResult) if err == errSecItemNotFound { let add = [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", kSecValueData: Data("opendoor".utf8), ] as NSMutableDictionary err = SecItemAdd(add, nil) } It’s a bit longer, but it’s much easier to track the flow. And if you want to eliminate the repetition, use a helper function: func makeDict() -> NSMutableDictionary { [ kSecClass: kSecClassGenericPassword, kSecAttrService: "AYS", kSecAttrAccount: "mrgumby", ] as NSMutableDictionary } var copyResult: CFTypeRef? = nil let query = makeDict() query[kSecReturnData] = true var err = SecItemCopyMatching(query, &copyResult) if err == errSecItemNotFound { let add = makeDict() query[kSecValueData] = Data("opendoor".utf8) err = SecItemAdd(add, nil) } Think Before Wrapping A lot of folks look at the SecItem API and immediately reach for a wrapper library. A keychain wrapper library might seem like a good idea but there are some serious downsides: It adds another dependency to your project. Different subsystems within your project may use different wrappers. The wrapper can obscure the underlying API. Indeed, its entire raison d’être is to obscure the underlying API. This is problematic if things go wrong. I regularly talk to folks with hard-to-debug keychain problems and the conversation goes something like this: Quinn: What attributes do you use in the query dictionary? J R Developer: What’s a query dictionary? Quinn: OK, so what error are you getting back? J R Developer: It throws WrapperKeychainFailedError. That’s not helpful )-: If you do use a wrapper, make sure it has diagnostic support that includes the values passed to and from the SecItem API. Also make sure that, when it fails, it returns an error that includes the underlying keychain error code. These benefits will be particularly useful if you encounter a keychain problem that only shows up in the field. Wrappers must choose whether to be general or specific. A general wrapper may be harder to understand than the equivalent SecItem calls, and it’ll certainly contain a lot of complex code. On the other hand, a specific wrapper may have a model of the keychain that doesn’t align with your requirements. I recommend that you think twice before using a keychain wrapper. Personally I find the SecItem API relatively easy to call, assuming that: I use the techniques shown in Less Painful Dictionaries, above, to avoid having to deal with CFDictionary. I use my secCall(…) helpers to simplify error handling. For the code, see Calling Security Framework from Swift. If you’re not prepared to take the SecItem API neat, consider writing your own wrapper, one that’s tightly focused on the requirements of your project. For example, in my VPN apps I use the wrapper from this post, which does exactly what I need in about 100 lines of code. Prefer to Update Of the four SecItem functions, SecItemUpdate is the most neglected. Rather than calling SecItemUpdate I regularly see folks delete and then re-add the item. This is a shame because SecItemUpdate has some important benefits: It preserves persistent references. If you delete and then re-add the item, you get a new item with a new persistent reference. It’s well aligned with the fundamental database nature of the keychain. It forces you to think about which attributes uniquely identify your item and which items can be updated without changing the item’s identity. Understand These Key Attributes Key items have a number of attributes that are similarly named, and it’s important to keep them straight. I created a cheat sheet for this, namely, SecItem attributes for keys. You wouldn’t believe how often I consult this! Investigating Complex Attributes Some attributes have values where the format is not obvious. For example, the kSecAttrIssuer attributed is documented as: The corresponding value is of type CFData and contains the X.500 issuer name of a certificate. What exactly does that mean? If I want to search the keychain for all certificates issued by a specific certificate authority, what value should I supply? One way to figure this out is to add a certificate to the keychain, read the attributes back, and then dump the kSecAttrIssuer value. For example: let cert: SecCertificate = … let attrs = try secCall { SecItemAdd([ kSecValueRef: cert, kSecReturnAttributes: true, ] as NSDictionary, $0) } as! [String: Any] let issuer = attrs[kSecAttrIssuer as String] as! NSData print((issuer as NSData).debugDescription) // prints: <3110300e 06035504 030c074d 6f757365 4341310b 30090603 55040613 024742> Those bytes represent the contents of a X.509 Name ASN.1 structure with DER encoding. This is without the outer SEQUENCE element, so if you dump it as ASN.1 you’ll get a nice dump of the first SET and then a warning about extra stuff at the end of the file: % xxd issuer.asn1 00000000: 3110 300e 0603 5504 030c 074d 6f75 7365 1.0...U....Mouse 00000010: 4341 310b 3009 0603 5504 0613 0247 42 CA1.0...U....GB % dumpasn1 -p issuer.asn1 SET { SEQUENCE { OBJECT IDENTIFIER commonName (2 5 4 3) UTF8String 'MouseCA' } } Warning: Further data follows ASN.1 data at position 18. Note For details on the Name structure, see section 4.1.2.4 of RFC 5280. Amusingly, if you run the same test against the file-based keychain you’ll… crash. OK, that’s not amusing. It turns out that the code above doesn’t work when targeting the file-based keychain because SecItemAdd doesn’t return a dictionary but rather an array of dictionaries (r. 21111543). Once you get past that, however, you’ll see it print: <301f3110 300e0603 5504030c 074d6f75 73654341 310b3009 06035504 06130247 42> Which is different! Dumping it as ASN.1 shows that it’s the full Name structure, including the outer SEQUENCE element: % xxd issuer-file-based.asn1 00000000: 301f 3110 300e 0603 5504 030c 074d 6f75 0.1.0...U....Mou 00000010: 7365 4341 310b 3009 0603 5504 0613 0247 seCA1.0...U....G 00000020: 42 B % dumpasn1 -p issuer-file-based.asn1 SEQUENCE { SET { SEQUENCE { OBJECT IDENTIFIER commonName (2 5 4 3) UTF8String 'MouseCA' } } SET { SEQUENCE { OBJECT IDENTIFIER countryName (2 5 4 6) PrintableString 'GB' } } } This difference in behaviour between the data protection and file-based keychains is a known bug (r. 26391756) but in this case it’s handy because the file-based keychain behaviour makes it easier to understand the data protection keychain behaviour. Import, Then Add It’s possible to import data directly into the keychain. For example, you might use this code to add a certificate: let certData: Data = … try secCall { SecItemAdd([ kSecClass: kSecClassCertificate, kSecValueData: certData, ] as NSDictionary, nil) } However, it’s better to import the data and then add the resulting credential reference. For example: let certData: Data = … let cert = try secCall { SecCertificateCreateWithData(nil, certData as NSData) } try secCall { SecItemAdd([ kSecValueRef: cert, ] as NSDictionary, nil) } There are two advantages to this: If you get an error, you know whether the problem was with the import step or the add step. It ensures that the resulting keychain item has the correct attributes. This is especially important for keys. These can be packaged in a wide range of formats, so it’s vital to know whether you’re interpreting the key data correctly. I see a lot of code that adds key data directly to the keychain. That’s understandable because, back in the day, this was the only way to import a key on iOS. Fortunately, that’s not been the case since the introduction of SecKeyCreateWithData in iOS 10 and aligned releases. For more information about importing keys, see Importing Cryptographic Keys. App Groups on the Mac Sharing access to keychain items among a collection of apps explains that three entitlements determine your keychain access: keychain-access-groups application-identifier (com.apple.application-identifier on macOS) com.apple.security.application-groups In the discussion of com.apple.security.application-groups it says: Starting in iOS 8, the array of strings given by this entitlement also extends the list of keychain access groups. That’s true, but it’s also potentially misleading. This affordance only works on iOS and its child platforms. It doesn’t work on macOS. That’s because app groups work very differently on macOS than they do on iOS. For all the details, see App Groups: macOS vs iOS: Working Towards Harmony. However, the take-home point is that, when you use the data protection keychain on macOS, your keychain access group list is built from keychain-access-groups and com.apple.application-identifier. Revision History 2025-06-29 Added the Data Protection and Background Execution section. Made other minor editorial changes. 2025-02-03 Added another specific example to the Careful With that Shim, Mac Developer section. 2025-01-29 Added somes specific examples to the Careful With that Shim, Mac Developer section. 2025-01-23 Added the Import, Then Add section. 2024-08-29 Added a discussion of identity formation to the Digital Identities Aren’t Real section. 2024-04-11 Added the App Groups on the Mac section. 2023-10-25 Added the Lost Keychain Items and Lost Keychain Items, Redux sections. 2023-09-22 Made minor editorial changes. 2023-09-12 Fixed various bugs in the revision history. Added the Erroneous Attributes section. 2023-02-22 Fixed the link to the VPNKeychain post. Corrected the name of the Context Matters section. Added the Investigating Complex Attributes section. 2023-01-28 First posted.
0
0
3.4k
6d
Unable to retrieve keychain data intermittently.
At present, we have been receiving numerous reports from customers who integrate our SDK who have been encountering the failures (errSecItemNotFound) while trying to retrieve a key using SecItemCopyMatching. We are raising this query we are still in the midst of properly reproducing this issue though it has been reported to occur in several devices during the OS upgrade to the iOS 17 betas. This issue is still occurring in the latest iOS 17 beta 7. This issue was not present in previous iOS version. At present, we are of the conclusion that this issue is occurring randomly amongst devices that upgraded to the iOS 17 betas and it is not limited to older devices. What we believe is occurring is that: A key is created and stored into Keychain using SecItemAdd. The same key is queried at a later timepoint but encounters the error errSecItemNotFound. Our SDK then attempts to regenerate a new key for the same label and attribute to store it using SecItemAdd, but the system then reports errSecDuplicateItem at the key already exists. The workaround here includes a manual deletion of the said key. This issue seems to occur only during an OS upgrade to the iOS17 betas with the likelihood that the key was already present in Keychain prior to the upgrade. I share below the snippet relating to how this said key is generated, stored and retrieved. // Initial key is added // A random data of 32 bytes length is generated CFDataRef dataRef = <32bytes of data>; *attr = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); BREAK_IF_NULL(*attr); CFDictionarySetValue(*attr, kSecClass, kSecClassGenericPassword); CFStringRef aKey = CFStringCreateWithCString(kCFAllocatorDefault, "KEY_ACCOUNT", kCFStringEncodingUTF8); CFDictionaryAddValue(*attr, kSecAttrAccount, aKey); CFDictionarySetValue(*attr, kSecReturnData, kCFBooleanFalse); CFDictionarySetValue(*attr, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock); label = CFStringCreateWithFormat(NULL, NULL, CFSTR("A_LABEL")); CFDictionarySetValue(attr, kSecAttrService, label); CFDictionarySetValue(attr, kSecValueData, dataRef); SecItemAdd(attr); // Query for retrieval of key label = CFStringCreateWithFormat(NULL, NULL, CFSTR("A_LABEL")); CFDictionarySetValue(attributes, kSecAttrService, label); CFDictionarySetValue(attributes, kSecReturnData, kCFBooleanTrue); CFDictionarySetValue(attributes, kSecMatchLimit, limit); CFDictionarySetValue(attributes, kSecReturnAttributes, returnAttributes); osStatus = SecItemCopyMatching(attributes, result); if (errSecItemNotFound == osStatus) { } Please do let me know if more information could be useful. At present, we have ensured that the key generated are well-within the size limits and is stored simply as a kSecClassGenericPassword with limited access control to the key. Additionally, the query used was intended to be generalised to avoid encountering such occurrences.
18
6
6.1k
Sep ’24
How to fix: IPA BINARY CODE ANALYSIS in iOS swift
I received the MOBSF security check result PDF, where I found some "High" severity issues. 1.Binary makes use of insecure API(s) with high CWE: CWE-676: Use of Potentially Dangerous Function OWASP Top 10: M7: Client Code Quality OWASP MASVS: MSTG-CODE-8. The binary may contain the following insecure API(s): _sscanf, _memcpy, _fopen. 2.Binary makes use of the malloc function with high CWE: CWE-789: Uncontrolled Memory Allocation OWASP Top 10: M7: Client Code Quality OWASP MASVS: MSTG-CODE-8. The binary may use the _malloc function instead of calloc. I have utilized a static analyzer, but I am unable to identify the APIs such as _sscanf and others in my codebase. This issue is not being shown in Xcode IDE either. I have attempted static analysis in Xcode using the approach: Product -> Analyze, but I am still unable to identify the mentioned issues. Can anyone please help me overcome this scenario and successfully pass the MOBSF test? Thanks in Advance
1
0
3.1k
Jul ’24
Write in /System folder on macOS Sonoma
Hello, I know this is not a good practice but i want to make a test. I would like to write a file into /System folder on macOS Sonoma. I have tried to reboot in recovery mode. I have disabled SIP. But i can't write into /System. This folder is mounted as read only. How can i write into this folder ? I know there is a kind of checksum mechanism to check if something has been modified in /System folder and i want to see what happens if this checksum does not match. Thanks
3
1
2.5k
Jul ’24
Secure Enclave security
Hello! the other day I had troubles with running the application to interact with the Secure Enclave. (https://developer.apple.com/forums/thread/748611?page=1#783968022) While my program is running perfectly fine now, I still have questions regarding its security. QUESTIONS: Is there any functionality just with the public key to get an evidence of a corresponding private key to be protected by the Secure Enclave without showing the source code? Even with the most recent update of iOS 17.4, there is still no way to directly access the functionality of a Secure Element itself, is that right? So far I found a function SecureElementPass, and it seems like it’s the only interaction possible. What is the difference between using Security API and Apple CryptoKit? I heard some were saying it the matter of habit and device support, but I still would like to hear an opinion of a professional. Any information regarding that will be helpful. Thank you in advance for your time and effort!
3
0
1.4k
Aug ’24
One FaceID for multiple operations in a short while
Hi, Is this possible? I would like to: Store a biometrically secured key in the Secure Enclave. Do multiple cryptographic operations using that key in a short period of time (say 5 seconds), not all at once. Only do one FaceID for that set. For the time I've only gotten either multiple flashing FaceId requests or the operations failing. Is it possible to set a time limit in which the first FaceID authentication is accepted? Should I do something else? Thanks!
4
0
1.2k
Sep ’24
macOS rejects certificate with non-ciritical unknown extension
In our macOS daemon process, we are trying to validate a leaf certificate by anchoring intermediate CA cert and evaluating it using SecTrustEvaluateWithError. The leaf certificate contains couple of non-critical MS extensions (1.3.6.1.4.1.311.21.10 and 1.3.6.1.4.1.311.21.7). The macOS API fails to parse these extensions and does not evaluate the cert chain. Below is the error returned: { NSLocalizedDescription = "\U201abc\U201d certificate is not standards compliant"; NSUnderlyingError = "Error Domain=NSOSStatusErrorDomain Code=-67618 \"Certificate 0 \U201abc\U201d has errors: Unable to parse known extension;\" UserInfo={NSLocalizedDescription=Certificate 0 \U201abc\U201d has errors: Unable to parse known extension;}"; } As per RFC2459, a non-critical extension can be ignored by the system: A certificate using system MUST reject the certificate if it encounters a critical extension it does not recognize; however, a non-critical extension may be ignored if it is not recognized. So, why does macOS not ignore these non-critical extension and returns a failure? OS version is 14.4.1.
4
0
1k
Jul ’24
Should embedded XPCServices validate incoming connections?
Hello, The man page for xpcservices.plist states that: Services embedded in an application bundle will only be visible to the containing application (...) What exactly "visible" means? Doest that mean that there is a mechanism to prevent other programs than the embedding application to access the XPCService's mach port or it just means that the XPCService is not listed (e.g. launchctl list) and if attacker can guess the mach port they can access it? I'm asking to understand if there is a security gain using the -[NSXPCConnection setCodeSigningRequirement:] for embedded XPCServices.
4
0
1.2k
Jul ’24
App Transport Security (ATS) blocking https request to valid server, only in production build testflight
Hi, We are developing react native app, and we are having issue with ATS policy in production build distributed to TestFlight internal testing, requests to https are being killed. The preview build ad-hoc distribution is working fine. (I am testing the app on physical device) I will described what I've tried and supply you with logs from different tools. I tried to disable ATS - requests are working enable ATS (no change to default config) - requests are failing with following error Task <55618987-64A8-4C04-9B00-2EFF074D796C>.<1> finished with error [-1022] Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection., NSErrorFailingURLStringKey=<private>, NSErrorFailingURLKey=<private>, _NSURLErrorRelatedURLSessionTaskErrorKey=<private>, _NSURLErrorFailingURLSessionTaskErrorKey=<private>, NSUnderlyingError=0x30127bb10 {Error Domain=kCFErrorDomainCFNetwork Code=-1022}} I tried to check server if it had met ATS requirements and ran ats-diagnostic ./TLSTool s_client -connect api.rankacy.com:443 * input stream did open * output stream did open * output stream has space * protocol: TLS 1.2 * cipher: ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 * trust result: unspecified * certificate info: * 0 + ecPublicKey 256 ecdsa-with-SHA384 'api.rankacy.com' * 1 + ecPublicKey 384 sha256-with-rsa-signature 'E6' * 2 + rsaEncryption 4096 sha256-with-rsa-signature 'ISRG Root X1' nscurl https://api.rankacy.com/ --verbose --ats-diagnostics Starting ATS Diagnostics Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://api.rankacy.com/. A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error. ================================================================================ Default ATS Secure Connection --- ATS Default Connection ATS Dictionary: { } Result : PASS --- ================================================================================ Allowing Arbitrary Loads --- Allow All Loads ATS Dictionary: { NSAllowsArbitraryLoads = true; } Result : PASS --- ================================================================================ Configuring TLS exceptions for api.rankacy.com --- TLSv1.3 ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.3"; }; }; } Result : PASS --- --- TLSv1.2 ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.2"; }; }; } Result : PASS --- --- TLSv1.1 ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.1"; }; }; } Result : PASS --- --- TLSv1.0 ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.0"; }; }; } Result : PASS --- ================================================================================ Configuring PFS exceptions for api.rankacy.com --- Disabling Perfect Forward Secrecy ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- ================================================================================ Configuring PFS exceptions and allowing insecure HTTP for api.rankacy.com --- Disabling Perfect Forward Secrecy and Allowing Insecure HTTP ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionAllowsInsecureHTTPLoads = true; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- ================================================================================ Configuring TLS exceptions with PFS disabled for api.rankacy.com --- TLSv1.3 with PFS disabled ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.3"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- --- TLSv1.2 with PFS disabled ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.2"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- --- TLSv1.1 with PFS disabled ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.1"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- --- TLSv1.0 with PFS disabled ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionMinimumTLSVersion = "TLSv1.0"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- ================================================================================ Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for api.rankacy.com --- TLSv1.3 with PFS disabled and insecure HTTP allowed ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionAllowsInsecureHTTPLoads = true; NSExceptionMinimumTLSVersion = "TLSv1.3"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- --- TLSv1.2 with PFS disabled and insecure HTTP allowed ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionAllowsInsecureHTTPLoads = true; NSExceptionMinimumTLSVersion = "TLSv1.2"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- --- TLSv1.1 with PFS disabled and insecure HTTP allowed ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionAllowsInsecureHTTPLoads = true; NSExceptionMinimumTLSVersion = "TLSv1.1"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- --- TLSv1.0 with PFS disabled and insecure HTTP allowed ATS Dictionary: { NSExceptionDomains = { "api.rankacy.com" = { NSExceptionAllowsInsecureHTTPLoads = true; NSExceptionMinimumTLSVersion = "TLSv1.0"; NSExceptionRequiresForwardSecrecy = false; }; }; } Result : PASS --- I am running out of ideas. Also it's hard to test because the preview ad-hoc build is working fine. So only after submitting the app to TestFlight I am having this issue Looking for your response Martin
5
0
1.5k
Jul ’24
Customise text for Secure Enclave authentications
Hello, I am writing a macOS CLI application that holds cryptocurrency funds and uses Secure Enclave for security. I intend to create APIs so that other applications can create transactions from user's wallet in a secure way for the user. I am using P256 curve and authenticating a transaction involves making a signature for a hash of transaction data. Currently, this is how the authentication box looks like: However, this does not display any information about the transaction. If user is using my application, then I could display the information in the terminal. However, if some other CLI app wants to create transactions for the user, printing information in the terminal is insecure since a malicious app could override what is being printed. I see a rich authentication UI in apple apps. I wonder if there is a way to write UI for such applications. I would like to display some transaction information in the box instead of just "myapp needs to authenticate to continue". It would be great if there is a way to customize that text / add more text to it.
1
0
739
Jul ’24
App Group Not working as intended after updating to macOS 15 beta.
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?
24
9
4.1k
Mar ’25