How to add any NSString data in Keychain in iOS - ios

I want to add some data in the Keychain in iOS. I am not worried about the security just wanted to store some string permanently some where(Keychain) which can be consistent even if user uninstalled the application. I am not storing any password, all the example in the Web are only showing how to store the password. I am planning to use the kSecClassKey attribute to store the string. Please guide me in the correct direction. Any sample code will be very helpful.

I've used SFHF Keychain lib ( https://github.com/kamiro/SFHFKeychainUtils ). It's really easy to use and works fine.
Here an example to use
NSString* username = #"myValue1";
NSString* service = #"com.organization.appname.myValue1";
NSString* valueToStore = #"....";
// Add/Update valute to keychain
NSError* anyStoringError = NULL;
BOOL stored = [SFHFKeychainUtils storeUsername:username andPassword:valueToStore forServiceName:service updateExisting:YES error:&anyStoringError];
// Get value from keychain
NSString *storedValue = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:service error:&anyStoringError];
// Remove value from keychain
BOOL valueRemoved = [SFHFKeychainUtils deleteItemForUsername:username andServiceName:service error:&anyStoringError];
Hope it helps

Related

Why does SecPKCS12Import automatically add SecIdentities to the Keychain?

The documentation on SecPKCS12Import states the following:
[…] You can then use the Keychain Services API (see Keychain Services
Reference) to put the identities and associated certificates in the
keychain.
This means that the items returned in the “items” argument (3rd argument of that function) should not be automatically added to the keychain. However, I have found that those items are automatically added to the keychain when using that function. If I try to add them using SecItemAdd, I get errSecDuplicateItem.
Is this a bug or should it be this way? Why are the items automatically added?
Here is some sample code:
NSDictionary *options = [[NSDictionary alloc] initWithObjectsAndKeys:#"password", (id)kSecImportExportPassphrase, nil];
CFArrayRef items_ = NULL;
OSStatus ret = SecPKCS12Import((CFDataRef)pkcs12data /* get this from somewhere … */, (CFDictionaryRef)options, &items_);
If you use that code and then open Keychain Access, you’ll see that the certificate and the private key have been added to the keychain.
Regards,
David.
It seems like Apple's documentation may be out of date for that link (SecPKCS12Import), because this link https://developer.apple.com/library/ios/qa/qa1745/_index.html mentions that "reading in a PKCS#12-formatted blob and then importing the contents of the blob into the app's keychain using the function SecPKCS12Import..."
Going by the document revision dates, QA1745 is more recent than the Certificate, Key, and Trust Services Reference.
Is this a bug or should it be this way?
It isn't a bug, the documentation is only incorrect. Well, it is correct for iOS, it is just incorrect for macOS.
Why are the items automatically added?
This is caused by the way how this function is implemented in macOS. The comments in the implementation reveal the cause:
// SecPKCS12Import is implemented on Mac OS X in terms of the existing
// SecKeychainItemImport API, which supports importing items into a
// specified keychain with initial access control settings for keys.
SecPKCS12Import in macOS is just a wrapper around SecKeychainItemImport and as the function name implies, this function imports into a keychain. This also explains the following code:
if (!importKeychain) {
// SecKeychainItemImport requires a keychain, so use default
status = SecKeychainCopyDefault(&importKeychain);
}
On iOS the function is implemented standalone and not as a wrapper since SecKeychainItemImport isn't even available on iOS.
But there is a way round that if that is a problem for you. Many people work around it by creating a temporary keychain, which will never be visible to the system or the user (and thus also not be visible to apps or in Keychain Access), of course, this will work too, but is a bit of an ugly hack.
Better: Use SecItemImport and as import format use kSecFormatPKCS12, then you also get the parsed identities but nothing is imported anywhere unless you request that.
SecPKCS12Import does NOT add items to the keychain. However, it WILL look in the keychain to see if imported items are already there. If it finds existing items, they will be returned for the SecIdentityRef (SecCertificateRef and SecKeyRef). This is why you can get errSecDuplicateItem when calling SecItemAdd after calling SecPKCS12Import.
When debugging, you might want to remove everything in your keychain using code like this:
void _EraseKeychain()
{
NSMutableArray *toDelete = [NSMutableArray array];
NSArray *classes = #[(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassGenericPassword];
NSMutableDictionary *query = [NSMutableDictionary dictionary];
query[(id)kSecClass] = (__bridge id)kSecClassIdentity;
query[(id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
query[(id)kSecReturnPersistentRef] = #YES;
id class;
for( class in classes )
{
query[(__bridge id)kSecClass] = class;
CFTypeRef items = nil;
OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)query, &items);
if( result == errSecSuccess )
{
[toDelete addObjectsFromArray:(__bridge NSArray*)items];
CFRelease(items);
}
}
id deleteRef;
for( deleteRef in toDelete )
{
NSString *objectKind = #"unknown";
if( CFGetTypeID(deleteRef) == CFDataGetTypeID() )
{
objectKind = [[NSString alloc] initWithUTF8String:(char *)[(__bridge NSData*)deleteRef bytes]];
}
NSDictionary *delRequest = #{(id)kSecValuePersistentRef:deleteRef};
OSStatus deleteResult = SecItemDelete((__bridge CFDictionaryRef)delRequest);
if( deleteResult == errSecSuccess )
NSLog(#"Deleted item(%#) with persistent ref %#", objectKind, deleteRef);
else if( deleteResult == errSecItemNotFound )
NSLog(#"Already deleted item(%#) with persistent ref %#", objectKind, deleteRef);
else
NSLog(#"Can't delete keychain item(%#) with persistent ref %#", objectKind, deleteRef);
}
}

What is the best way to store a local player's high score

I want to store a local player's high score in the iOS game. What is the best way to approach it? Should I use NSUserDefaults in this case? Is it safe enough to store a high score?
Thanks in advance.
NSUserDefaults is not correct place to store this kind of information. The best way is to hide the user score in Keychain, so that noone can crack it (perhaps the game score is relevant to your monetization idea).
Other good thing is that Keychain is seamless syncable via iCloud, "it's just works".
The best way to store player's high score is to use following code in your project.
Following method will help you to save some value in Keychain:
- (void) setValue: (NSData *) value forAccount: (NSString *) account service: (NSString *) service {
NSDictionary *searchDict = #{
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
(__bridge id) kSecMatchLimit: (__bridge id) kSecMatchLimitOne,
(__bridge id) kSecAttrService: service,
(__bridge id) kSecAttrAccount: account,
(__bridge id) kSecReturnData: #YES,
};
CFDataRef keyData = NULL;
SecItemCopyMatching ((__bridge CFDictionaryRef) searchDict, (CFTypeRef *) &keyData);
if (keyData) {
CFRelease (keyData);
NSDictionary *removeDict = #{
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
(__bridge id) kSecAttrService: service,
(__bridge id) kSecAttrAccount: account,
};
SecItemDelete ((__bridge CFDictionaryRef) removeDict);
}
if (value) {
NSDictionary *writeDict = #{
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
(__bridge id) kSecAttrService: service,
(__bridge id) kSecAttrAccount: account,
(__bridge id) kSecValueData: value,
};
SecItemAdd ((__bridge CFDictionaryRef) writeDict, NULL);
}
}
where value is your user score archived into NSData
to pack integer into NSData:
NSUInteger score = <some number>;
NSData *dataValue = [NSData dataWithBytes:&score length:sizeof(score)];
to unpack:
NSUInteger score;
[dataValue getBytes:&score length:sizeof(score)];
account - is the name of your value, for example #"userScore", or score for certain user #"ForzenHeart_Score"
service - is the name of your application, you can use your bundle ID [[NSBundle mainBundle] bundleIdentifier]
To get saved data from Keychain you use this method:
- (NSData *) valueForAccount: (NSString *) account service: (NSString *) service {
NSDictionary *searchDict = #{
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
(__bridge id) kSecMatchLimit: (__bridge id) kSecMatchLimitOne,
(__bridge id) kSecAttrService: service,
(__bridge id) kSecAttrAccount: account,
(__bridge id) kSecReturnData: #YES,
};
CFDataRef keyData = NULL;
SecItemCopyMatching ((__bridge CFDictionaryRef) searchDict, (CFTypeRef *) &keyData);
return (__bridge_transfer NSData *) keyData;
}
And don't forget to wipe all user related data when you have to, simply by passing nil as value parameter for -(void)setValue... method listed above.
NSUserDefaults is not safe because the defaults are actually keyed into a .plist file stored locally on the device in your application folder. This is the directory: .../YourApp.app/Library/Preferences/appBundleName.plist. This can very easily be modified and viewed with tools on and off the device, and no jail-break is required for off-device viewing. As other's have suggested, use the iOS Keychain for it's added security. Also, here is an open-source library for secure defaults in iOS: Secure-NSUserDefaults.
To answer your question directly, it is not safe enough for any sensitive data that you do not want the user modifying indirectly. NSUserDefaults can easily be accessed and modified with little effort. Do not store encrypted data in NSUserDefaults because that too can easily be accessed as well. The attacker won't easily be able to decrypt the data but they can still access it nonetheless with ease. The iOS Keychain is also not very secure either because the contents can be dumped easily too but not decrypted. For your purposes though, the iOS Keychain is fine.
NSUSerDefaults in never safe.
Either use iOS Keychain or upload the data to your server (the most reliable way)
No NSUserDefaults is not safe.You can use Keychain.
The contents of NSUserDefaults are stored in plain text. They can be accessed and modified with tools like iExplorerwihout the jailbreak.
If that is not the issue you can go for NSUserDefaults
Generally, NSUserDefaults should only contain settings that can be changed by the user. Internal data of your app should not be stored in NSUserDefaults.
Use iOS Keychain to store really secure data.
Alternatively, you could use a cryptographic algorithm (e.g. AES) to encrypt the data you store in NSUserDefaults, or save it in a dictionary and write it to a file.
Do not use NSUserDefaults to hold anything locally. It is not secure. You should store score in the keychain where it is encrypted, there's small wrapper class that makes saving username/password into the keychain very easy:
Click here to Know more
I often choose coredata something like these situations. There are lots of handy & safe framework about this. Nowadays I use MagicalRecord framework. It's useful and commonly used framework.
You just create your model and decare magical record framework to your AppDelegate.m file. And anywhere you want save data properly.

unable to do login valiadtions in ios

i'm new to IOS.
can anyone tell me how to do validations for username and password by comparing text entered to the data in plists?
(i have created 2 arrays username and password in plist manually)
any help is appreciated.
Hello and welcome to StackOverflow. I would recommend you follow this link https://stackoverflow.com/help/how-to-ask in order to better understand how a question should be asked here. In any case, from what I understood from your question, here's something to get you started.
Suppose you have a plist called authentication.plist which has two fields of type string (I know you wrote arrays but couldn't understand why. If you are determined to use them the link posted by Kumar Kl explains how to read them from the plist):
username
password
You read the plist file into an NSDictionary using:
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"authentication" ofType:#"plist"]];
Let's suppose you have the username entered by the user in an NSString called enteredUsername and the password in an NSString called enteredPassword.
You could then compare the entered values with the ones in the plist like so:
if ([enteredUsername isEqualToString:[dictionary objectForKey:#"username"]])
{
NSLog(#"Username matches!");
}
else
{
NSLog(#"Username does not match");
}
if ([enteredPassword isEqualToString:[dictionary objectForKey:#"password"]])
{
NSLog(#"Password matches!");
}
else
{
NSLog(#"Password does not match");
}

Why SSKeychain returns different result with the same parameters for different iOS apps

I tried to use SSKeychain to reserve UUID on iOS
and the sample code is below
NSString *retrieveuuid = [SSKeychain passwordForService:#"tempApp" account:#"tempUser"];
if (retrieveuuid == nil) {
//Generate UUID
CFUUIDRef cfuud = CFUUIDCreate(kCFAllocatorDefault);
NSString *uuid = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
//save in keychain
[SSKeychain setPassword:uuid forService:#"tempApp" account:#"tempUser"];
return uuid;
} else {
return retrieveuuid;
}
My question is that I run the first app and then generate a UUID, and then run the second app which
has the same parameters, so that the retrieveduuid should not be null, then why the second App returns a different UUID? I think that will return the same
UUID as first app because I have saved the UUID in the keychain in App1 and try to retrieve it by the same parameters in App2.
Thanks for help
UUID is a Universally Unique IdenTifier. Would be kind of stupid if you got the same in two different apps, wouldn't it? ;)
If you want to share keychains between apps, see this question: How to share keychain data between iOS applications

Keychain iOS not always storing values

I´m developing a couple of iOS applications and i need to share a element between them, that i want to store in the keychain.
This element is used in a complex login process with 3 or 4 steps, in each one i need to read the value from the keychain, to do this i used the code bellow:
- (NSString *)installationToken
{
KeychainItemWrapper *kw = [[KeychainItemWrapper alloc] initWithIdentifier:#"uuid" accessGroup:#"yyyyy.xxxxxxxxxxx"];
if (![kw objectForKey:(NSString*)kSecAttrAccount] || [[kw objectForKey:(NSString*)kSecAttrAccount] isEqualToString:#""]) {
NSString *result;
CFUUIDRef uuid;
CFStringRef uuidStr;
uuid = CFUUIDCreate(NULL);
assert(uuid != NULL);
uuidStr = CFUUIDCreateString(NULL, uuid);
assert(uuidStr != NULL);
result = [NSString stringWithFormat:#"%#", uuidStr];
assert(result != nil);
CFRelease(uuidStr);
CFRelease(uuid);
[kw setObject:result forKey:(NSString*)kSecAttrAccount];
return result;
} else {
return [kw objectForKey:(NSString*)kSecAttrAccount];
}
}
This all works well in almost every device but in some, users are complaining. So, i checked what my server is receiving, and saw that different values are being sent.
I checked the code and in no other place i'm acessing/emptying this keychain element, what can be wrong with this? For the majority of devices this works like a charm but for some reason, in some devices, they aren't storing or retrieving well from the keychain.
The problem happens in different invocation in the same application.
If you are using Apples' sample code for KeyChainWrapper, then main problem is sometimes randomly, SecItemCopyMatching fails and then the sample code has resetKeychainItem which will basically reset your keychain.
if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)
{
// Stick these default values into Keychain if nothing found.
[self resetKeychainItem];
}
In our app, we noticed similar problems, and so now we are using
https://github.com/carlbrown/PDKeychainBindingsController to do all keyChain related functionality. Now it works very well.

Resources