How to compare SSL certificates using AFNetworking - ios

In my iPhone app I'm using an https connection with a self-signed SSL certificate to download sensible data (username and password) from a server.
This app is for private use only, it is not meant for production.
I'm using AFNetworking to manage the https connection but, since my certificate isn't signed from a CA, in order to make it work I had to add the following to the header of the AFURLConnectionOperation class:
#define _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_ 1
But with this my app will allow any certificate.
Is there a way to allow only the certificate from my server maybe bundling it in the app and comparing it with the certificate provided by the server in the https connection?
And if it were possible, would there be any significant advantage in terms of security?
I'm very new to security and I'm kind of confused.

The term you're looking for is SSL Pinning, where the app verifies that a known certificate or public key matches one presented by a remote server.
AFNetworking supports both pinning with certificates or public keys. You'll need to add the certificate(s) or public key(s) to your app's Bundle, and enable the feature by setting either the defaultSSLPinningMode property on AFHttpClient or the SSLPinningMode property on AFURLConnectionOperation.
You can pin using AFSSLPinningModePublicKey or AFSSLPinningModeCertificate. AFSSLPinningModeCertificate means that the server's certificate must exactly match one of those in the bundle.
AFSSLPinningModePublicKey is more liberal and means that the server's certificate must match for any public key in the bundle, or any public key attached to certificates in the bundle.
There's an example of setting the pinning mode in the AppDotNet example.

To expand a bit on David's answer with respect to AFSSLPinningModePublicKey versus AFSSLPinningModeCertificate. Ideally, you would pin the public key and not the certificate. That's because some sites and services, like Google, rotate their certificates every 30 days or so. But they re-certify the same public key.
The certificates are rotated frequently to keep the size of the CRL small for mobile clients. But they re-certify the same public key (rather than creating a new one) to allow for key continuity testing.
Public key pinning is why tools like Certificate Patrol miss the mark. The certificate is expected to change; the public key is not.
Public key pinning is a lot like SSH's StrictHostKeyChecking, if you are familiar with it.
OWASP has a write-up on it too at Certificate and Public Key Pinning.

Related

How do I enable Application Transport Security (ATS) restrictions

How do I, Ensure ATS is enabled within the iOS mobile application so that confidential information sent between the application and the back end servers are secured and not to be intercepted by man­-in­the­-middle style attacks.
Below is what I already set but still Fails for the penetration test.enter image description here
Preventing Man-in-the-Middle Attacks in iOS with SSL Pinning.
Restricting the set of trusted certificates through pinning prevents attackers from analyzing the functionality of the app and the way it communicates with the server.
Types of SSL Certificate Pinning
Pin the certificate: You can download the server’s certificate and bundle it into your app. At runtime, the app compares the server’s certificate to the one you’ve embedded.
Pin the public key: You can retrieve the certificate’s public key and include it in your code as a string. At runtime, the app compares the certificate’s public key to the one hard-coded in your code.
There are multiple blogs available on topic of SSL pinning. for your reference attaching one
Please go through this to understand SSL pinning

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 adding a trusted root certificate - Public or Private?

I have a pretty basic understanding of PKI/SSL/TLS but not a lot of experience with it. I have several iOS devices connecting through a VPN to an internal server. The server is reachable, but we receive an SSL error. I understand I need to add the CA's cert to the iOS devices - but I'm confused as to if I add the CA's public or private cert?
According to this blog It's private, but I want to verify first.
http://nat.guyton.net/2012/01/20/adding-trusted-root-certificate-authorities-to-ios-ipad-iphone/
Any advice or further documentation would be greatly appreciated.
There's no such thing as a private certificate.
In an asymmetric encryption you have two keys - the public key and the private key. As the names suggest, the private key is kept private and the public key is distributed. A certificate is a separate object that serves to tie a public key to a "security principal" - i.e. a person, server, certification authority or whatever.
So your VPN server has a certificate. This certificate states that the given public key X belongs to your server. When you contact the server, it gives you its certificate and public key, then can prove it has the corresponding private key by you giving it a randomly generated number to sign/decrypt.
However, how do you know the server isn't lying when it gives you the certficate? Because the certificate is signed by a third party - a certificate authority. The CA's signed the server's certificate with its private key to confirm that what it says is true. You can verify this signature with the CA's public key. But how do you know that the CA's public key actually belongs to the CA?
As you can see, this can go on for a while, creating a chain of certificates or chain of trust. Eventually you have to have some certificates that you just trust without any third parties - the root certificates. iOS comes with a set of these that Apple trusts, but does not include the root of the chain for your VPN server. That is the certificate that you are being asked to install.

AFNetworking: secure handshake no longer works after certificate renewal

My app uses AFNetworking.
What type of certificate extension does AFNetworking look for?
I was given a .crt file by GeoTrust but I have read here that AFNetworking specifically looks for .cer files.
I ask because my https in a browser works fine with my new certificate chain, and using a tool called SSLDetective I can see that the whole chain is trusted, but for some reason my app no longer accepts the bundle for a trusted handshake. Any other time I had issues, SSLDetective showed me that a part of the chain was not trusted. In this case, I can't seem to find anything wrong. Not much changed in my ssl certificate configuration other than overwriting the old key and crt files with the new files with the exact same name. Help is appreciated. Thanks!
EDIT:
I now know for sure that AFNetworking does use and automatically detects .cer files in the app bundle. I also know that the certificate attached in the bundle must match the same public key as the certificate on my web server. With public key pinning, AFNetworking simply extracts the public key from the local certificate and compares it to the one on the server. If the one on the server matches, it uses the server's certificate.
In my case, in the beginning, the keys did not match, but now I made sure that they do. The chain is still trusted and good according to SSLDetective. I downloaded a .cer version of the certificate from the server using SSLDetective and attached it to my app bundle. However, now that I know for sure the public keys match as well, I still can't seem to get a secure handshake. This question is somewhat related to the question asked here.

Cryptographic Keys exchange between client and server

I have seen many examples on verifying client or server certificates using Security framework APIs but this will solve only problem of Identification of security features but what about Confidentiality of data? How do I exchange private and public keys between client and server? What about Interception, Modifications, or Fabrication attacks? What if someone pretending and sending correct certificate as expected by client?
Identification is provided by verifying the cert as you note. Confidentiality is provided via encryption. Authentication is provided by signing the data. Together they are often implemented via TLS over a network connection.
In short, if you properly implement and deploy HTTPS, and validate your certificates, then you will get all of the things you're describing. NSURLConnection will do almost all of this for you by default if you just use an "https" URL.
If you deploy a certificate on the server and protect its private key, then it is not feasible for an attacker to pretend to have that certificate. Only the server has the server's private key (it is up to you to protect the private key from copying or theft).
A typical approach is to use a commercial certificate, in which a certificate authority (CA) like Verisign attests that the private key was issued to the owner of a given host (known as the CN or common name). This is a simple-to-use approach and generally cost effective. Go to one of the well-known CAs and buy a cert.
However, you can also create your own public/private server keypair, protect the private key, and distribute the public key in your client. You can then configure your client to only accept that one certificate and no others. This is actually more secure than the commercial certificate. For an example of this, see SelfCert. This is from my CocoaConf-RTP-2012 talk. I'll be giving a similar talk at CocoaConf-DC-2013. It is also discussed at length in chapter 15 of iOS:PTL.
Client certificates are less common. They are used to authenticate the client, not the server. For a client certificate to work correctly, each client must have its own certificate. You can't ship a private key as part of your bundle. If you do, anyone can use that private key to impersonate a client. (Conversely, it is completely fine to put the server's public key in the bundle. It's public; you don't care who sees it.)
With CFNetwork, after connecting, you would need to use CFReadStreamCopyProperty to fetch the kCFStreamPropertySSLPeerTrust. You could then evaluate the returned SecTrust object. That said, I recommend the NSURLConnection code if you can use it. If you need lower-level access, you could still use NSStream. Jeff Lamarche discusses this in NSStream: TCP and SSL. But I'd recommend a tool like AFNetworking or CocoaAsyncSocket instead if you need lower-level control over TCP+SSL.

Resources