Transport Layer Security (TLS) is the most important security protocol on the Internet today. Most notably, TLS puts the S into HTTPS, adding security to the otherwise insecure HTTP protocol.
IMPORTANT TLS is the successor to the Secure Sockets Layer (SSL) protocol. SSL is no longer considered secure and it’s now rarely used in practice, although many folks still say SSL when they mean TLS.
TLS is a complex protocol. Much of that complexity is hidden from app developers but there are places where it’s important to understand specific details of the protocol in order to meet your requirements. This post explains the fundamentals of TLS, concentrating on the issues that most often confuse app developers.
For standard TLS to work the server must have a digital identity, that is, the combination of a certificate and the private key matching the public key embedded in that certificate. TLS crypto magic ensures that:
the client gets a copy of the server’s certificate
the client knows that the server holds the private key matching the public key in that certificate
In a typical TLS handshake the server passes the client a list of certificates, where item 0 is the server’s certificate (the leaf certificate), item N is (optionally) the certificate of the certificate authority that ultimately issued that certificate (the root certificate), and items 1…N-1 are any intermediate certificates required to build a cryptographic chain of trust from 0 to N.
Note The cryptographic chain of trust is established by means of digital signatures. Certificate N in the chain is issued by certificate N+1. The owner of certificate N+1 uses their private key to digitally sign certificate N. The client can verify this signature using the public key embedded in certificate N+1. Eventually this chain terminates in a trusted anchor, that is, a certificate that the client trusts by default. Typically this anchor is a self-signed root certificate from a certificate authority.
Note Item N is optional for reasons I’ll explain below. Also, the list of intermediate certificates may be empty (in the case where the root certificate directly issued the leaf certificate) but that’s uncommon for servers in the real world.
Once the client gets the server’s certificate, it must evaluate trust on that certificate to confirm that it’s talking to the right server. There’s three levels of trust evaluation here:
Basic X.509 trust evaluation checks that there’s a cryptographic chain of trust from the leaf through the intermediates to a trusted root certificate. The client has with a set of trusted root certificates built in (these are from well-known certificate authorities, or CAs), and a site admin can add more via a configuration profile.
This step also checks that none of the certificates have expired, and various other more technical criteria (like the Basic Constraints extension).
Note This explains why the server does not have to include the root certificate in the list of certificates it passes to the client; the client has to have the root certificate installed if trust evaluation is to succeed.
In addition, TLS trust evaluation (per RFC 2818) checks that the DNS name that you connected to matches the DNS name in the certificate. Specifically, the DNS name must be listed in either the Common Name element of the Subject or in the Subject Alternative Name extension.
Note The Subject Alternative Name extension can also contain IP addresses, although that’s a much less well-trodden path.
App Transport Security (ATS) adds its own security checks.
The above happens for all TLS connections, although you can override trust evaluation in many situations (for details, see Technote 2232 HTTPS Server Trust Evaluation). Such overrides can either tighten or loosen security. For example:
You might tighten security by checking that the server certificate was issued by a specific CA. That way, if someone manages to convince a poorly-managed CA to issue them a certificate for your server, you can detect that and fail.
You might loosen security by adding your own CA’s root certificate as a trusted anchor.
IMPORTANT If you rely on loosened security, you have to disable ATS. If you leave ATS enabled, it will require that default server trust evaluation succeed regardless of any customisations you do.
The previous section discusses server trust evaluation, which is required for all standard TLS connections. That process describes how the client decides whether to trust the server. Client certificate authentication is the opposite of that, that is, it’s the process by which the server decides whether to trust the client.
Client certificate authentication is optional. The server must require a certificate from the client and the client may choose to supply one or not (although if the server requests a certificate and the client does not supply one it’s likely that the server will then fail the connection).
At the TLS protocol level this works much like it does with the server certificate. For the client to provide this certificate it must apply a digital identity to the connection, and TLS crypto magic assures the server that, if it gets a certificate from the client, the client holds the private key associated with that certificate.
Where things diverge is in trust evaluation. Trust evaluation of the client certificate is done on the server, and the server uses its own rules to decided whether to trust a specific client certificate. For example:
Some servers do basic X.509 trust evaluation and then check that the chain of trust leads to one specific root certificate; that is, a client is trusted if it holds a digital identity whose certificate was issued by a specific CA.
Some servers just check the certificate against a list of known trusted client certificates.
When the client sends its certificate to the server it actually sends a list of certificates, much as I’ve described above for the server’s certificates. In many cases the client only needs to send item 0, that is, its leaf certificate. That’s because:
the server already has the intermediate certificates required to build a chain of trust from that leaf to its root
there’s no point sending the root, as I discussed above in the context of the server trust evaluation
However, there are no hard and fast rules here; the server does its client trust evaluation using its own internal logic, and it’s possible that this logic might require the client to present intermediates, or indeed present the root certificate even though it’s typically redundant. If you have problems with this, you’ll have to ask the folks running the server to explain these requirements.
Note If you do need to send additional certificates to the server, you can do this by passing them to the
certificates parameter of the method you use to create your NSURLCredential (typically
+[NSURLCredential credentialWithIdentity:certificates:persistence:]). Be aware that this list does not need to include the leaf certificate; that is extracted from the digital identity that you pass to the
One thing that bears repeating is that trust evaluation of the client certificate is done on the server, not the client. The client does not care whether the client certificate is trusted or not. Rather, it simply passes that certificate the server and it’s the server that makes the decision.
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"