Cocoa interface to MacOS X Keychain - objective-c

I've got a bit of Mac code that needs to store, access and update passwords in order to connect users with a web API. The right place to put this information should be the Mac Keychain, but there doesn't seem to be a cocoa interface (see this answer) -- is this still correct?
I've looked at Apple's Keychain documentation, and the API seems incredibly clunky. I can store to it and retrieve records, but anything more complex seems to require a lot of thought as to what might go wrong (see this list of error codes).
Is there a better interface to the Mac keychain, aside from slogging through the C code? The closest I've come is EMKeychain but it seems like it needs a bit of work (e.g. no error handling code aside from spitting to the console).

You should take a look at SSKeychain. Works great, awesome code.

Too late answer but would be good for future help. Below is what I did to save the password in Keychain of Mac
#pragma -mark Password save in Keychain
-(NSURLProtectionSpace *)createProtectionSpaceForBasicAuthentication{
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:#"http://yourURLHere"
port:1804 //add Your port here
protocol:#"http" //can be pass as nil
realm:nil
authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
return protectionSpace;
}
-(void )createCredentialForUsername:(NSString *)username Password:(NSString *)password{
NSURLCredential *credentialObject;
credentialObject = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistencePermanent];
[[NSURLCredentialStorage sharedCredentialStorage] setCredential:credentialObject forProtectionSpace:[self createProtectionSpaceForBasicAuthentication]];
}
For saving password
- (IBAction)saveButtonClicked:(id)sender {
[self createCredentialForUsername:#"User_Name" Password:#"Your_Pass"];
}
for fetching the password
NSURLCredential *credential;
NSDictionary *credentials;
credentials = [[NSURLCredentialStorage sharedCredentialStorage] credentialsForProtectionSpace:[self createProtectionSpaceForBasicAuthentication]];
credential = [credentials.objectEnumerator nextObject];
NSLog(#"Username: %# and password %#", credential.user, credential.password);
When we run the app to fetch password, we will get user action prompt for keychain access.

Late to the party, as always, but I can recommend UICKeyChainStore.

Related

Prompt the user to authorise the application to share types in HealthKit

When first calling requestAuthorizationToShareTypes:readTypes:completion: of the HKHealthStore with the set of HKQuantityType I want permissions to, I can see this modal view requesting authorization from the user to read and share every type of object for which the application may require access.
I'm trying to figure out if there is a way to prompt the user this modal view, besides the first time i'm calling: requestAuthorizationToShareTypes:readTypes:completion:.
I tried calling the requestAuthorizationToShareTypes:readTypes:completion: every time with the same set but after the first call its not prompting anymore. When trying to change the set with a new type that wasn't there before I can successfully prompt this screen, but I don't think calling this method each time with a new HKQuantityType in the set is the right way (as there is a limit to the amount of types exists).
Is it even possible?
Thanks for any help whatsoever.
UPDATE
I'll add some code snippet of the call:
[self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {
if (!success) {
NSLog(#"You didn't allow HealthKit to access these read/write data types. The error was: %#.", error);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// Update the user interface based on the current user's health information.
});
}];
where writeDataTypesz and readDataTypes are NSSet returned from the following methods:
// Returns the types of data that I want to write to HealthKit.
- (NSSet *)dataTypesToWrite
{
HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
return [NSSet setWithObjects: heightType, weightType, nil];
}
// Returns the types of data that I want to read from HealthKit.
- (NSSet *)dataTypesToRead
{
HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
HKCharacteristicType *birthdayType = [HKCharacteristicType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];
HKCharacteristicType *biologicalSex = [HKCharacteristicType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
return [NSSet setWithObjects:heightType,weightType,birthdayType,biologicalSex, nil];
}
Changing the set to include NEW types each time I call requestAuthorizationToShareTypes:readTypes:completion: results in opening this view with all the types that I ever asked permissions to (not necessarily in this specific call):
By design, it is not possible to re-prompt the user for authorization. If you would like the user to consider changing their authorizations for your app, ask them to go to Settings or the Health app to do so.
By the time being we can't do it. However, it makes sense to be able to do so because switching the access rights is not as simple as tap the "ok" or "cancel" button. You may assume that everyone knows how to tap the switch and turn things on, but trust me, you'll be wrong if you assume that way.
I'd file a bug in apple's radar to let them know that their design is not flawless. We should be able to prompt the user again. If user is annoyed, he can always delete our app, and I feel it's much better than reading a really long "how to enable the access in Health.app" list and getting confused, and that's what a normal user would do.

I want to delete all items in my self created KeyChain on Mac OS X

I'm writing a little tool to synchronize passwords. I'm using my own KeyChain for this purpose. Prior to saving, I want to clear this KeyChain. However, it seems I don't understand how to use the SecItemDelete function.
NSMutableDictionary *deleteQuery = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
kSecClassGenericPassword, kSecClass,
kSecMatchLimit, kSecMatchLimitAll, nil];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
NSLog(#"%#", SecCopyErrorMessageString(status, NULL));
This is what I've written so far, but unfortunately my items (called Root.Foo and Root.Bar) remain in the KeyChain. Also I'm wondering, how this function knows, which KeyChain should be searched? Most examples I'm fonding are about iOS, where every Application has it own KeyChain by default.
Thanks for any help :)
Solved it:
I've missed passing in an array of KeyChains to look for! It seems on iOS, always the default KeyChain of an app is used but on Mac OS you need to specify the KeyChain, as an array containing SecKeychainRefs:
NSMutableDictionary *q = [NSMutableDictionary dictionary];
[q setObject:kSecClassGenericPassword forKey:kSecClass];
[q setObject:[NSArray arrayWithObject:(__bridge id)keyChain] forKey:kSecMatchSearchList];
[q setObject:kSecMatchLimitAll forKey:kSecMatchLimit];
SecItemDelete((__bridge CFDictionaryRef)q);
This code worked perfectly.

Is it possible to load an SSL-encrypted website with a self-signed security certificate in an iPhone application?

This is a question that seems to be floating around all over the place, but so far I haven't been able to find a definite answer.
I'm tasked with writing an iPhone application for my company. My company has an SSL-encrypted website with a self-signed certificate. This website is used very frequently, and my employer would like for the site to be easily accessible on an iPhone. My app's function is to provide a method of storing the credentials used to access the site, and when the user opens the app, it automatically sends the stored credentials to the site and skips the login dialog to go straight into the site.
I've set up my app to use a UIWebView and load a request for the site. I've set up the usual authentication methods, such as:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {...}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {...}
When my app reaches the part of the code where I tell the UIWebView to load the NSURLRequest, it doesn't encounter any of my authentication methods. It jumps straight to didFailLoadWithError and the error tells me that I'm connecting to a site that may only be pretending to be my site because it has a self-signed certificate.
How do I get around this? I've found a few answers involving the NSURLConnection methods, but those methods don't appear to be called before I reach the error and am forced to stop loading the page. I've also found some answers involving overriding "undocumented" methods, but when I try implementing that solution my web view never reaches either "didFailLoadWithError" or "didFinishLoad" and never displays any content.
The UIWebKit delegate doesn't forward through any of the NSURLConnection delegate methods to your app. One way to get around this would be to load the page using NSURLConnection and then push it into the UIWebView using -loadData:MIMEType:textEncodingName:baseURL:. Once you've done that you've verified the first page, which (as long as your site doesn't have links off of it), should stay safe. So, how do we verify a self-signed certificate?
I had to solve this with an OSX App a little earlier this year and, once I figured out what I was doing, it was pretty straightforward, assuming you have a similar setup. The solution I propose here actually verifies the server certificate (although in my case I was using a private CA, so I added the CA certificate to the trust root, instead of the server certificate, it should work just as well with that).
You'll need to add tests for NSURLAuthenticationMethodServerTrust to both the -connection:canAuthenticateAgainstProtectionSpace: and -connection:didReceiveAuthenticationChallenge: methods so that you can both request interest in and process the Security challenge.
Hope this helps.
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
// ... implement any other authentication here, such as your client side certificates or user name/password
if ([[protectionSpace authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
return YES;
return NO;
}
-(void)connection:(NSURLConnection *)aConnection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
// implement your client side auth here for NSURLAuthenticationMethodHTTPDigest or basic or your client-side cert
if ([[protectionSpace authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
// install our private certificates or CAs
NSString *myCAString = #"<string containing your CA cert>";
SecCertificateRef certRef = SecCertificateCreateWithData ( NULL, (CFDataRef)[NSData dataFromBase64String: myCAString]);
NSArray *anchorArray = [NSArray arrayWithObject: (NSObject*)certRef];
SecTrustSetAnchorCertificates( challenge.protectionSpace.serverTrust, (CFArrayRef) anchorArray);
SecTrustResultType trustResultType;
OSStatus err=SecTrustEvaluate(challenge.protectionSpace.serverTrust, &trustResultType);
if ((!err) && (trustResultType == kSecTrustResultProceed || trustResultType == kSecTrustResultConfirm || trustResultType == kSecTrustResultUnspecified)) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
} else {
CFArrayRef certChain=NULL;
CSSM_TP_APPLE_EVIDENCE_INFO *statusChain;
SecTrustGetResult( challenge.protectionSpace.serverTrust, &trustResultType, &certChain, &statusChain);
[challenge.sender cancelAuthenticationChallenge:challenge];
}
} else {
// Cancel if we don't know what it is about... this is supposed to be secure
[challenge.sender cancelAuthenticationChallenge:challenge];
}
}
I have managed to do what you desire by using RestKit framework, so yes, I can confirm it can be done. Go see in the RestKit sources how they do it, or just use RestKit. This says how to use SSL with RestKit. In my case, there was one catch - the self signed certificate had to contain the certain extensions, otherwise I always got the kSecTrustResultRecoverableTrustFailure. Go see here, the answer is voted -1 but it actually fixed the problem for me.

Get list of installed apps on iPhone

Is there a way (some API) to get the list of installed apps on an iPhone device.
While searching for similar questions, I found some thing related to url registration, but I think there must be some API to do this, as I don't want to do any thing with the app, I just want the list.
No, apps are sandboxed and Apple-accepted APIs do not include anything that would let you do that.
You can, however, test whether a certain app is installed:
if the app is known to handle URLs of a certain type
by using [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"thisapp://foo"]
You can get a list of apps and URL schemes from here.
For jailbroken devices you can use next snipped of code:
-(void)appInstalledList
{
static NSString* const path = #"/private/var/mobile/Library/Caches/com.apple.mobile.installation.plist";
NSDictionary *cacheDict = nil;
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath: path isDirectory: &isDir] && !isDir)
{
cacheDict = [NSDictionary dictionaryWithContentsOfFile: path];
NSDictionary *system = [cacheDict objectForKey: #"System"]; // First check all system (jailbroken) apps
for (NSString *key in system)
{
NSLog(#"%#",key);
}
NSDictionary *user = [cacheDict objectForKey: #"User"]; // Then all the user (App Store /var/mobile/Applications) apps
for (NSString *key in user)
{
NSLog(#"%#",key);
}
return;
}
NSLog(#"can not find installed app plist");
}
for non jailbroken device, we can use third party framework which is called "ihaspp", also its free and apple accepted. Also they given good documentation how to integrate and how to use. May be this would be helpful to you. Good luck!!
https://github.com/danielamitay/iHasApp
You could do this by using the following:
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector = NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSMutableArray *Allapps = [workspace performSelector:selectorALL];
NSLog(#"apps: %#", Allapps);
And then by accessing each element and splitting it you can get your app name, and even the Bundle Identifier, too.
Well, not sure if this was available back when the last answer was given or not (Prior to iOS 6)
Also this one is time intensive, yet simple:
Go into settings > Gen. >usage. The first category under usage at least right now is Storage.
It will show a partial list of apps. At the bottom of this partial list is a button that says "show all apps".
Tap that and you'll have to go through screen by screen, and take screenshots (Quick lock button and home button takes a screenshot).
I'm doing this now and I have hundreds of apps on my iPhone. So it's going to take me a while. But at least at the end of the process I'll have Images of all my apps.

Objective C - Webkit external files causing 401

I'm currently using Webkit to display a mobile web on my iPhone app.
The domain is password protected, so I pass the username & password to the request.
It loads the main HTML page fine, however, even after passing the username & password, any other pages that the main HTML page loads (i.e css and js) are all returning 401.
Is there a way around that?
Thanks,
Tee
This is one of the use cases for ASIWebPageRequest.
If you want something lower level, you'll have to create an NSURLCredential instance and an NSURLProtectionSpace and save them to the credential store. At this point, the UIWebView should use the credential for all requests that match the protection space (which basically represents your site).
Here's some sample code, taken from here, which is not really where I'd expect to find it.
NSURLCredential *credential = [[NSURLCredential alloc]
initWithUser: #"userName"
password: #"password"
persistence: NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost: #"www.mydomain.com"
port: 80
protocol: #"http"
realm: #"mydomain.com"
authenticationMethod: NSURLAuthenticationMethodDefault];
[[NSURLCredentialStorage sharedCredentialStorage]
setDefaultCredential: credential
forProtectionSpace: protectionSpace];
[credential release];
[protectionSpace release];
i think you need to pass the username and password in the css and js includes also...