Secure Enclave keys exists even after app uninstallation - ios

I have generated Keys inside the Secure enclave using the following Code Snippet,
func generateKeyPair(accessControl: SecAccessControl) throws -> (`public`: SecureEnclaveKeyReference, `private`: SecureEnclaveKeyReference) {
let privateKeyParams: [String: Any] = [
kSecAttrLabel as String: privateLabel,
kSecAttrIsPermanent as String: true,
kSecAttrAccessControl as String: accessControl,
]
let params: [String: Any] =
[
kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: privateKeyParams
]
var publicKey, privateKey: SecKey?
let status = SecKeyGeneratePair(params as CFDictionary, &publicKey, &privateKey)
guard status == errSecSuccess else {
throw SecureEnclaveHelperError(message: "Could not generate keypair", osStatus: status)
}
return (public: SecureEnclaveKeyReference(publicKey!), private: SecureEnclaveKeyReference(privateKey!))
}
Post un-installation of the application the keys still exists, is there a way to remove the keys from secure enclave ?
Thank you in advance :)

There is no trigger to perform code when the app is deleted from the device. Access to the keychain is dependant on the provisioning profile that is used to sign the application. Therefore no other applications would be able to access this information in the keychain.
https://stackoverflow.com/a/5711090/7350472
If you want to delete key from Secure Enclave you can call:
SecItemDelete(query as CFDictionary)
https://developer.apple.com/documentation/security/1395547-secitemdelete

Related

Retrieve issuer name from certificate by SecCertificate

I have a certificate file. SecCertificate object is created correctly.
let data = try? Data(contentsOf: fileUrl!)
let certificate = SecCertificateCreateWithData(nil, data! as CFData)
▿ Optional<SecCertificateRef>
- some : <cert(0x7fec5d608250) s: www.google.com i: Avast trusted CA>
// I can get Subject name by
SecCertificateCopySubjectSummary(certificate!)! as String
// I tried printing SecCertificateCopyNormalizedIssuerSequence
var data1 = SecCertificateCopyNormalizedIssuerSequence(certificate!)! as Data
print(String(data: data1, encoding: .utf8))
Optional("0h1\u{0B}0\t\u{06}\u{03}U\u{04}\u{06}\u{13}\u{02}CZ1\u{0F}0\r\u{06}\u{03}U\u{04}\u{08}\u{0C}\u{06}Prague1\u{0E}0\u{0C}\u{06}\u{03}U\u{04}\n\u{0C}\u{05}AVAST1\u{1D}0\u{1B}\u{06}\u{03}U\u{04}\u{0B}\u{0C}\u{14}Software Development1\u{19}0\u{17}\u{06}\u{03}U\u{04}\u{03}\u{0C}\u{10}Avast trusted CA")
But I need to get issuers name info like common name or organisation(like i from SecCertificateRef in above example) using SecCertificate.h
Thanks.

How to get SecIdentity after SecCertificateCreateWithData and SecItemAdd?

I was given a .cer file which needs to be used to connect with the server properly. At first SecCertificateCreateWithData crashed, so after research, I ended up converting .cer file to der format (openssl x509 -in cert.cer -outform der -out certder.der), and then iOS seemed to accept it.
I need to get SecIdentity object, however, there seems to be a problem:
let cerPath = Bundle.main.path(forResource: "certder", ofType: "der")
let data: NSData = try! Data(contentsOf: URL(fileURLWithPath: cerPath!)) as! NSData
let cert: SecCertificate = SecCertificateCreateWithData(nil, data)!
let params : [String: Any] = [
kSecClass as String : kSecClassCertificate,
kSecValueRef as String : cert,
kSecAttrLabel as String: "label1",
]
SecItemDelete(params as CFDictionary)
let status = SecItemAdd(params as CFDictionary, nil)
print("status1: \(status)")
let query: [String: Any] = [
kSecClass as String : kSecClassIdentity,
kSecReturnRef as String: true,
kSecAttrLabel as String: "label1"
]
var secIdentity: SecIdentity?
var extractedData: AnyObject?
let status2 = SecItemCopyMatching(query as NSDictionary, &extractedData)
print("status2: \(status2)")
if (status2 == errSecSuccess) {
print("success")
secIdentity = extractedData as! SecIdentity?
}
Log:
status1: 0
status2: -25300. Error -25300 is errSecItemNotFound
Here is how info of that given certificate looks like:
openssl x509 -in cert.cer -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1465898828 (0x575fd74c)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=LT, L=NamePurposelyHidden, O=NamePurposelyHidden, OU=NamePurposelyHidden, CN=NamePurposelyHidden
Validity
Not Before: Aug 9 08:21:03 2014 GMT
Not After : Jul 27 08:21:03 2064 GMT
Subject: C=LT, L=NamePurposelyHidden, O=NamePurposelyHidden, OU=NamePurposelyHidden, CN=NamePurposelyHidden
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b6:26:54:85:05:15:bd:2f:af:39:c0:91:41:05:
d2:35:74:bf:d2:e5:35:2d:3d:21:60:15:6b:d9:f6:
66:2b:02:29:c9:94:8b:3c:36:af:f8:5a:05:0f:21:
25:7d:90:5b:ab:70:f5:85:e8:ce:8a:60:2b:a4:61:
36:a8:b9:16:f6:79:7b:e1:1f:37:83:5b:fd:18:db:
70:7a:dd:79:3b:74:0d:4b:4f:4d:49:8c:79:0b:8c:
c1:2e:7c:4c:62:ae:b3:e4:b5:cf:4b:20:2c:98:20:
4a:4c:dd:49:67:99:7f:c1:dd:39:4d:be:8c:b7:6a:
b8:e6:c3:e3:a8:03:21:ec:a3:c8:e4:46:d7:e6:d4:
83:4a:5a:d7:a1:35:6f:54:72:96:b2:52:54:37:d4:
b4:62:f8:07:eb:27:d5:f0:42:0f:5a:3a:b8:ae:78:
38:73:e4:b5:7c:d1:6b:e4:61:6e:fd:df:c8:03:a4:
8d:fe:d3:92:98:df:30:fb:e1:03:45:cb:dd:6a:ca:
50:25:b2:a6:4b:4a:64:e0:79:a6:ef:35:53:df:d9:
48:16:f8:39:08:94:9b:8e:f0:74:01:ae:76:46:9c:
9c:a3:ec:70:45:24:44:96:3d:b4:06:51:2e:1e:dd:
4d:72:09:0d:9d:f1:11:08:9d:24:4b:5c:3e:79:0b:
b9:c7
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
AE:95:DF:CD:E0:D9:BB:D1:8E:CD:15:86:98:9B:04:35:D7:E6:96:44
Signature Algorithm: sha256WithRSAEncryption
61:d3:98:46:a9:53:db:fc:a9:3b:db:40:40:63:55:7c:66:74:
35:82:83:c8:19:4a:89:ae:bd:1b:5c:26:8e:20:f9:5b:db:aa:
0c:4c:99:4e:28:3b:51:00:33:96:5f:22:40:c1:c1:c8:ed:70:
d7:f7:3e:aa:ec:f9:0e:bb:ec:f8:17:12:c1:49:f6:02:0d:73:
57:39:48:5d:ad:8f:e5:e9:3b:b1:f8:1d:d4:7e:cf:58:d5:78:
73:9c:56:3b:bb:3a:25:36:c8:3b:b8:ba:90:97:6e:28:51:5d:
63:23:3e:90:7d:87:61:13:03:c5:27:1b:51:55:c0:17:76:18:
1a:74:6d:f9:2c:c8:f1:7a:89:64:41:fc:3b:7b:82:4f:e3:e8:
5d:02:40:3a:d5:81:f3:38:de:aa:79:53:1a:d2:a3:60:59:94:
8c:bd:62:bb:6d:70:da:0d:22:3e:9d:e0:fa:5d:20:87:ce:16:
f0:53:f4:b7:1d:c7:b3:59:64:ba:8a:73:75:bc:7f:61:cb:14:
d4:9f:34:3f:bd:b1:06:0c:62:f6:1f:b4:d2:15:38:61:bd:2c:
44:94:1c:e1:88:f4:d2:fc:42:d7:6d:ed:d6:4e:a2:b7:67:20:
01:7a:87:39:3b:4c:2b:3c:ef:3c:15:54:1d:cc:00:30:9c:cc:
e2:4f:71:98

Generating key pair in iOS using SecKeyGeneratePair failed with errSecInteractionNotAllowed

In my app I am using SecKeyGeneratePair to generate RSA key pair. After releasing the app, I started to notice occasional errSecInteractionNotAllowed errors (currently very rare) when using this function, so far only on iOS 10 devices. It is unclear to me why the key pair generation failed, or what I should do to fix that. Also, I could not find any documentation as to why key pair generation should fail with this error.
This is the code I used to generate the key pair:
guard let access = SecAccessControlCreateWithFlags(nil,
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
[],
nil) else {throw Error(description: "Failed to create access control")}
let privateAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): keyTag + self.privateKeyExtension,
String(kSecAttrAccessControl): access] as [String : Any]
let publicAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): keyTag + self.publicKeyExtension] as [String : Any]
let pairAttributes = [String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): self.rsaKeySize,
String(kSecPublicKeyAttrs): publicAttributes,
String(kSecPrivateKeyAttrs): privateAttributes] as [String : Any]
var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(pairAttributes as CFDictionary, &pubKey, &privKey)
After this code, I am checking the status, and if it is not errSecSuccess, I am logging an error with the status returned from the function. This is where I noticed the errSecInteractionNotAllowed error.
So, why does key pair generation or what I could do in order to fix it?
Thanks,
Omer
Two suggestions:
Try to add an other protection class like kSecAttrAccessibleAlways to your call of SecAccessControlCreateWithFlags and test if the behavior still occurs.
Further define a flag for your use case instead of passing an empty array. E.g. userPresence.
Additionally I stumbled across this SO post, maybe you can find some inspiration there.
After discussing this with Apple Developer Support, here is the solution:
let privateAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): keyTag + self.privateKeyExtension,
String(kSecAttrAccessible): kSecAttrAccessibleAlways] as [String : Any]
let publicAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): keyTag + self.publicKeyExtension,
String(kSecAttrAccessible): kSecAttrAccessibleAlways] as [String : Any]
let pairAttributes = [String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): self.rsaKeySize,
String(kSecPublicKeyAttrs): publicAttributes,
String(kSecPrivateKeyAttrs): privateAttributes] as [String : Any]
var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(pairAttributes as CFDictionary, &pubKey, &privKey)
The important part is the kSecAttrAccessible, choose the value that matches your needs from this list. Notice that some of the values will limit the access to the key in KeyVault.

Slow compiling swift source file - Xcode 8 swift 3

I have just updated my project to Xcode 8 and Swift 3.
My problem is that my app is very very slow to compile a specific class, the following:
var dict: Dictionary<String, AnyObject> {
return [
"book_key": book_key as AnyObject,
"book_title": book_title as AnyObject,
"book_author": book_author as AnyObject,
"book_price": book_price as AnyObject,
"book_publisher" : book_publisher as AnyObject,
"page_count": page_count as AnyObject,
"book_description": book_description as AnyObject,
"book_urlImage" : book_urlImage as AnyObject,
"book_urlImage2": book_urlImage2 as AnyObject,
"user_key": user_key as AnyObject,
"user_name": user_name as AnyObject,
"user_tag_login" : user_tag_login as AnyObject,
"onGoingNegotiations" : onGoingNegotiations as AnyObject,
"other_user_key": other_user_key as AnyObject,
"other_tag_login": other_tag_login as AnyObject,
"book_condition": book_condition as AnyObject,
"timestamp": timestamp! as AnyObject
]
}
How can I resolve my problem? Thanks for the help.
The method is so slowly and so I have found the solution.
The timestamp NSNumber will be cast as String and so the method works perfectly again.
var dict: Dictionary<String, String> {
return [
"book_key": book_key,
"book_title": book_title,
"book_author": book_author,
"book_price": book_price,
"book_publisher" : book_publisher,
"page_count": page_count,
"book_description": book_description,
"book_urlImage" : book_urlImage,
"book_urlImage2": book_urlImage2,
"user_key": user_key,
"user_name": user_name,
"user_tag_login" : user_tag_login,
"onGoingNegotiations" : onGoingNegotiations,
"other_user_key": other_user_key,
"other_tag_login": other_tag_login,
"book_condition": book_condition,
"timestamp": timestamp
]
}
Thanks all for the help
Swift 3 like this:
if it is of several types
var dict: [String: Any] = [
"book_key": true,
"book_title": "Yannick",
"book_author": "Test",
"book_price": 123
]
Or if you return lot of String like this
var dict: [String: String] = [
"book_key": "true",
"book_author": "Yannick",
"book_price": "123",
"book_publisher": "Yannick",
"page_count": "123"
]

Adding item to keychain using Swift

I'm trying to add an item to the iOS keychain using Swift but can't figure out how to type cast properly. From WWDC 2013 session 709, given the following Objective-C code:
NSData *secret = [#"top secret" dataWithEncoding:NSUTF8StringEncoding];
NSDictionary *query = #{
(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrService: #"myservice",
(id)kSecAttrAccount: #"account name here",
(id)kSecValueData: secret,
};
OSStatus = SecItemAdd((CFDictionaryRef)query, NULL);
Attempting to do it in Swift as follows:
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "MyService",
kSecAttrAccount: "Some account",
kSecValueData: secret
]
yields the error "Cannot convert the expression's type 'Dictionary' to 'DictionaryLiteralConvertible'.
Another approach I took was to use Swift and the - setObject:forKey: method on a Dictionary to add kSecClassGenericPassword with the key kSecClass.
In Objective-C:
NSMutableDictionary *searchDictionary = [NSMutableDictionary dictionary];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
In the Objective-C code, the CFTypeRef of the various keychain item class keys are bridged over using id. In the Swift documentation it's mentioned that Swift imports id as AnyObject. However when I attempted to downcast kSecClass as AnyObject for the method, I get the error that "Type 'AnyObject' does not conform to NSCopying.
Any help, whether it's a direct answer or some guidance about how to interact with Core Foundation types would be appreciated.
EDIT 2
This solution is no longer valid as of Xcode 6 Beta 2. If you are using Beta 1 the code below may work.
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let query = NSDictionary(objects: [kSecClassGenericPassword, "MyService", "Some account", secret], forKeys: [kSecClass,kSecAttrService, kSecAttrAccount, kSecValueData])
OSStatus status = SecItemAdd(query as CFDictionaryRef, NULL)
To use Keychain Item Attribute keys as dictionary keys you have to unwrap them by using either takeRetainedValue or takeUnretainedValue (as appropriate). Then you can cast them to NSCopying. This is because they are CFTypeRefs in the header, which aren't all copyable.
As of Xcode 6 Beta 2 however, this causes Xcode to crash.
You simply need to downcast the literal:
let dict = ["hi": "Pasan"] as NSDictionary
Now dict is an NSDictionary. To make a mutable one, it's very similar to Objective-C:
let mDict = dict.mutableCopy() as NSMutableDictionary
mDict["hola"] = "Ben"
In the xcode 6.0.1 you must do this!!
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
Perhaps things have improved since. On Xcode 7 beta 4, no casting seems to be necessary except when dealing with the result AnyObject?. Specifically, the following seems to work:
var query : [NSString : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : "MyAwesomeService",
kSecReturnAttributes : true, // return dictionary in result parameter
kSecReturnData : true // include the password value
]
var result : AnyObject?
let err = SecItemCopyMatching(query, &result)
if (err == errSecSuccess) {
// on success cast the result to a dictionary and extract the
// username and password from the dictionary.
if let result = result as ? [NSString : AnyObject],
let username = result[kSecAttrAccount] as? String,
let passdata = result[kSecValueData] as? NSData,
let password = NSString(data:passdata, encoding:NSUTF8StringEncoding) as? String {
return (username, password)
}
} else if (status == errSecItemNotFound) {
return nil;
} else {
// probably a program error,
// print and lookup err code (e.g., -50 = bad parameter)
}
To add a key if it was missing:
var query : [NSString : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : "MyAwesomeService",
kSecAttrLabel : "MyAwesomeService Password",
kSecAttrAccount : username,
kSecValueData : password.dataUsingEncoding(NSUTF8StringEncoding)!
]
let result = SecItemAdd(query, nil)
// check that result is errSecSuccess, etc...
A few things to point out: your initial problem might have been that str.dataUsingEncoding returns an Optional. Adding '!' or better yet, using an if let to handle nil return, would likely make your code work. Printing out the error code and looking it up in the docs will help a lot in isolating the problem (I was getting err -50 = bad parameter, until I noticed a problem with my kSecClass, nothing to do with data types or casts!).
This seemed to work fine or at least compiler didn't have kittens - UNTESTED beyond that
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var array1 = NSArray(objects:"\(kSecClassGenericPassword)", "MyService", "Some account", secret)
var array2 = NSArray(objects:"\(kSecClass)","\(kSecAttrService)", "\(kSecAttrAccount)","\(kSecValueData)")
let query = NSDictionary(objects: array1, forKeys: array2)
println(query)
let status = SecItemAdd(query as CFDictionaryRef, nil)
Seems to work fine in Beta 2
In order to get this to work, you need to access the retained values of the keychain constants instead. For example:
let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString
You can then reference the values in the MSMutableDictionary like so:
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
I wrote a blog post about it at:
http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/
Hope this helps!
rshelby
Swift 3
let kSecClassKey = String(kSecClass)
let kSecAttrAccountKey = String(kSecAttrAccount)
let kSecValueDataKey = String(kSecValueData)
let kSecClassGenericPasswordKey = String(kSecClassGenericPassword)
let kSecAttrServiceKey = String(kSecAttrService)
let kSecMatchLimitKey = String(kSecMatchLimit)
let kSecReturnDataKey = String(kSecReturnData)
let kSecMatchLimitOneKey = String(kSecMatchLimitOne)
you can also do it inside the dictionary itself alá:
var query: [String: Any] = [
String(kSecClass): kSecClassGenericPassword,
String(kSecAttrService): "MyService",
String(kSecAttrAccount): "Some account",
String(kSecValueData): secret
]
however, this is more expensive for the compiler, even more so since you're probably using the query in multiple places.
more convenient to use the cocoa pods SSKeychain
+ (NSArray *)allAccounts;
+ (NSArray *)accountsForService:(NSString *)serviceName;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;

Resources