Check if SSL Certificate is present or not in iOS? - ios

I have an API end point which has an SSL Certificate (secure https API). My iOS app bundle also includes that certificate so when I try to reach the API end point, objective C verifies if the certificate matches or not. My implementation of matching certificates works fine.
But I also need to verify if the API provides an SSL Certificate or not.
I am using AFNetworking for matching the certificates, and the place where it verifies the certificate: - (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge does not even gets called when there is no certificate present. Hence the app doesn't verify anything.
I need something to verify in first place if the API has certificate or not, and if it doesn't then the app should not reach to the API end point.

Related

iOS - how do I get server certificate into iOS client keychain for pinning

TL;DR version: Is there any way to pass a Server certificate to an iOS client that doesn't involve also passing along the Server's private key?
I have written an iOS client app that communicates with my macOS server app (so I have control over both ends). I have implemented certificate pinning using a self-signed certificate to make things more secure. To accomplish this during development, I hardcoded the Server cert into the iOS client app and told the client to only connect to a server that gives you that exact cert during the TLS handshake. Everything is working great.
However in the real world I am selling this system as a set (1 Server, multiple clients to each customer), so I cannot hardcode a Server cert into the iOS client. My plan is to instead deliver the Server cert out of band (via email) to the iOS client like mentioned here: Making Certificates and Keys Available To Your App:
Apps can only access keychain items in their own keychain access groups.
To use digital identities in your own apps, you will need to write code to import them. This typically means reading in a PKCS#12-formatted blob and then importing the contents of the blob into the app's keychain using the function SecPKCS12Import
One way to provision an identity is via email. When you provision a device, send the associated user an email with their client identity attached as a PKCS#12 file.
My problem is that a .p12 file contains the certificate and the private key of the server - this seems very wrong to pass the private key along as well.
Is there any other way to pass the Server certificate to the iOS client that doesn't involve also passing along the Server's private key?
Thanks!!!
I was overthinking things here, the solution is actually pretty simple.
I just needed to email the Server's public certificate out of band to the client device with a custom extension like cert.myCustomExt1234. This is because the .crt extension is already claimed by iOS so you have to register your app to handle custom extensions (see apple docs here). Then in my app I can do all the logic of cert pinning using that out of band delivered Server public cert.
The key was changing the file extension to something not already claimed by iOS.

iOS network issue. TLS Session cache loophole

I'm facing a security-related issue for my iOS application. I'm using HTTPS for all my network calls and the public certificate used is from Trusted Authority which is bundled in the application to prevent Main in the Middle Attack (ref: Man in the middle attack - Wiki). I'm doing SSL Pinning (verifying the certificate from server in/before every network call) in Android it works perfectly fine, in iOS however there is a TLS Session Cache which cache the certificate validity after first network call.
For first network call the certificate validation part works fine, for 2nd call, cache is used by OS and I'm unable to verify the certificate. My QA team can easily attack and get all the data from the network call for 2nd and consecutive network calls. Here's the reference for TLS Session Cache iOS documentation. Seems like there's no way to clear the cache programmatically ref: AdvancedURLConnections.
Changing query parameter doesn't help, I already tried that. Please provide iOS specific solution. I can't do encryption of my data for business reasons.
EDIT:
I'm using below mentioned method to verify my certificate. For first network call this method is called by the OS, for consecutive calls this method is not being called.
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
My QA team simply does MiTM attack for every network call, they try to use their certificate and if for any network call I don't verify certificate then they can easily read the data. Because of cache I'm unable to verify my certificate.
The answer to this question is that this method will be called again if you switch your network. The response or result of the authentication is persistent for a session and as long as the session is valid, the connection is secured. So just rely on the framework's method and keep your communication secured :)

Can NSURLConnection use a client certificate installed in profiles on the device?

I have this setup:
A tomcat server configured to use ssl client certificate authentication (clientAuth=true)
An ipad with a valid client certificate installed on it (emailed as a .p12 file and visible under profiles)
When browsing via ios safari, the ipad uses the client cert and authenticates against the server fine.
However in code, using a NSURLConnection, it won't connect. Debugging on the server shows the client isnt sending and cert at all.
On the client I get an error like this:
Request(https://192.168.1.5:8443/device/security/policy>, 0, 0)) didFailWithError:Error Domain=NSURLErrorDomain Code=-1205 "The server “192.168.1.5” did not accept the certificate." UserInfo=0xe2eae30
{NSErrorFailingURLStringKey=https://192.168.1.5:8443/device/security/policy>, NSErrorFailingURLKey=https://192.168.1.5:8443/device/security/policy>, NSLocalizedDescription=The server “192.168.1.5” did not accept the certificate.,
NSUnderlyingError=0xe2eb250 "The server “192.168.1.5” did not accept the certificate.", NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0xe6ab010>}
I understand I can implement the delegate method for the challenge for the NSURLAuthenticationMethodClientCertificate protection space, but if I do that I dont have the certificate to send, its installed on the device and that isnt accessible via code (is it??)
I tried calling [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge] but that appeared to have no effect.
I was expecting that NSURLConnection would behave as per safari and access the installed certificate, but it appears not. I dont want to have to install the certificate into my app somehow - thats what the built in certificate management is for!
Or am I missing something? Any help appreciated.
Fundamentally you can't get at globally installed certificates from within an app in iOS (as of iOS 8), and the operating system won't help you out by sending them with an NSURLConnection. Safari has special rights to access the certificates. So the only way to use them from within an app is to install them into the app somehow, which makes the whole thing difficult.

How to accept trusted certificate added to iOS device via iPhone Configuration Utility in application

I want my application to have the ability to accept trusted root certificates that have been added to an iOS device by using the iPhone Configuration Utility.
I added a trusted certificate to an iOS device using the iPhone Configuration Utility and confirmed that Safari accepts my self-signed certificate by sending my server a https request. However, when I make a simple test app that uses NSURLConnection to make a GET request to my server using HTTPS, I get the following error message:
"
Error - The certificate for this server is invalid. You might be connecting to a server enter code here`that is pretending to be “myserver” which could put your confidential information at risk.
"
I imagine that my iOS app is sandboxed, and does not accept the self-signed certificate by default. I've tried manipulating code in willSendRequestForAuthenticationChallenge to accept the self signed certificate without success. I was hoping that someone else has figured out how to do this. I do not want to accept all self-signed certificates. I only want to accept self-signed certificates that have been configured to be trusted on the device.

If a server has a trusted certificate, What steps are needed to hit that link on IOS using NSURLConnection?

The Application i am working on needs to connect to a webservice over https, The certificate is trusted and valid.
I have used NSURLConnection is previous projects to use soap over http
Can anybody please point the difference between the two above mentioned scenarios,
I also need to understand what exactly happens when connecting over https, is the certificate stored automatically on the device, how does ssl handshake happen.
Any Pointers in this direction will be really helpful.
Regards,
Ishan
I need some clarification. Is the certificate signed by Apple for use with notifications or is it signed by an SSL root certificate authority (like VeriSign)?
Apple signed certificates are only to be used with WebServer to Apple Server communications like the Apple Push Notification Service. They are not intended for iOS device to WebServer.
A SSL certificate signed by a SSL root certificate authority should just work.
I think you are looking for an HTTP over SSL/TLS primer. So, here it goes.
HTTP is an unencrypted channel. The request and response are in a plain text data stream. HTTPS is an encrypted channel. The request and response are in a data stream encrypted using a shared master key. The magic of SSL/TLS is how this encrypted channel is created.
First, the client and server say hello to each other (in a clear channel).
Next, the client downloads the server's public certificate (in a clear channel).
At this point, the client has some work to do. It needs to verify the certificate. It needs to know that it understands the certificate, that the date range is valid, that the certificate is signed by a trusted certificate authority, and that the certificate has not been revoked.
Now, the client knows that it can trust the server.
Next, It sends a few short messages encrypted with the public key of the server (which is in the server's public certificate). These messages can only be decrypted by the server's private key (which only the server knows about). These messages allow the client and the server to negotiate a master key.
Finally, the client and the server begin the normal HTTP request and response using the newly created encrypted channel.
I hope this is what you are looking for. For a more detailed description see: http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html
If the certificate was issued by a chain of certificate authorities whose root is trusted by Apple, then there is nothing to do. The iOS device will accept the certificate, as long as it is otherwise valid (ie not expired, not revoked, etc).
If the CA chain's root is not trusted by Apple, you will need to download the root's certificate to the phone. This can be done (I think) via the iPhone Configuration Utility. Enterprise provisioning scenarios undoubtedly support this also.

Resources