I have the following code which creates a key pair in the secure enclave.
let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.privateKeyUsage,
nil)!
var attributes: [String: Any] = [
kSecAttrKeyType as String: encryptionType,
kSecAttrKeySizeInBits as String: encryptionBits,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "abc".data(using: .utf8) as Any,
kSecAttrAccessControl as String: access,
],
]
if Device.hasSecureEnclave {
attributes[kSecAttrTokenID as String] = kSecAttrTokenIDSecureEnclave
}
var error: Unmanaged<CFError>?
SecKeyCreateRandomKey(attributes as CFDictionary, &error)
When running in a simulator or on the device it works just fine. But when I run it in a unit test, an error is returned from the SecKeyCreateRandomKey call:
Error Domain=NSOSStatusErrorDomain Code=-50
"Key generation failed, error -50" UserInfo={NSDescription=Key generation failed, error -50}
After trying a few things I found that the problem was the kSecAttrIsPermanent key in the attributes dictionary. If I remove it, the unit tests run fine.
All the doco I've read indicates it should be ok, but it's failing every time.
Anyone know why?
You may have already seen this:
http://www.openradar.me/36809637
I have the exact same issue and there was nothing in the Xcode 9.3 (beta) release notes to suggest it's been fixed.
Related
I'm generating the private key in my iOS app for secure communication between server and app.
The private key is being stored in the keychain. In the new version of the app, I want to use the shared keychain group because of notification extensions. How do is transfer the private key that was stored in the app keychain to the shared group keychain. Below is the code I m using to generate the private key
func createPrivateKey(withLabel label: String) throws -> SecKey {
let privateKeyAttrs: [String: Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationLabel as String: label,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
]
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPrivateKeyAttrs as String: privateKeyAttrs,
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
// swiftlint:disable:next force_unwrapping
throw error!.takeRetainedValue() as Error
}
return privateKey
}
You'll have to specify the access group on creation, or update the existing keys with the new access groups. After you've done that, you should setup the entitlements correctly, so that apps and extensions you create can access the correct keychain access group. Read with care, as there is only a thin line between app groups and keychain sharing group. Make sure you set up the correct one (documentation here).
As for your query
let accessGroup = "<# Your Team ID #>.com.example.SharedItems"
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecAttrAccessGroup as String: accessGroup,
kSecPrivateKeyAttrs as String: privateKeyAttrs
]
That should create the query for creating keys for the access group you specify. I suppose you can figure out the update query yourself.
I have been trying to delete keys from Secure Enclave on iOS in Swift. I continuously get OSStatus response -50. From what I can tell from the link there are 22 errors associated with OSStatus -50 for SecBasePrivate.h. How can I get more detailed error info or a specific error name?
var query : [String: Any] = [
kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecAttrLabel as String: keychainTagPublic,
]
var status = SecItemDelete(query as CFDictionary)
For SecItemDelete status results, we should probably be looking in the SecBase.h header:
errSecParam = -50, /* One or more parameters passed to a function were not valid. */
I have the following code following this storing_keys_in_the_keychain.
func generateInitialKey() -> Data {
let key = AES256.randomKey()
let addQuery: Dictionary<String, Any> = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: applicationTag,
kSecValueRef as String: key
]
let status = SecItemAdd(addQuery as CFDictionary, nil)
print(errSecParam, status)
guard status == errSecSuccess else { fatalError("Can't save Key") }
return key
}
The function AES256.randomKey() generates Data of 64 bytes. The applicationTag is Data too:
let applicationTag = "example".data(using: .utf8)!
However, I do end up receiving the errSecParam(-50) error. Can someone help me out?
Read the documentation carefully. errSecParam(-50) means one or more parameters passed to the function were not valid. The link leads you to the site where you can see the description of the status.
At a minimum, you specify the type and size of keys to create using the kSecAttrKeyType and kSecAttrKeySizeInBits parameters, respectively.
This will result in you having the next problem: there is no kSecAttrKeyTypeAES. This is already discussed and answered on the Apple developer forums. The advice there is to use kSecClassGenericPassword.
I am wondering if anyone knows whether its possible to update the flags after the key creation inside the Secure Enclave or not?
Here's how I am creating the key:
let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[SecAccessControlCreateFlags.userPresence,
SecAccessControlCreateFlags.privateKeyUsage],
nil)!
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "stacksometimesoverflow",
kSecAttrAccessControl as String: access
]
]
var error: Unmanaged<CFError>?
guard SecKeyCreateRandomKey(attributes as CFDictionary, &error) != nil else {
throw error!.takeRetainedValue() as Error
}
As you can see, the key is created with
SecAccessControlCreateFlags.userPresence, SecAccessControlCreateFlags.privateKeyUsage
My question is, is it possible to update the access flag of the key (same key), say I want to remove SecAccessControlCreateFlags.userPresence
All the best!
Johnny
I don't think that's possible. According to Apple's documentation:
... because its backing storage is physically part of the Secure Enclave, you can never inspect the key’s data.
I think the best way is to delete your key with SecItemDelete(_:) and then create new key without the .userPresence flag.
I am writing some keychain code on iOS. When I try to insert an item in keychain I get error -50.
What does OSStatus error -50 mean?
It's errSecParam, indicating one or more of your parameters is wrong.
Here:
https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html#//apple_ref/c/econst/errSecParam
If you are adding a password to the keychain make sure you pass it as Data and not String, otherwise you will get an OSStatus error -50.
static func savePassword(password: Data, account: String) throws -> OSStatus {
let query = [
kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccount as String: account,
kSecValueData as String: password
] as [String: Any]
SecItemDelete(query as CFDictionary)
return SecItemAdd(query as CFDictionary, nil)
}
Error -50 is a errSecParam, and means that at least one of the parameters you passed in a function was/are not valid.
This can be due to type differences, or perhaps an invalid value.
See this page on the Apple site to read the official documentation from Apple on errSecParam.