I've been looking all around but still not found the answer. Any idea how to return all kSecAttrAccounts from keychain? I would like to get back a list of every identifier I used when creating keychain items, then to choose the one I want to delete it using a "[KeychainWrapper deleteItemFromKeychainWithIdentifier:identifier] method".
It works fine when I remember the username (identifier) I used when creating the account but cannot figure out how to have them all back when you created lots of them.
I tried a kind of basic [dictionary objectForKey:(__bridge(id)kSecAttrAccount)] but it does not make it.
Thanks a lot!!
Tricho
Use kSecMatchLimitAll to get all values in your query dictionary for kSecMatchLimit
(__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit
It will fetch all passwords in keychain for kSecClassGenericPassword.You can use other keychain classes as well like this
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
(__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
(__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass, //change your class in query
nil];
CFTypeRef result = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
NSLog(#"%#", (__bridge id)result);
if (result != NULL) CFRelease(result);
EDIT : For delting all keys of your app you can use
+(void)deleteAllKeychainItems{
NSArray *secItemClasses = #[(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity];
for (id secItemClass in secItemClasses) {
NSDictionary *spec = #{(__bridge id)kSecClass:secItemClass};
SecItemDelete((__bridge CFDictionaryRef)spec);
}
}
It will delete all keychain items including all accounts password or values associated.
Related
I need to delete all the keys stored at the keychain. Problem is that they are not static strings (I create them using "the user name + static string") and I have to implement something like a reset app option.
At the moment of the app implementation (couple of years ago) I used a library called FXKeychain to access the keychain but didn't seem to have an option like that.
Is there any other way to do it?
I'm working with Objective-C.
I found this code searching about it:
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
(__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
nil];
NSArray *secItemClasses = [NSArray arrayWithObjects:
(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity,
nil];
for (id secItemClass in secItemClasses) {
[query setObject:secItemClass forKey:(__bridge id)kSecClass];
CFTypeRef result = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
NSLog(#"%#", (__bridge id)result);
if (result != NULL) CFRelease(result);
}
But I don't totally understand it and I think it's not working (maybe it has to be with the library I used?)
To delete the keychain items added from your app, this should work fine :
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:[[NSBundle mainBundle] bundleIdentifier] accessGroup:nil];
[keychain resetKeychainItem];
did you already try?
I am using the following code to remove saved keychain data from my App.
for (id secclass in #[
(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity]) {
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
secclass, (__bridge id)kSecClass,
nil];
SecItemDelete((__bridge CFDictionaryRef)query);
}
This works well, but I'm not sure if this deletes keychain data for other apps,sites,ect..
I don't think it does, but I am not too sure. Please let me know.
If it did, that would be a huge security problem.
(It doesn't. You're fine.)
To generate key pair I am using Secure Enclave (kSecAttrTokenIDSecureEnclave). When trying to access generated key pair, iOS system, asks for TouchID. Below is code snapshot how I am generating and accessing key pair.
Is here a way to setup properties/attributes, that Secure Enclave functionality will be able to use without TouchID and Passcode?
Generate key pair:
SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlocked,
kSecAccessControlUserPresence | kSecAccessControlPrivateKeyUsage, &error);
NSDictionary *parameters = #{
(__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave,
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeEC,
(__bridge id)kSecAttrKeySizeInBits: #256,
(__bridge id)kSecPrivateKeyAttrs: #{
(__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,
(__bridge id)kSecAttrIsPermanent: #YES,
(__bridge id)kSecAttrLabel: #“SecKey”,
},
};
SStatus status = SecKeyGeneratePair((__bridge CFDictionaryRef)parameters, &publicKey, &privateKey);
Access key pair:
NSDictionary *query = #{
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate,
(__bridge id)kSecAttrLabel: #"SecKey",
(__bridge id)kSecReturnRef: #YES
};
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);
This code is taken form Apple examples KeychainTouchID. By removing kSecAccessControlTouchIDAny it is possible to generate private key inside secure enclave and later use it without entering a passcode.
SecAccessControlRef sacObject;
// Should be the secret invalidated when passcode is removed? If not then use `kSecAttrAccessibleWhenUnlocked`.
sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
/*kSecAccessControlTouchIDAny |*/ kSecAccessControlPrivateKeyUsage, &error);
// Create parameters dictionary for key generation.
NSDictionary *parameters = #{
(__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave,
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeEC,
(__bridge id)kSecAttrKeySizeInBits: #256,
(__bridge id)kSecPrivateKeyAttrs: #{
(__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject,
(__bridge id)kSecAttrIsPermanent: #YES,
(__bridge id)kSecAttrLabel: #"my-se-key",
},
};
I'm struggling with iOS keychain and i can't seem to find any good documentation.
Anyway, I have two apps, and basically all i want to do is share some data in the keychain and keep some data private so that the other app cannot access it.
I've tried to implement the KeychainItemWrapper provided by Apple, but this is simply not working. I have no issue sharing data, but if i don't set an access group, the data is still shared. I working with a device not the simulator, which could lead to the same problems.
Here is my code
App 1 :
KeychainItemWrapper *item = [[KeychainItemWrapper alloc] initWithIdentifier:#"SharedKeyChainApp" accessGroup:nil];
[item setObject:#"MyAccount" forKey:(__bridge id)kSecAttrAccount];
[item setObject:#"SecureValue" forKey:(__bridge id)kSecValueData];
App 2 :
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"SharedKeyChainApp" accessGroup:nil];
NSString *data = [keychain objectForKey:(__bridge id)kSecValueData];
NSLog(#"data is : %#",data); //Prints "data is : SecureValue"
If I remove in project properties my keychain group in one or the other app, it won't print anything. But obviously i'm not able to share data between those two apps anymore.
Thanks
If it's a shared keychain then it's shared. All the data in it will be accessible to any other app which can access the keychain.
You could:
Create 2 keychain. One shared and one private. Sharable stuff goes in shared, private stuff goes in private.
Encrypt data you won't want to to share with others.
I'd probably go with the first. IMHO, KeychainItemWrapper is pretty poor as grab and use code. It's old code which provides little in the way of functionality. I'm attaching a quick and dirty bit of code I wrote to play with and test out using the key chain functionality without KeychainItemWrapper. In this case I was playing with items both in "app" and "Security" to create some shared and non-shared items. You can't really tell that here since it's just some test code and sharing is under Targets->Capabilities->Keychain Sharing.
- (void)viewDidLoad {
[super viewDidLoad];
// [self removeKeychainItem];
// [self addKeychainItem];
[self searchForKeychainItems];
}
- (void)searchForKeychainItems {
[self log:#"\n\n EXISTING KEYCHAIN ITEM(S)"];
NSDictionary *query = #{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue, // returns password
(__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue, // returns rest of data
// (__bridge id)kSecAttrAccessGroup: #"AAAAAAAAAA.com.foo.Security"
// (__bridge id)kSecAttrAccessGroup: #"AAAAAAAAAA.com.foo.app"
};
OSStatus resultCode;
CFArrayRef *searchResults = nil;
resultCode = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&searchResults);
NSArray *foo = CFBridgingRelease(searchResults);
[self log:[NSString stringWithFormat:#"Search result code: %d", (int)resultCode]];
[self log:[NSString stringWithFormat:#"Search Results: %#", foo]];
NSDictionary *keychainItem = foo[0];
NSString *password = [[NSString alloc] initWithData:[keychainItem objectForKey:(__bridge id)kSecValueData] encoding:NSUTF8StringEncoding];
[self log:[NSString stringWithFormat:#"password is `%#`", password]];
}
- (void)addKeychainItem {
[self log:#"\n\n ADDING KEYCHAIN ITEM"];
NSDictionary *genericDataDictionary = #{#"authState": #"1",
#"lastAuthDate": #"2/11/2014",
#"otherCrap": #"poo"};
NSData *encodedGenericData = [NSKeyedArchiver archivedDataWithRootObject:genericDataDictionary];
NSData *encodedPassword = [#"secret" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = #{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrCreator: #"MyCom",
(__bridge id)kSecAttrComment: #"keychain tests",
(__bridge id)kSecAttrService: #"Credentials",
(__bridge id)kSecAttrAccount: #"username",
(__bridge id)kSecValueData: encodedPassword,
(__bridge id)kSecAttrGeneric: encodedGenericData,
(__bridge id)kSecAttrAccessGroup: #"AAAAAAAAAA.com.foo.Security"
};
OSStatus result;
result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
NSLog(#"Add status code: %d", (int)result);
}
- (void)removeKeychainItem {
[self log:#"\n\n REMOVING KEYCHAIN ITEM"];
NSDictionary *query = #{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
// (__bridge id)kSecAttrCreator: #"MyCom",
// (__bridge id)kSecAttrService: #"Credentials",
(__bridge id)kSecAttrComment: #"New Keychain standards Test Item",
// (__bridge id)kSecAttrAccount: #"username",
// (__bridge id)kSecValueData: [#"password" dataUsingEncoding:NSUTF8StringEncoding],
// (__bridge id)kSecAttrGeneric: encodedGenericData
// (__bridge id)kSecAttrAccessGroup: #"AAAAAAAAAA.com.foo.Security"
};
OSStatus resultsCode;
resultsCode = SecItemDelete((__bridge CFDictionaryRef)query);
NSLog(#"Delete results code: %d", (int)resultsCode);
}
- (void)log:(NSString *)text {
self.textView.text = [[self.textView.text stringByAppendingString:text] stringByAppendingString:#"\n"];
}
One thing to be aware of is that you need to add both a private and a shared access group. The private access group should match the app's bundle ID and come before the shared one.
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)<APP BUNDLE ID></string>
<string>$(AppIdentifierPrefix)com.mybusiness.shared</string>
</array>
</dict>
If you only add a single named access group then this will be the default for all keychain items effectively sharing everything.
I have the following code to create a keychain item in the keychain:
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
[dict setObject: (__bridge id) kSecClassGenericPassword forKey: (__bridge id) kSecClass];
[dict setObject: MYKEY forKey: (__bridge id) kSecAttrService];
[dict setObject: #"0" forKey: (__bridge id) kSecValueData];
SecItemAdd ((__bridge CFDictionaryRef) dict, NULL);
Which works fine. Can anyone give the syntax for what exactly to put for SecItemUpdate if I want to change this item?
UPDATE: with the following:
NSMutableDictionary *query = [NSMutableDictionary dictionary];
NSMutableDictionary *attributesToUpdate = [NSMutableDictionary dictionary];
[query setObject: (__bridge id) kSecClassGenericPassword forKey: (__bridge id) kSecClass];
[query setObject: MYKEY forKey: (__bridge id) kSecAttrService];
[query setObject: (id) kCFBooleanTrue forKey: (__bridge id) kSecReturnData];
NSString *numberOfBalloonsString = [NSString stringWithFormat:#"%d", numberOfBalloonsUsed];
NSData *numberOfBalloonsData = [numberOfBalloonsString dataUsingEncoding:NSUTF8StringEncoding];
[attributesToUpdate setObject: numberOfBalloonsData forKey:(__bridge id)kSecValueData];
OSStatus error = SecItemUpdate ((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) attributesToUpdate);
NSLog(#"Error #: %ld", error);
I'm getting the error code -50 =
One or more parameters passed to the function were not valid.
SecItemUpdate is terribly documented.
The query parameter of SecItemUpdate is documented as a query (as used in other functions) as well as the vague statement: "Specify the items whose values you wish to change". This seems to imply that you must include the existing attribute value in this dictionary that you want to change but I don't think you do. I've found you can use the same query you use to get attributes for the item you want to update.
The attributes parameter should be the result of SecItemCopyMatching with the kSecValueData key and value added and any attributes changed.
A late answer, but an answer nonetheless:
I've been struggling with updating items in the keychain as well, my context was a little different though.
What happened:
I could add a keychain item with success (using SecItemAdd), but calling SecItemUpdate on the same item failed with the notorious errSecParam -50.
What was even worse; if the keychain item already existed (hence I called SecItemUpdate immediately), the update went through with no problems at all.
I've got absolutely no idea as of why that happened...
How I fixed it:
Quite simple actually, I just removed "params" until the big bad -50 was satisfied. This happened when I removed the kSecClass from the dictionary I retrieved from kSecItemCopyMatching.
Here's my code:
// If the item already exists, we update it instead
if (SecItemCopyMatching((__bridge CFDictionaryRef)self.searchQueryDict, (CFTypeRef *)&foundItem) == errSecSuccess) {
NSMutableDictionary *updateDict = (__bridge NSMutableDictionary *)foundItem;
[updateDict addEntriesFromDictionary:dictToSave];
[updateDict removeObjectForKey:(__bridge id)kSecClass];
OSStatus updateSuccess = SecItemUpdate((__bridge CFDictionaryRef)self.updateQueryDict,
(__bridge CFDictionaryRef)updateDict);
NSAssert(updateSuccess == errSecSuccess, #"Couldn't save the dirty info to the keychain, might want to log the updateSuccess (%d)", updateSuccess);
}
As a reference I used the following dictionaries
self.searchQueryDict contained:
(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword
(__bridge id)kSecAttrService : service
(__bridge id)kSecAttrGeneric : [identifier dataUsingEncoding:NSUTF8StringEncoding]
(__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne
(__bridge id)kSecReturnAttributes : (__bridge id)kCFBooleanTrue
(__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue
self.updateQueryDict contained:
(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService : service,
(__bridge id)kSecAttrGeneric : [identifier dataUsingEncoding:NSUTF8StringEncoding]
dictToSave should contain the values (in the correct format) which needs to change
Removing the kSecClass fixed the problem for me.