Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
9,671
6,212
I'm working both an iOS App and a server that it interacts with. Confidential information will be going back and forth between the two meaning that it's important that their communications are encrypted.

I'm confused about HTTPS/SSL and the certificate(s?) involved. What exactly is the purpose of a certificate authority? I vaguely get that somehow it's supposed to tell the client application that they can trust that the only person who can decrypt the messages that they encrypt is who they think it is (that is, that a man-in-the-middle attack has not taken place).

But it seems to me that I could just have a self-signed certificate. The client applications would know they could trust the server because they'd be downloaded from the app store already knowing the fingerprint of the proper certificate and so they'd be able to see that it was correct without needing to rely on a certificate authority.

Am I naïve in thinking that I can just use a self-signed certificate?

If a self-signed certificate is adequate, then my next question is, how do I obtain a self-signed certificate?

Then my final questions (for now) whether I end up needing a self-signed certificate or one from a certificate authority are:
1 - What changes do I need to make to my server? My stack is OS X, Apache, Python, Django, Django Rest Framework... I think that using the self-signed certificate means some kind of change to Apache, but I'm not exactly sure.

2 - What changes do I need to make to my iOS App? It's using MKNetworkKit.
 
Last edited:
I don't see a problem with a self-signed cert in your case. You can think of it like your web browser's CA trust list. That's a list of fingerprints hardcoded in your browser that it knows to trust. You would essentially be emulating that same idea by storing the fingerprint of your server's cert in the client app.

This is much different from a web browser which you would use to connect to thousands of different sites run by strangers and have to verify each one. Since you control the server's cert and you can limit the app to connecting just to that server, I don't think it would be a problem. If anything, what you are doing is closer to SSH. I don't need a CA to sign my SSH keys for my own SSH server and my own client machines because I have personally set them to be trusted on each device. If the SSH client sees a different cert, Iwill know I'm being MITM'd.

However, depending on the information you are transmitting, you may fall under laws that regulate how it is transmitted and stored. In that case, you'll be stuck following a standard that will probably require a CA.

Unless I'm looking at this wrong, I think you're good to go with self-signed certs. I can't help you with implementation though.

edit: Another thing to consider are legal implications of deviating from common SSL practices. I would rather stand up in court and tell them that I did everything I could to secure their data, including getting a CA to verify the keys. If you deviate from a standard or common practice, you might be opening yourself up to trouble. It's not right, but the world's love affair with standards rather than results has caused problems before.
 
Last edited:
The iOS SSL architecture is based around valid certificates with a valid CA. If you use self-signed certs you end up writing code that does a byte wise comparison of your in-app cert and the cert that is downloaded from the server. If your hypothetical man-in-the-middle attacker sends your app the cert that was downloaded from your server and your app does a simple byte wise comparison it will say thumbs up, when it's really thumbs down.

Couple things:

You can probably buy a valid cert for $100 or less. Then things will 'just work.' Look around for CAs that will sell them for a low price.

Security is a very hard topic. You don't want to go off and invent your own security methodology unless you're an expert.
 
If your hypothetical man-in-the-middle attacker sends your app the cert that was downloaded from your server and your app does a simple byte wise comparison it will say thumbs up, when it's really thumbs down.

I'm not certain how SSL works, but it seems to me that if a MitM attack was as simple as you just suggested, then an attacker could do this with a CA certificate just as easily as they could with a self signed certificate.

I ended up using the ISPCertificatePinning class from OpenSSL to store a der in my app resource bundle and to check that against what my app receives from the server.
 
I'm not certain how SSL works, but it seems to me that if a MitM attack was as simple as you just suggested, then an attacker could do this with a CA certificate just as easily as they could with a self signed certificate.

In order to do a man in the middle attack they would need to sign the certificate with a trusted root CA certificate. This is the major advantage of not using self signed certificates. If the certificate authority keeps their root certificates safe then theoretically it should ensure that such man in the middle attacks are much harder to carry out.

Of course there have been instances where hackers have got access to certificate authority signing certificates and that has meant that they could create valid certificates for any domain of their choice but thankfully this fairly rare.

With a self signed certificate it is easy. Just create a new self signed certificate and away you go. That is why trust chains are so important with SSL.
 
In order to do a man in the middle attack they would need to sign the certificate with a trusted root CA certificate. This is the major advantage of not using self signed certificates. If the certificate authority keeps their root certificates safe then theoretically it should ensure that such man in the middle attacks are much harder to carry out.

Of course there have been instances where hackers have got access to certificate authority signing certificates and that has meant that they could create valid certificates for any domain of their choice but thankfully this fairly rare.

With a self signed certificate it is easy. Just create a new self signed certificate and away you go. That is why trust chains are so important with SSL.

That's true for a MITM attack on a web browser, but I think a CA signed cert is unnecessary for the OP's application. If he hardcodes the server's public key into the app and has the app verify that it is receiving that key, then even an attacker who managed to get a valid CA signed cert would not be able to MITM the connection.

The CA system is designed to authenticate servers where you don't know their public keys. It's really designed for the open internet where you connect to many different servers. Pinning the cert in the app and checking the server's cert against it accomplishes that authentication. SSH does it this way.

Of course, the OP also needs to build in a way to update the app if he changes his server's keys, which is a good idea to do every so often. It also requires other security measures to protect the server, data stored in the app on the device, data stored on the server, etc., but security always requires layers.
 
Certificate pinning is meant to be used in addition to the normal verification of the server that is done by TLS/SSL. If you're using a self-signed certificate you're not using the normal verification done by TLS/SSL. Looking at that code all I see is

Code:
           if ([pinnedCertificate isEqualToData:DERCertificate]) {
                return YES;
That's the byte wise comparison I mentioned. Any fake server can download the cert from your server and send it to your client. And your code will return YES.

Self signed certs should only be used on internal web sites. Not on sites available on the public internet.
 
Last edited:
Certificate pinning is meant to be used in addition to the normal verification of the server that is done by TLS/SSL. If you're using a self-signed certificate you're not using the normal verification done by TLS/SSL. Looking at that code all I see is

if ([pinnedCertificate isEqualToData:DERCertificate]) {
return YES;

That's the byte wise comparison I mentioned. Any fake server can download the cert from your server and send it to your client. And your code will return YES.

Self signed certs should only be used on internal web sites. Not on sites available on the public internet.

The fake servers would not download the private key, so I don't see how downloading the cert would help them. All they would get if they performed a MITM attack is ciphertext that they could not decrypt without the corresponding private key.
 
Certificate pinning is meant to be used in addition to the normal verification of the server that is done by TLS/SSL. If you're using a self-signed certificate you're not using the normal verification done by TLS/SSL. Looking at that code all I see is

Code:
           if ([pinnedCertificate isEqualToData:DERCertificate]) {
                return YES;
That's the byte wise comparison I mentioned. Any fake server can download the cert from your server and send it to your client. And your code will return YES.

Self signed certs should only be used on internal web sites. Not on sites available on the public internet.

This post has been haunting the back of my mind for the past ~2 weeks.

My server has a private key, in the form of a cert file. It sends out a corresponding public key, in the form of a der file. I have the same public key pinned in my client side application. If it receives an SSL connection with that public key, it'll accept the connection, and if it gets a different key, it'll reject the connection.

So this means that a Man In the Middle can't just produce their own public/private key and try communicating with my client in that way, because the client would reject that.

The man in the middle COULD get the public key, but that's not useful for intercepting messages from the client to the server, as all it would get is ciphertext, nor is it useful for manipulating messages from the server to the client, as it wouldn't be something the client could decrypt.

Unless I'm mistaken... the man in the middle could receive and decrypt messages from the server, as well as properly respond to the server. Couldn't the man in the middle do the exact same even if I had a CA sign my certificate, though? Or is there another element to SSL and certificate signing/pinning that I'm missing that would cover this?
 
This post has been haunting the back of my mind for the past ~2 weeks.

My server has a private key, in the form of a cert file. It sends out a corresponding public key, in the form of a der file. I have the same public key pinned in my client side application. If it receives an SSL connection with that public key, it'll accept the connection, and if it gets a different key, it'll reject the connection.

So this means that a Man In the Middle can't just produce their own public/private key and try communicating with my client in that way, because the client would reject that.

The man in the middle COULD get the public key, but that's not useful for intercepting messages from the client to the server, as all it would get is ciphertext, nor is it useful for manipulating messages from the server to the client, as it wouldn't be something the client could decrypt.

Unless I'm mistaken... the man in the middle could receive and decrypt messages from the server, as well as properly respond to the server. Couldn't the man in the middle do the exact same even if I had a CA sign my certificate, though? Or is there another element to SSL and certificate signing/pinning that I'm missing that would cover this?

Yes. A CA signature doesn't prevent someone from stealing the public key. This is okay because public keys are useless without the corresponding private key. SSL wouldn't work at all if stealing the public key was a problem.

The only thing a CA signature does is help provide authentication over an insecure channel. In your case, this authentication is accomplished by you when you are coding the correct public key fingerprint into the client app.

Think of it this way. When you connect to a banking website, you have no idea if the key you're presented with is the bank's or an attacker's. Normally you have a CA verify that it is indeed the bank's and look for the CA's signature. But you could also go to the bank and ask them to print out the public key fingerprint for their server and hand it to you.

Now you don't care if a CA signed it or not. You know from the horse's mouth what the correct fingerprint looks like. You accomplished the authentication by meeting in person with the bank and finding out their fingerprint (this is the concept behind a PGP signing party). In your app's case, you know for a fact that fingerprint XYZ is your server's public key because you created the server's key pair.

Impersonating a client is possible with SSL because it does not generally verify the identity of clients. That's why you have to enter a password when you go to your bank's site; they need to verify that the connection they have is really from you and not someone pretending to be you before they start giving out your bank account info. Impersonating a client is not a MITM attack though because they are not intercepting a connection, i.e they are not "in the middle" of anything.

SSH is better set up for mutual authentication than SSL. In SSH, you pin the server's public key on the client device, and you can also pin the client's public key on the server. This prevents any other client from connecting to the server. It simultaneously prevents anyone from pretending to be the server.
 
Yes. A CA signature doesn't prevent someone from stealing the public key. This is okay because public keys are useless without the corresponding private key. SSL wouldn't work at all if stealing the public key was a problem.

The only thing a CA signature does is help provide authentication over an insecure channel. In your case, this authentication is accomplished by you when you are coding the correct public key fingerprint into the client app.

Think of it this way. When you connect to a banking website, you have no idea if the key you're presented with is the bank's or an attacker's. Normally you have a CA verify that it is indeed the bank's and look for the CA's signature. But you could also go to the bank and ask them to print out the public key fingerprint for their server and hand it to you.

Now you don't care if a CA signed it or not. You know from the horse's mouth what the correct fingerprint looks like. You accomplished the authentication by meeting in person with the bank and finding out their fingerprint (this is the concept behind a PGP signing party). In your app's case, you know for a fact that fingerprint XYZ is your server's public key because you created the server's key pair.

Impersonating a client is possible with SSL because it does not generally verify the identity of clients. That's why you have to enter a password when you go to your bank's site; they need to verify that the connection they have is really from you and not someone pretending to be you before they start giving out your bank account info. Impersonating a client is not a MITM attack though because they are not intercepting a connection, i.e they are not "in the middle" of anything.

SSH is better set up for mutual authentication than SSL. In SSH, you pin the server's public key on the client device, and you can also pin the client's public key on the server. This prevents any other client from connecting to the server. It simultaneously prevents anyone from pretending to be the server.

Okay, so imagine this:

Client -[Encrypted w/ Public Key "Here's my credentials, log me in"]-> Server
Server -[Encrypted w/ Private Key "Here's a token so I remember you"] -> MITM
MITM can decrypt the token and use it.
MITM -[Encrypted w/ Public Key "Move all the money from this account, here's the token, to this other account"]-> Server

Also:

Client -[Encrypted w/ Public Key "Give me my super secret file, here's my credentials"]-> Server
Server -[Encrypted w/ Public Key "Here's the super secret file"]-> MITM
MITM now had the super secret file.

I must be missing something here. If this is really the way it works, couldn't I listen to absolutely anything that a server sends to its clients, regardless of whether they use SSL or not? Depending on how the authentication system works, I could even impersonate the user without ever knowing their login credentials.
 
HTTPS connections give you two things: Certainty that the server is who it says it is and encryption of the conversation. If you use this pinning method with a self-signed cert you give up the first of those two, but not the second.

Valid CA-signed certs are part of how the identity of the server is confirmed.

The MITM cannot decrypt the conversation. Having the primary key is required to decrypt the conversation.
 
Okay, so imagine this:

Client -[Encrypted w/ Public Key "Here's my credentials, log me in"]-> Server
Server -[Encrypted w/ Private Key "Here's a token so I remember you"] -> MITM
MITM can decrypt the token and use it.
MITM -[Encrypted w/ Public Key "Move all the money from this account, here's the token, to this other account"]-> Server

Also:

Client -[Encrypted w/ Public Key "Give me my super secret file, here's my credentials"]-> Server
Server -[Encrypted w/ Public Key "Here's the super secret file"]-> MITM
MITM now had the super secret file.

I must be missing something here. If this is really the way it works, couldn't I listen to absolutely anything that a server sends to its clients, regardless of whether they use SSL or not? Depending on how the authentication system works, I could even impersonate the user without ever knowing their login credentials.

That's not quite how it works. Here's a somewhat simplified version

1.) The client contacts the server and requests a secure connection.

2.) The server responds by sending its public key

3.) The client verifies that the key belongs to the server and is not revoked or expired. Normally, this is done by looking for a valid CA signature. In your case, it would be done by checking the public key against the pinned value inside the app. No match means no connection.

4.) If the public key is good, the client generates a symmetric session key that will be used for actual encryption of the data.

5.) The client encrypts this session key with the server's public key and sends it to the server

6.) The server decrypts the session key with its private key. Now the session key can be used to encrypt the data.

7.) The server encrypts the data with the symmetric key and sends it to the client

8.) The client already knows the session key since it created it, so it uses the session key to decrypt the data.

9.) Any further communication is encrypted with just the session key, which both the client and server now have.

Now let's imagine Eve situates her computer on the network such that she can perform a MITM attack being performed. The following is the data that passes through Eve's computer and what she could/couldn't do with it.

A.) The client's request for a secure connection. If Eve responds with her own public key, the attack fails as long as the client knows that it is not the server's public key. This could be because she doesn't have a valid CA signature on her public key. It could also be because it is not the fingerprint stored in the client app. Eve could steal the public key from the server and trick the client into connecting to her, but that is okay because she doesn't have the corresponding private key.

B.) The server's public key. If Eve intercepts or steals this, she could pretend to be the server. However, she can't do anything with just the public key. She'd need the corresponding private key to decrypt any data encrypted with this.

C.) The encrypted session key. Since this was encrypted with the server's public key, Eve cannot decrypt it. Thus she does not know the session key.

D.) Data encrypted using the session key. Since Eve doesn't have the session key, she cannot decrypt this data.

Now, Eve could certainly pretend to be a client and connect to the server. This is why your bank asks for a password. The server needs to know which user the client is and thus what kind of access he is authorized to have. SSL/TLS does not provide this client authentication.
 
Last edited:
HTTPS connections give you two things: Certainty that the server is who it says it is and encryption of the conversation. If you use this pinning method with a self-signed cert you give up the first of those two, but not the second.

Valid CA-signed certs are part of how the identity of the server is confirmed.

The MITM cannot decrypt the conversation. Having the primary key is required to decrypt the conversation.

Not in a case where the owner of the public key can be established offline. If I have a piece of paper from the CEO of my bank telling me their public key fingerprint is XYZ, I know that whenever I see that fingerprint, it belongs to the bank. I don't need a CA to tell me who owns the public key in this case because I have already verified it via a secure channel (in person).

If I connect to my own SSH server, I don't need a CA to tell me that the fingerprint I receive belongs to 556fmjoe's SSH server. I know what the correct fingerprint is because I created it myself and am the owner of it.

Likewise if ArtOfWarfare creates his server's key pair and then pins the public key into the app, he doesn't need a CA to tell him that the public key's fingerprint belongs to ArtOfWarfare's server.
 
How do you know that you're connected to your own server?

If the fingerprints match, you do know that only your server can read the data.

You could just as easily steal a CA signed cert and pretend to be the website. You'll get people connecting to you just fine, but you won't be able to read anything.

edit: Also, if an attacker just has your public key, the connection will fail when he can't decrypt the session key.
 
Last edited:
How do you know that you're connected to your own server?

You don't know directly, only indirectly. The session key is sent encrypted under the server's public key. Only the holder of the private key can decrypt it. So if the session is established correctly using the session key, then we can infer that the entity on the other end of the connection has the private key corresponding to the server's public key. If they didn't have the private key, they wouldn't be able to decrypt the session key, and the session wouldn't be established.

This is fundamental to all asymmetric-key cryptography: something encrypted under a public key can only be decrypted with the private key. If that premise doesn't hold true, then public-key crypto has a fatal theoretical flaw.
 
4.) If the public key is good, the client generates a symmetric session key that will be used for actual encryption of the data.

5.) The client encrypts this session key with the server's public key and sends it to the server

6.) The server decrypts the session key with its private key. Now the session key can be used to encrypt the data.

7.) The server encrypts the data with the symmetric key and sends it to the client

8.) The client already knows the session key since it created it, so it uses the session key to decrypt the data.

9.) Any further communication is encrypted with just the session key, which both the client and server now have.

Now let's imagine Eve situates her computer on the network such that she can perform a MITM attack being performed. The following is the data that passes through Eve's computer and what she could/couldn't do with it.

[...]

C.) The encrypted session key. Since this was encrypted with the server's public key, Eve cannot decrypt it. Thus she does not know the session key.

D.) Data encrypted using the session key. Since Eve doesn't have the session key, she cannot decrypt this data.

Okay, all this session key stuff is what was missing from my mental model of how it was working. I have everything implemented properly; I just wasn't quite sure what SSL was handling automatically for me. Thanks for clarifying it.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.