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...?!
Related
What is the best practice to store sensitive(not secure) data on iOS devices?
Where should I store information that user bought something in app? For example where should I store BOOL showAds variable if user bought "Remove Ads"?
I do understand that everything breakable, especially on jailbroken devices, I just asking what is the best practice.
My variants:
.plist in App Documents -- Editable using iFunBox, for example
NSUserDefaults -- Same here, I guess
Keychain -- best variant in my opinion so far
You can store you data in NSUserDefaults using base 64 encoding data to keep safe it.
The code is very simple:
NSUserDefaults *persistValues;
persistValues = [NSUserDefaults standardUserDefaults];
To set data (encoding it using base 64):
// Create NSData object
NSData *nsdata = [#"iOS Developer Tips encoded in Base64" dataUsingEncoding:NSUTF8StringEncoding];
// Get NSString from NSData object in Base64
NSString *base64Encoded = [nsdata base64EncodedStringWithOptions:0];
[persistValues setObject:base64Encoded forKey:#"some_key"];
To get data:
base64Encoded = [persistValues stringForKey:#"some_key"];
NSData *nsdataFromBase64String = [[NSData alloc]
initWithBase64EncodedString:base64Encoded options:0];
// Decoded NSString from the NSData
NSString *base64Decoded = [[NSString alloc]
initWithData:nsdataFromBase64String encoding:NSUTF8StringEncoding];
And if you data is bigger I suggest you uses web services and store it in a web server
Keychain is best option to store sensitive data.
Other than sensitive data below are 2 options :
Small data can be stored in NSUserDefault as its best way as physical file will not be available for changing data.
Bigger data can be stored in database with encryption
I have the first part figured out...
- (void)webViewDidFinishLoad:(UIWebView*) myTapView {
NSString *jsString = #"localStorage.getItem('username');";
NSString *someKeyValue = [tapView stringByEvaluatingJavaScriptFromString:jsString];
// This is where I would start storing it so I can retrieve it later
}
So here is my issue, I need to store the value I just got from localStorage so I can use it again. Problem is I don't know where to start. This code is inside my ViewController for the WebView, so I need to be able to use it in other areas in the ViewController.m file.
What is the best method for storing the info? I would also want the above code to be able to update the value, as it won't always be the same username (people can switch accounts).
There are a few ways to store data on iOS, but it depends on what kind of value you want to store. If it's just a plain string or other "light" data, the best approach is to save it to NSUserDefaults. If it's an image or video, you may want to consider archiving it using NSKeyedArchiver. Otherwise, if you want to build a complicated data model, use Core Data.
Here's a good post on NSHipster that describe in detail pros and cons of each approach.
According to your code, you want to store a string. I'd go with NSUserDefaults in this case.
Use NSUserDefaults to store UIWebView local storage value as follows
- (void)webViewDidFinishLoad:(UIWebView*) myTapView {
NSString *jsString = #"localStorage.getItem('username');";
NSString *someKeyValue = [myTapView stringByEvaluatingJavaScriptFromString:jsString];
// Store your Username in iPhone persistence variable and use it later in the application
NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];
[userdefault setObject:someKeyValue forKey:#"username"];
[userdefault synchronize];
//use of User default
NSLog(#"User name %#",[userdefault valueForKey:#"username"]);
}
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
I am developing an application with keychain implementation . i am able to create & Save data into keychain . I am using the Keychain Wrapper classes provided By Apple.
According to requirement , I have to implement best possible Security in the KeyChain (The security team pointed out lapses , such as it's accessibility on Jail-broken devices).
Could Someone give me direction?
I had also Implemented keychain in application long Back using the same Wrapper you cited , but , of course with a lot of modifications.
Basically Keychain is quite secure .According to Apple , it's an encrypted container that holds secure information for multiple applications ,which means that when the keychain is locked, no one can access its protected contents .
In iOS , only the application creating the keychain can access it.
According to Apple's documentation , iOS can choose to Memory-Cache or Disk Cache it.
But from iOS 4.xx++ , it's only disk-cached(dunno why) , thus always creating a
sqlite DB , where all the data in the keychain are stored corresponding to a particular Identifier.
The Sqlite DB Can be Hacked on rooted or Jail-broken devices.
To Secure the Keychain
1 Add the security keyword "kSecAttrAccessibleWhenUnlockedThisDeviceOnly" while adding or
updating the data in keychain on the methods "SecItemUpdate" & "SecItemAdd".
Something like :-
- (void)writeToKeychain
{
NSDictionary *attributes = NULL;
NSMutableDictionary *updateItem = NULL;
OSStatus result;
if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr)
{
updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
[updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];
NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
[tempCheck removeObjectForKey:(id)kSecClass];
#if TARGET_IPHONE_SIMULATOR
[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];
#endif
[updateItem setObject:(id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly forKey:(id)kSecAttrAccessible];
result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
NSAssert( result == noErr, #"Couldn't update the Keychain Item." );
CFRelease(attributes);
}
else
{
[keychainItemData setObject:(id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly forKey:(id)kSecAttrAccessible];
result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, #"Couldn't add the Keychain Item." );
}
}
2 Encrypt the data before Adding to the Keychain .I used AES-128 Encryption.
Also ensure that the key used for Encryption is RSA key.(sent by SSL Web Service ).
NOTE :-The Keychain Data is stored in the /private/var/Keychains/keychain-2.db file on the iPhone.
Hope it helps you.
[attributeDict setObject:(__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
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.