I'm writing an app that uses the App Store Connect API and would like to store the private key contained in the .p8 file downloaded from the website in the keychain.
The following code successfully stores a key in the keychain with SecItemAdd, then tries to read it immediately, but without success (the error code of SecItemCopyMatching is errSecItemNotFound and the console outputs nil). Running the code a second time causes SecItemAdd to fail with code errSecDuplicateItem, and SecItemCopyMatching again with code errSecItemNotFound.
What am I doing wrong?
class AppDelegate: NSObject, NSApplicationDelegate {
private let secApplicationTag = "com.example.app".data(using: .utf8)!
func applicationDidFinishLaunching(_ aNotification: Notification) {
do {
try storeKey("asdf")
print(try readKey() as Any)
} catch {
print(error)
}
}
private func storeKey(_ key: String) throws {
guard let data = Data(base64Encoded: key) else {
fatalError()
}
let status = SecItemAdd([kSecClass as String: kSecClassKey, kSecAttrLabel as String: "Asdf", kSecAttrApplicationTag as String: secApplicationTag, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecValueData as String: data, kSecAttrSynchronizable as String: true] as [String: Any] as CFDictionary, nil)
if status != errSecSuccess {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
}
}
private func readKey() throws -> String? {
var item: CFTypeRef?
let status = SecItemCopyMatching([kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: secApplicationTag, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecReturnData as String: true] as [String: Any] as CFDictionary, &item)
switch status {
case errSecSuccess:
let data = item as! Data
return (data as Data).base64EncodedString()
case errSecItemNotFound:
return nil
default:
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
}
}
}