SSL Certificate confusion - ios

I am clearly missing the Aha! moment because I have been doing research on using CA or Self signed Certificates and such for gaining access to a secure URL https and I am still having trouble fully understanding it, I am mostly just blundering about with other people code and solutions to try and get mine working, i am clearly lacking a fundamental understanding so hopefully the denizens of the site can help.
Essentially, i have an app that communicates, with a https server using a self signed certificate.
What I believe to be the certificate and key needed to access the server are both stored in a p12 stored in the root bundle of the app. I then add this p12 to the phones or apps keychain through this code I found on the internet
NSString *p12Path = [[NSBundle mainBundle] pathForResource:p12Name ofType:#"p12"];
NSData *p12Data = [[NSData alloc] initWithContentsOfFile:p12Path];
NSError *error = nil;
NSData *data = [[SFHFKeychainUtils getPasswordForUsername:[[UIDevice currentDevice]name] andServiceName:serviceName error:&error] dataUsingEncoding:NSUTF8StringEncoding];
[self transform:data];
NSString *pass = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
CFStringRef password = (__bridge CFStringRef)pass;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef p12Items;
OSStatus result = SecPKCS12Import((__bridge CFDataRef)p12Data, optionsDictionary,&p12Items);
if(result == noErr)
{
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);
SecCertificateRef certRef;
SecIdentityCopyCertificate(identityApp,&certRef);
SecCertificateRef certArray[1] = { certRef };
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
CFRelease(certRef);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:(__bridge NSArray *)myCerts persistence:NSURLCredentialPersistencePermanent];
CFRelease(myCerts);
[[RKObjectManager sharedManager].HTTPClient setDefaultCredential:credential];
}
And, well this seems to work, But i have to have this enabled
_httpClient = [RKObjectManager sharedManager].HTTPClient;
[_httpClient setAllowsInvalidSSLCertificate:YES];
Otherwise it does not connect, Now I have seen various posts saying, you need to have this set to yes to allow self signed certs, but at the same time i have seen other posts saying that it should only be for development as otherwise this makes using https entirely redundant. And obviously redundant security is bad so i set it to no...and it does not connect.
So Really does anyone have any links or could spare some time themselves filling up the gaps in my knowledge on how this stuff works? it would be much appreciated and would save me getting grief from the boss.

This needs to be handled by AFNetworking and is called 'SSL Pinning'. Check the docs here for details on how to enable the feature and supply your certificate. There is also useful information here.

Related

iOS Connect to Wi-Fi via EAP-TTLS and EAP-PEAP

I will briefly explain to you what problem I'm having.
I spent a lot of time to find the solution but without any positive result.
So I hope that you can help me.
Our app is used to connect to a secure network via NEHotspotConfiguration.
NEHotspotConfiguration *configuration = [[NEHotspotConfiguration alloc] initWithSSID:networkName eapSettings:settings];
I configured NEHotspotEAPSettings with: username, password, outerIdentity, identity, supportedEAPTypes (NEHotspotConfigurationEAPTypeEAPTTLS and NEHotspotConfigurationEAPTypeEAPPEAP), trustedServerNames and trustedServerCertificates (client.cer and rootCA.cer).
Those certificates were generated by the guys from the server side and are incorporated in the iOS project via xcode.
I have checked if those certificates are valid. For example, in the debuger logs I can see the expiration date.
In the final step when the alert message appears that the app wants to join the secure network, after pressing the join button we see the message "Unable to join the network".
In the debuger logs I see the following:
NEHotspotEAPSettings failed to find persistent reference for identity. status = -25300
NEHotspotEAPSettings failed to find persistent reference for trusted server certificate. status = -25300
NEHotspotEAPSettings found nil persistent reference for identity
Do you have any ideas on how to fix it or what is wrong?
Thank you in advance!
This is the code for successful authentication
NEHotspotEAPSettings *eapSettings = [[NEHotspotEAPSettings alloc] init]; eapSettings.username = #"username"; eapSettings.password = #"password"; eapSettings.supportedEAPTypes = [NSArray arrayWithObjects:[NSNumber numberWithInteger:NEHotspotConfigurationEAPTypeEAPPEAP], nil]; eapSettings.outerIdentity = #""; eapSettings.trustedServerNames = #[#"xxx.xxx.com"]; NEHotspotConfiguration *hotspotConfig = [[NEHotspotConfiguration alloc]initWithSSID:#"ssidName" eapSettings:eapSettings]; [[NEHotspotConfigurationManager sharedManager] applyConfiguration:hotspotConfig completionHandler:^(NSError * _Nullable error) { NSLog(#"success"); }];
This is the code for authentication failure
`NSString *caCertificatePath = [[NSBundle mainBundle] pathForResource:#"caCertificate" ofType:#"cer"];
NSData *caCertificateData = [[NSData alloc] initWithContentsOfFile:caCertificatePath];
SecCertificateRef caCertificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)caCertificateData);
SecCertificateRef userCertificate = nil;
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"userCertificate" ofType:#"cer"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef password = CFSTR("password");
const void *keys[] = { kSecImportExportPassphrase }; };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
CFRelease(options);
CFRelease(password);
if (securityError == errSecSuccess) {
NSLog(#"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
userCertificate = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
NEHotspotEAPSettings *eapSettings = [[NEHotspotEAPSettings alloc] init];
eapSettings.tlsClientCertificateRequired = YES;
eapSettings.trustedServerNames = #[#"xxx",#"xxx.xxx.com"];
eapSettings.supportedEAPTypes = [NSArray arrayWithObjects:[NSNumber numberWithInteger:NEHotspotConfigurationEAPTypeEAPTLS], nil];
eapSettings.outerIdentity = #"xxxx";
[eapSettings setTrustedServerCertificates:#[(__bridge id)caCertificateRef]];
[eapSettings setIdentity: userCertificate];
NEHotspotConfiguration *hotspotConfig = [[NEHotspotConfiguration alloc]initWithSSID:#"ssidName" eapSettings:eapSettings];
[[NEHotspotConfigurationManager sharedManager] applyConfiguration:hotspotConfig completionHandler:^(NSError * _Nullable error) {
NSLog(#"failure");//NEHotspotEAPSettings failed to find persistent reference for identity. status = -25300
}];
} else {
NSLog(#"Error opening Certificate.");
}
`
The difference between the two is that I want to use certificate authentication not username password authentication
By the way, the test device is iphone12pm, the version is ios16.2, the controller is Cisco.
1、Check the Apple Developer website
2、No valid information in Google Bing and other website searches

iOS: Having problems with self-signed certificate. SSL Handshake Error (-9824)

I have an application where I need to perform sort of a 2-Step-Authentication and long story short I'm getting per-user base64 encoded pem format certificates from the server and using them on each request.
First I generate a key pair, make a CSR, give them the CSR, they give me the certificate and this is where I have to use it and fail. I'm getting the following errors in the console for each individual request:
CFNetwork SSLHandshake failed (-4)
CFNetwork SSLHandshake failed (-9824)
CFNetwork SSLHandshake failed (-9824)
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
My approach is the following:
-grab the DER encoded data from the PEM formatted signed certificate they are sending me
-make a SecCertificateRef which I add to the keychain
-query for the SecIdentityRef in the keychain by label
-I then do some mostly needless stuff like grab the SecCertificateRef and private key from the identity mostly to be sure what's going on
-I also insert a CA Certificate which I have from the server and grab a reference to it from the keychain (not sure if I need to use it for the credential but I tried with or without it - the result was the same)
-I then initialize the credential with the identity and my certificates and use it when I get a NSURLAuthenticationMethodClientCertificate auth method (I don't do the check but that's all I get besides the server trust).
So up to this point nothing is NULL, everything gets initialized and looks good but the requests don't succeed. When I try to use the server trust credential on all requests, I get through and don't get the error but my server is giving me a security error as it should. As soon as I use the custom credential for any challenge I get the above errors.
note: I know the code is messy and I shouldn't be inserting certificates on each request but it's still very early work in progress and that's not the problem since the refs get instantiated correctly
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
SSLConnectionWrapper *wrapper = [self wrapperForConnection:connection];
NSString *certStringBase64 = [[NSUserDefaults standardUserDefaults] SSLCertificateForUserWithID:wrapper.userID];
NSData *certData = [[NSData alloc] initWithBase64EncodedString:certStringBase64 options:0];
NSString *certString = [[NSString alloc] initWithData:certData encoding:NSUTF8StringEncoding];
certString = [certString stringByReplacingOccurrencesOfString:#"-----BEGIN CERTIFICATE-----" withString:#""];
certString = [certString stringByReplacingOccurrencesOfString:#"-----END CERTIFICATE-----" withString:#""];
certString = [[certString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
//at this point certString contains the DER encoded certificate data
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)([[NSData alloc] initWithBase64EncodedString:certString options:kNilOptions]));
OSStatus err = SecItemAdd((__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id) kSecClassCertificate, kSecClass,
cert, kSecValueRef,
kCFBooleanTrue, kSecReturnPersistentRef,
[NSString stringWithFormat:#"CertLabel_UserID_%#", wrapper.userID], kSecAttrLabel,
nil], NULL);
const void *keys[] = { kSecClass, kSecReturnRef, kSecAttrLabel };
const void *values[] = { kSecClassIdentity, kCFBooleanTrue, (__bridge const void *)([NSString stringWithFormat:#"CertLabel_UserID_%#", wrapper.userID]) };
CFDictionaryRef queryForIdentityDict = CFDictionaryCreate(NULL, keys, values,
3, NULL, NULL);
SecIdentityRef identityKeychainRef = NULL;
OSStatus s = SecItemCopyMatching(queryForIdentityDict, (CFTypeRef *)&identityKeychainRef);
SecCertificateRef certKeychainRef = NULL;
OSStatus s2 = SecIdentityCopyCertificate(identityKeychainRef, &certKeychainRef);
SecKeyRef privateKey;
SecIdentityCopyPrivateKey(identityKeychainRef, &privateKey);
NSString *stringForCACert = [self stringForCACert];
SecCertificateRef caCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)([[NSData alloc] initWithBase64EncodedString:stringForCACert options:kNilOptions]));
OSStatus s3 = SecItemAdd((__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id) kSecClassCertificate, kSecClass,
caCert, kSecValueRef,
#"CACert", kSecAttrLabel,
nil], NULL);
const void *keys1[] = { kSecClass, kSecReturnRef, kSecAttrLabel };
const void *values1[] = { kSecClassCertificate, kCFBooleanTrue, #"CACert" };
CFDictionaryRef queryForCACert = CFDictionaryCreate(NULL, keys1, values1,
3, NULL, NULL);
SecCertificateRef caCertKeychainRef = NULL;
OSStatus s4 = SecItemCopyMatching(queryForCACert, (CFTypeRef *)&caCertKeychainRef);
NSURLCredential *credential = [[NSURLCredential alloc] initWithIdentity:identityKeychainRef certificates:#[ (__bridge id)certKeychainRef, (__bridge id) caCertKeychainRef] persistence:NSURLCredentialPersistencePermanent];
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}else{
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
}
It's an authentication challenge faced by server. You can bypass by following code (Using NSURLCOnnection)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host {
return YES;
}
Note: Don't use above if you uploading app on app store.
For iOS 9 above would not work, please edit plist as follows
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

iPhoneHTTPServer with SSL and certificate

I'm using the iPhoneHTTPServer sample into my project for using HTTPServer, and host a .plist file with a .ipa to simulate an ad-hoc deployment.
As you may know, since iOS7, the server which hosts files must be secured, and so I'm trying to use a SSL authentication, but it failed.
First, the server seems to start correctly, but it failed when I'm trying to access to my server like this:
NSURL *plistUrl = [NSURL URLWithString:#"itms-services://?action=download-manifest&url=https://localhost:8080/TestMAJ2.plist"];
[[UIApplication sharedApplication] openURL:plistUrl];
I have this error :
NSAssert(NO, #"Security option unavailable - kCFStreamSSLLevel" #" - You must use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMax");
How can I bypass this error? I was trying to removed the kCFStreamSSLLevel of the TLS settings (why not ? ^^), but the connection still don't work, I've got a popup with "Unable to connect to localhost" or something like that ...
About the SSL authentication, the DDKeychain class from the sample was not good because it's Mac's API, so I use this code: How to make iPhoneHTTPServer secure server, and the certificate come from Keychain Access, and it's the certificate that I used for signing my app. Maybe it's not the correct certificate ? Or the correct piece of code ? Do you know a very simple example of using SecureHTTPServer in iOS?
I have the SSL certificate working perfectly with a .html file. I am having problems with the .manifest and the .ipa files.
What I did for the SSL is creating MyHTTPConnection class that implements the following:
- (NSArray *)sslIdentityAndCertificates
{
SecIdentityRef identityRef = NULL;
SecCertificateRef certificateRef = NULL;
SecTrustRef trustRef = NULL;
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"server" ofType:#"pfx"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef password = CFSTR("test123");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = errSecSuccess;
securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
identityRef = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
trustRef = (SecTrustRef)tempTrust;
} else {
NSLog(#"Failed with error code %d",(int)securityError);
return nil;
}
SecIdentityCopyCertificate(identityRef, &certificateRef);
NSArray *result = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, (__bridge id)certificateRef, nil];
return result;
}
Then in the App Delegate I just setted this custom connection class to the httpServer:
[httpServer setConnectionClass:[MyHTTPConnection class]];
The last thing that you need to do is to add the server.pfx certificate to the bundle. I created the certificate following this tutorial. I just did until you have the server.crt file. Then I converted it to pfx12 in this website. I did this because p12 certificate was not working, I was having this problem.
Chris, could you edit your entry explaining a little bit how you did the manifest thing. I want to do exactly the same as you but I am having troubles with the URLs that you need to open.

NSURLConnection fails with CocoaHTTPServer with SSL using certificate signed by custom CA

I have a small app with CocoaHttpServer siting inside of it. I want to enable it only for loopback calls and I want to be able to make those calls only from my app. Don't ask why - I need it.
self.httpServer = [[HTTPServer alloc] init];
self.httpServer.port = HTTP_SERVER_PORT;
self.httpServer.interface = #"loopback";
self.httpServer.connectionClass = [HTTPSolFSConnection class];
NSError *error;
if([self.httpServer start:&error]) {
DDLogInfo(#"Started HTTP Server on port %hu", [self.httpServer listeningPort]);
}
else {
DDLogError(#"Error starting HTTP Server: %#", error);
}
The server uses custom HTTPConnection class which returns YES in isSecureServer method and uses certificate signed with custom CA.
- (BOOL)isSecureServer {
return YES;
}
- (NSArray *)sslIdentityAndCertificates {
NSArray *result = nil;
NSData *serverCertificateData = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"server" ofType:#"p12"]];
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { CFSTR("friday_internet") };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = NULL;
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) serverCertificateData, options, &items);
if (options) {
CFRelease(options);
}
if (securityError == errSecSuccess) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
SecCertificateRef serverCertificate = NULL;
SecIdentityCopyCertificate(identity, &serverCertificate);
NSData *caCertificateData = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ca" ofType:#"der"]];
SecCertificateRef caCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertificateData);
NSArray *anchorCertificates = #[ (__bridge id) serverCertificate, (__bridge id) caCertificate ];
SecTrustResultType trustResult;
SecTrustRef trust = (SecTrustRef) CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef) anchorCertificates);
SecTrustSetAnchorCertificatesOnly(trust, false);
SecTrustEvaluate(trust, &trustResult);
NSLog(#"SecTrustResultType: %d", trustResult);
result = #[ (__bridge id) identity, (__bridge id) caCertificate, (__bridge id) serverCertificate ];
if (serverCertificate) {
CFRelease(serverCertificate);
}
}
if (items) {
CFRelease(items);
}
return result;
}
Every time I create NSURLConnection to the server, it logs SecTrustResultType: 4 and fails with error:
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid.
You might be connecting to a server that is pretending to be “localhost” which could
put your confidential information at risk." UserInfo=0xb93da40
{NSErrorFailingURLStringKey=https://localhost:12345/file.txt,
NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?,
NSErrorFailingURLKey=https://localhost:12345/file.txt,
NSLocalizedDescription=The certificate for this server is invalid.
You might be connecting to a server that is pretending to be “localhost” which could
put your confidential information at risk., NSUnderlyingError=0xf155310
"The certificate for this server is invalid. You might be connecting to a server
that is pretending to be “localhost” which could put your confidential information
at risk.", NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x13329720>}
I read many articles and many questions on StackOverflow but it doesn't help:
How do I programatically import a certificate into my iOS app's keychain and pass the identity to a server when needed?
How to make iPhoneHTTPServer secure server
SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure with SecPolicyCreateSSL
Unable to trust a self signed certificate on iphone
https://developer.apple.com/library/ios/technotes/tn2232/_index.html#//apple_ref/doc/uid/DTS40012884-CH1-SECCUSTOMROOT
https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
There are two "hacks" which solve the problem:
I can implement connection:willSendRequestForAuthenticationChallenge: method of NSURLConnectionDelegate
I can implement custom NSURLProtocol
Email the CA's certificate to a device and manually trust it
The thing is that I'm using a third party library inside my app which communicates with my server. It doesn't use Foundation framework so first two methods don't work. Is there a way to do it programmatically instead of manually sending certificates?

Memory leak with SecPKCS12Import

My app connects to my server using mutual authentification, so I have a .p12 file containing a certificate. Everything works the way it is supposed to, but when I'm profiling my app using Instruments, it detects a memory leak on this line :
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]){
NSData* p12data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"client" ofType:#"p12"]];
CFArrayRef itemsCFArray = nil;
NSDictionary* dico = [NSDictionary dictionaryWithObjectsAndKeys:#"password",kSecImportExportPassphrase, nil];
// MEMORY LEAK just below
OSStatus check = SecPKCS12Import((__bridge CFDataRef)p12data, (__bridge CFDictionaryRef)dico, &itemsCFArray);
if(check != noErr){
NSLog(#"Error importing PKCS");
}
NSArray* items = (__bridge NSArray*)itemsCFArray;
SecIdentityRef identityRef = (__bridge SecIdentityRef)[[items objectAtIndex:0] objectForKey:(__bridge id)kSecImportItemIdentity];
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:identityRef certificates:nil persistence:NSURLCredentialPersistenceNone];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
I've tried to use a CFDictionaryRef instead, but it does not resolve the error.
I found someone with the same problem, but his solution is ios4, and i'm using ios5 (Actually, I'm already doing the same thing) : http://www.ipup.fr/forum/viewtopic.php?id=2855 (in french, sorry)
How can I resolve this ? Will Apple reject my app because of this memory leak ?
I don't think the issue is with the dictionary, itemsCFArray is what appears to be getting leaked. SecPKCS12Import passes back a CF reference to itemsCFArray, which you will need to CFRelease when you are done using the objects in it.
Try calling CFRelease(itemsCFArray) after creating the credential.

Resources