I am storing and loading a serialized nsdictionary to the keychain as in this post (Store NSDictionary in keychain), but I need to be able to update/edit the dictionary contents so I would like to delete it and re add.
I just dont know how to do it. I have the following code taken from the above post:
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"arbitraryId" accessGroup:nil]
NSString *error;
//The following NSData object may be stored in the Keychain
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[keychain setObject:dictionaryRep forKey:kSecAttrService];
//When the NSData object object is retrieved from the Keychain, you convert it back to NSDictionary type
dictionaryRep = [keychain objectForKey:kSecAttrServce];
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:dictionaryRep mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
SecItemDelete((CFDictionaryRef)dictionaryRep); // doesnt work
the values arent deleted from the keychain.
thanks
This is a strange place to store the data. You're putting it in kSecAttrService, which isn't encrypted. I think you mean to put this in kSecValueData (this is the only piece of a keychain item that is encrypted).
That said, there's no need to delete the item. You can just use [keychain setObject:forKey:] to update the value whenever you want. KeychainItemWrapper automatically detects whether the item exists already, and updates it if it does.
If you want to delete the item using KeychainItemWrapper, use -resetKeychainItem. This calls SecItemDelete() with the correct value. You cannot in general mix-and-match the use of KeychainItemWrapper and raw calls to SecItem* without a good understanding of the Keychain API and exactly how KeychainItemWrapper works.
Although i have never worked on accessing Keychain properties. but by looking at the SecItemDelete method in Apple document the expected parameter is a dictionary, in your code you are passing dictionaryRep which is a NSData type.
https://developer.apple.com/library/mac/#documentation/security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/c_ref/SecItemDelete
Found a question here, hope it might help.
I want to delete all items in my self created KeyChain on Mac OS X
Related
The following code is returning an exception with the following error message and My Application Crashed, in my code all data store in NSMutableDictionary and then store in NSUserDefaults
After get the data and assign global NSMutableDictionary and i will try to update data in NSMutableDictionary app crash and showing error
-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * cacheUploadDataDic = [[defaults objectForKey:#"SalaryIncomeData"] mutableCopy];
Exactly, it crashes with mutable type issue, all the objects you retrieved from user defaults are inmutable, it uses the plist file to serialized the data list to file, check the plist programming guide about the supported types.
You shall regard the user defaults as a simple plist file storage wrapper.
Use NSMutableDictionary's 'initWithDictionary:' method instead.
Since an answer to the question in my comment does not appear to be forthcoming, I'm just going to go out on a limb and make the guess that your cacheUploadDataDic dictionary contains some more NSDictionary objects inside of it, and that your crash is occurring when you try to mutate one of those dictionaries. This fails because mutableCopy performs a shallow copy; only the dictionary object itself is copied, and all of the objects inside the dictionary, including any additional dictionaries, remain their original immutable selves.
You can fix this by making a deep copy of the dictionary instead. There are a few ways to do this, but the simplest is probably to use the CFPropertyListCreateDeepCopy function. Unfortunately, we have to do some bridging, since this API is only available at the CoreFoundation level, the reason for which is one of those eternal mysteries.
Anyway, do something like this:
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"SomeKey"];
NSMutableDictionary *mutableDict = CFBridgingRelease(
CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(__bridge CFDictionaryRef)dict,
kCFPropertyListMutableContainersAndLeaves)
);
You will now be able to mutate the contents of mutableDict to your heart's content.
Follow your code, there shows no problem.
Then if possible, you can check the class of cacheUploadDataDic before update data and after NSMutableDictionary * cacheUploadDataDic = [[defaults objectForKey:#"SalaryIncomeData"] mutableCopy].
If there are one NSMutableDictionary and the other is NSDictionary then you must change cacheUploadDataDic at other line.
if you can't find out where you change the object,you can try KVO.
I am building a mobile iOS app for a web backend. I retrieve the JSON response using the following code:
NSError *error;
NSString *url_string = [NSString stringWithFormat: #"https://myURL"];
NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString:url_string]];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
What would be the best/simplest way to store this data locally so that I can use a local database when internet connectivity is not available? The intention is to check web connectivity at launch of the app. If connection is available, get JSON response, parse and update local copy, if connection is not available parse the data from local storage.
Thanks in advance.
If you wish to cache the JSON data so you can still use it offline, I would write the JSON dictionary (or the data) to a file in your app's sandbox. A subfolder of the "Application Support" folder would be a good place. You don't want to use the Caches folder in this case because the files could be purged by iOS when you need them offline.
The trick is to map a given URL to a filename. You need this mapping to both save a file for a given URL and to later load the file if offline. You should be able convert a URL to a useful filename simply by converting all / characters to something else such as an underscore.
You probably don't want these files backed up when a user backups their iOS device so be sure you mark the files with the "do not backup" attribute. There are many existing question covering that topic.
The best way is CoreData and
the simplest way is NSUserDefaults
NSUserDefaults Class Reference
[[NSUserDefaults standardUserDefaults] setObject: jsonDict forKey:#"dictionaryKey"];
//...
NSDictionary * myDictionary = [[NSUserDefaults standardUserDefaults] dictionaryForKey:#"dictionaryKey"];
Only a short question here, I would like to know the best place to put my registration code for the user that I get back from the server. I am encrypting it as seen here.
//encrypting
NSString* strToEncrypt =NewPINField.text
NSString* theKey = #\"KeyKeyKeyKey\";
NSData* dataToEncrypt = [strToEncrypt dataUsingEncoding: NSUTF8StringEncoding];
NSData *encryptedData = [dataToEncrypt EncryptWithKey: theKey];
NSLog(#\"Encrypted data: %#\", encryptedData);
//decrypting
NSData* encryptedData = (NSData*)[profileData objectForKey:#\"PIN\"];
NSString* theKey = #\"KeyKeyKeyKey\"; //notice this is the same as above. It MUST be
NSData *decData = [encryptedData DecryptWithKey: theKey ];
currentPIN = [NSString stringWithUTF8String:[decData bytes]];
NSLog(#\"Decrypted pin: %#\", currentPIN);
The only other specification is to hide it / put it somewhere no know would think to look.
I need to save state so it needs to be some sort of plist, I was just wondering if there is a way to hide it a little better than just adding it straight to my plist file.
what would you do?
Any help would be greatly appreciated.
If you need to securely store data, I would highly recommend using the keychain. There is a tutorial on creating a basic keychain app here: http://www.raywenderlich.com/6475/basic-security-in-ios-5-tutorial-part-1
You can safely store data in the keychain without worrying about encrypting it first, since that is handled by the OS. But you can if you want to.
When implementing security, never try to hide things where someone won't look: http://en.wikipedia.org/wiki/Security_through_obscurity
I have a PKCS12 file in my app's document folder that contains one certificate and one private key.
I am able to open this .p12 file, extract the identity object and display some information thanks to Apple's documentation (https://developer.apple.com/library/ios/#documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html#//apple_ref/doc/uid/TP40001358-CH208-DontLinkElementID_10)
What I'm trying to do now is to store this Identity to the keychain so I can use it later. I've read a lot of different stuff on iOS Keychain and I'm having a hard time figuring out how it really works.
Apple's code seems to use a persistent_ref to retrieve the Identity stored in Keychain. But I don't really understand what this is... Is it a simple reference like a memory ref ? If that is the case what happens when the device reboots ?
Unable to find a lot more information about this I tried to do it differently by using the kSecAttr attributes.
The current code works fine to add Identity to keychain :
NSMutableDictionary * dictionary = [[[NSMutableDictionary alloc] init] autorelease];
[dictionary setObject:#"LABEL" forKey:kSecAttrLabel];
[dictionary setObject:(id)newIdentity forKey:(id)kSecValueRef];
OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
But if I try to add it a second time I'm receiving a -25299 error which is "fine" since it already exists. I tried handling it by an update like this :
NSMutableDictionary *searchDictionary = [[[NSMutableDictionary alloc] init] autorelease];
[searchDictionary setObject:#"LABEL" forKey:kSecAttrLabel];
[searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnRef];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
[updateDictionary setObject:(id)newIdentity forKey:(id)kSecValueRef];
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,(CFDictionaryRef)updateDictionary);
With this code I get a -50 status error apparently because I have invalid parameters... Which one ? Why ? What can I do to update my keychain correctly ?
EDIT: As suggested I tried to delete the existing element prior to adding it but I'm stuck with the same status code (-50). Below is the code I tried:
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
OSStatus status = SecItemDelete((CFDictionaryRef)searchDictionary);
NSAssert(status == noErr, #"Problem deleting current keychain item." );
setupSearchDirectoryForIdentifier simply create a NSDictionnary with the label of my item:
- (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier {
// Setup dictionary to access keychain.
NSMutableDictionary *searchDictionary = [[[NSMutableDictionary alloc] init] autorelease];
[searchDictionary setObject:identifier forKey:kSecAttrLabel];
return searchDictionary;
}
Thank you
PS : I'm developing on Xcode 4.2 / iPad 5.1.1
Even if this is an old post, as I came up with similar issues very recently when working with identities in keychains (to be used for SSL client authentication), I am providing my own experience (with XCode 5.0.1) as support for this seems to be still very confusing.
SecItemAdd must indeed be used with very few keys. kSecValueRef (with the identity) is of course mandatory and optionally kSecAttrLabel. Using several other keys (e.g. kSecClass) resulted in silent failures, i.e. no error reported but the identity wasn't added. This was very confusing.
I didn't succeed either to use SecItemUpdate with such an identity. All my attempts resulted in a -50 error.
To be able to update an existing identity I deleted the existing identity using SecItemDelete and then added it with SecItemAdd. What was again confusing is that although the call to SecItemDelete returned a -25300 error back (errSecItemNotFound), it could be ignored as the identity was indeed deleted as it was possible to add it afterwards (using SecItemAdd) without an error.
SecIdentityRefs, SecKeyRefs and similar Sec...Ref values are ephemeral representations of keychain items. They become invalid when the application exits or when their retain count reaches zero. They cannot be directly saved in persistent storage.
On the other hand, a persistent reference is a piece of CFDataRef that you can use to retrieve a particular keychain item later. You can store it in a file, in NSUserDefaults, or anywhere else you fancy. It will not become invalid when the application exits or when the device is rebooted. (However, a persistent reference may become invalid when the keychain itself is removed (i.e., when device is restored), when the item it refers to is deleted or when one of the item's identifying attributes is modified.)
Apple's sample code uses SecItemAdd's second argument to retrieve persistent references to the items it adds to the keychain. This is then presumably stored in NSUserDefaults. Given the persistent reference, the app can later use SecItemCopyMatching to convert it to an SecIdentityRef that it can use.
Note that you don't have to use persistent references if you don't want to; if you wish, you can also choose to retrieve keychain items based on their label, or any other identifying attribute.
SecItemUpdate probably fails because a SecIdentityRef isn't a real keychain item. It is but a pseudo-item that is constructed when a public key and its associated private key are both on the keychain. As such, it doesn't make much sense to update it -- it doesn't own any attributes. All its properties are inherited from its associated SecCertificateRef (the certificate) and SecKeyRef (the private key). Updating these items instead of the identity should work. (But it is easier to simply create the item correctly: you can initialize any attribute by adding it and its value to SecItemAdd's first parameter.) Alternatively, you could try simply deleting the identity (SecItemDelete) before re-adding it to the keychain.
I have a login-screen in my iOS app.
The username and password will be saved in the NSUserDefaults and be loaded into the login-screen again when you enter the app again (of course, NSUserDefaults are permanent).
Now, the user have the possibility to disable the username/password saving feature.
So the NSUserDefaults will be cleared then.
But In my app I need this username/password for database queries for the user.
So: Where to store the data except NSUserDefaults?
(This place can / should be deleted when the user quit the app or logout).
You should always use Keychain to store usernames and passwords, and since it's stored securely and only accessible to your app, there is no need to delete it when app quits (if that was your concern).
Apple provides sample code that stores, reads and deletes keychain items and here is how to use the keychain wrapper class from that sample which greatly simplifies using Keychain.
Include Security.framework (in Xcode 3 right-click on frameworks folder and add existing framework. In Xcode 4 select your project, then select target, go to Build Phases tab and click + under Link Binary With Files) and KeychainItemWrapper .h & .m files into your project, #import the .h file wherever you need to use keychain and then create an instance of this class:
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"YourAppLogin" accessGroup:nil];
(YourAppLogin can be anything you chose to call your Keychain item and you can have multiple items if required)
Then you can set the username and password using:
[keychainItem setObject:#"password you are saving" forKey:kSecValueData];
[keychainItem setObject:#"username you are saving" forKey:kSecAttrAccount];
Get them using:
NSString *password = [keychainItem objectForKey:kSecValueData];
NSString *username = [keychainItem objectForKey:kSecAttrAccount];
Or delete them using:
[keychainItem resetKeychainItem];
If you need an ARC version of the wrapper here is the link https://gist.github.com/1170641
Thanks to
A very easy solution via Keychains.
It's a simple wrapper for the system Keychain. Just add the SSKeychain.h, SSKeychain.m, SSKeychainQuery.h and SSKeychainQuery.m files to your project and add the Security.framework to your target.
To save a password:
[SSKeychain setPassword:#"AnyPassword" forService:#"AnyService" account:#"AnyUser"]
To retrieve a password:
NSString *password = [SSKeychain passwordForService:#"AnyService" account:#"AnyUser"];
Where setPassword is what value you want saved and forService is what variable you want it saved under and account is for what user/object the password and any other info is for.
You can simply use NSURLCredential, it will save both username and password in the keychain in just two lines of code.
See my detailed answer.
I decided to answer how to use keychain in iOS 8 using Obj-C and ARC.
1)I used the keychainItemWrapper (ARCifief version) from GIST:
https://gist.github.com/dhoerl/1170641/download
- Add (+copy) the KeychainItemWrapper.h and .m to your project
2) Add the Security framework to your project (check in project > Build phases > Link binary with Libraries)
3) Add the security library (#import ) and KeychainItemWrapper (#import "KeychainItemWrapper.h") to the .h and .m file where you want to use keychain.
4) To save data to keychain:
NSString *emailAddress = self.txtEmail.text;
NSString *password = self.txtPasword.text;
//because keychain saves password as NSData object
NSData *pwdData = [password dataUsingEncoding:NSUTF8StringEncoding];
//Save item
self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"YourAppLogin" accessGroup:nil];
[self.keychainItem setObject:emailAddress forKey:(__bridge id)(kSecAttrAccount)];
[self.keychainItem setObject:pwdData forKey:(__bridge id)(kSecValueData)];
5) Read data (probably login screen on loading > viewDidLoad):
self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"YourAppLogin" accessGroup:nil];
self.txtEmail.text = [self.keychainItem objectForKey:(__bridge id)(kSecAttrAccount)];
//because label uses NSString and password is NSData object, conversion necessary
NSData *pwdData = [self.keychainItem objectForKey:(__bridge id)(kSecValueData)];
NSString *password = [[NSString alloc] initWithData:pwdData encoding:NSUTF8StringEncoding];
self.txtPassword.text = password;
Enjoy!
If you are having an issue retrieving the password using the keychain wrapper, use this code:
NSData *pass =[keychain objectForKey:(__bridge id)(kSecValueData)];
NSString *passworddecoded = [[NSString alloc] initWithData:pass
encoding:NSUTF8StringEncoding];
checkout this sample code
i tried first the apple's wrapper from the sample code but this is much simpler for me
try this one:
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"YourAppLogin" accessGroup:nil];
[keychainItem setObject:#"password you are saving" forKey:kSecValueData];
[keychainItem setObject:#"username you are saving" forKey:kSecAttrAccount];
may it will help.
I looked at using KeychainItemWrapper (the ARC version) but I didn't find its Objective C wrapper as wholesome as desired.
I used this solution by Kishikawa Katsumi, which meant I wrote less code and didn't have to use casts to store NSString values.
Two examples of storing:
[UICKeyChainStore setString:#"kishikawakatsumi" forKey:#"username"];
[UICKeyChainStore setString:#"P455_w0rd$1$G$Z$" forKey:#"password"];
Two examples of retrieving
UICKeyChainStore *store = [UICKeyChainStore keyChainStore];
// or
UICKeyChainStore *store = [UICKeyChainStore keyChainStoreWithService:#"YOUR_SERVICE"];
NSString *username = [store stringForKey:#"username"];
NSString *password = [store stringForKey:#"password"];
There is a small bug in the above code (by the way Dave it was very helpful your post thank you)
In the part where we save the credentials it also needs the following code in order to work properly.
[self.keychainItem setObject:#"myCredentials" forKey:(__bridge id)(kSecAttrService)];
most probably is because the second time we try to (re-)sign in with the same credentials it finds them already assigned in the keychain items and the app crashes. with the above code it works like a charm.
To update this question:
For those using Swift checkout this drag and drop swift implementation by Mihai Costea supporting access groups:
https://github.com/macostea/KeychainItemWrapper.swift/blob/master/KeychainItemWrapper.swift
Before using the keychain:
consider twice before storing passwords. In many cases storing an authentication token (such as a persistence session id) and the email or account name might be enough. You can easily invalidate authentication tokens to block unauthorized access, requiring the user to login again on the compromised device but not requiring reset password and having to login again on all devices (we are not only using Apple are we?).
But, now you can go for NURLCredential instead of keychain wrapper. It does what we need to do.
For swift you can use this library:
https://github.com/jrendel/SwiftKeychainWrapper
It supports all versions of swift.
The following should work just fine:
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"YourAppLogin" accessGroup:nil];
[keychainItem setObject:#"password you are saving" forKey:kSecValueData];
[keychainItem setObject:#"username you are saving" forKey:kSecAttrAccount];
If your app has an associated domain it's better to use Managing Shared Credentials. It will provide the best experience for the user and it's managed by the system itself.
Step 1:
Set up your Associated Domains with webcredentials key
Step 2
Mark your text fields
userIdTextField.textContentType = .username
passwordTextField.textContentType = .password
Step 3
Use the following code to save the details whenever the user successfully login. iOS will show a confirmation action sheet for the user to save the password. And next time the user tries to login keyboard will suggest the user credentials for your app.
SecAddSharedWebCredential("www.example.com" as CFString, "user_id" as CFString, "password" as CFString) { error in
if error != nil {
// Handle error
return
}
// The credentials have been successfully saved.
}
Now you're ready to go.
Read more...?!