TLS certificate validation on iOS


Greetings, traveler!

Most iOS apps talk to servers over HTTPS. In everyday work, certificate validation stays in the background. You create a request, the system sends it, and everything works.

Most of the time, developers do not need to think about certificate validation at all. Still, understanding the basics can be useful when reviewing a server configuration, checking compatibility with supported iOS versions, or investigating an unexpected connection failure. In those cases, it helps to know how iOS evaluates the certificate chain and how to inspect it.

What iOS validates

When an app connects to an HTTPS server, iOS verifies the server before sending application data.

That validation includes a few core checks:

  • the certificate matches the requested hostname
  • the certificate is still valid
  • the certificate can be used for server authentication
  • each certificate in the chain has a valid signature
  • the chain reaches a root certificate trusted by the system
  • publicly trusted certificates meet Apple’s Certificate Transparency requirements where applicable

Apple ships trusted root certificates as part of its Root Store. These certificates act as trust anchors during server trust evaluation.

In normal cases, your app does not need to implement this logic itself. URLSession uses the platform’s built in trust evaluation unless you add custom server trust handling.

The certificate chain

A server certificate is usually not trusted by itself. It is part of a chain.

Server certificate
        ↓
Intermediate certificate authority
        ↓
Root certificate authority

The server certificate, often called the leaf certificate, identifies the domain. It is usually signed by an intermediate certificate authority. That intermediate is signed by another authority, and the process continues until the chain reaches a root certificate already trusted by the system.

The server usually sends the leaf certificate and one or more intermediate certificates during the TLS handshake. The trusted root typically comes from the operating system, not from the server.

A missing intermediate is a common problem. A server can appear fine in one environment and fail in another because its chain is incomplete.

Subject and issuer

Every certificate has a subject and an issuer. The subject identifies the certificate owner. For a leaf certificate, this is usually the server or domain. The issuer identifies the certificate authority that signed it.

A simple chain might look like this:

Subject: api.example.com
Issuer: Example Intermediate CA

Subject: Example Intermediate CA
Issuer: Example Root CA

When you inspect a chain, the issuer of one certificate should lead to the subject of the next certificate in the path.

That said, names alone are not enough for exact matching. For reliable analysis, it is better to check fingerprints and certificate identifiers as well.

Cross signed certificates

A certificate with “Root CA” in its name is not always the final trust anchor.

Sometimes a newer certificate authority is cross signed by an older one:

api.example.com
→ New Intermediate CA
→ New Root CA
→ Existing Trusted Root CA

This allows clients that do not yet trust the new root directly to build a valid path to an older root that already exists in the Root Store.

This matters because the list of certificates sent by the server is not always the only possible validation path. The trust engine may build another valid path by combining what the server sends with certificates already available on the device.

Certificate pinning

Certificate pinning adds an application level restriction on top of normal trust evaluation.

Without pinning, the app accepts a server certificate as long as the system can validate it through its normal trust rules. With pinning, the app also requires a specific certificate or public key to match a value it already knows.

Apple also supports pinned domains through App Transport Security. In that model, the app pins public key identities by storing SHA 256 hashes of certificate public keys.

Pinning can improve control, but it also adds operational risk. If a certificate or key changes and the app does not contain a valid backup pin, network access can break. Pinning is not a replacement for standard TLS validation. It is an additional check.

How to inspect a server certificate

The easiest first step is to inspect the server with OpenSSL:

openssl s_client \
  -connect example.com:443 \
  -servername example.com \
  -showcerts \
  -verify_hostname example.com \
  -verify_return_error

These options do different things:

  • -connect specifies the host and port
  • -servername sends the hostname through SNI so the server can choose the correct virtual host
  • -showcerts prints the certificates sent by the server
  • -verify_hostname checks that the certificate matches the requested hostname
  • -verify_return_error stops on verification errors instead of continuing

Note: -servername does not validate the hostname. It only affects which certificate the server returns.

How to read the OpenSSL output

You will usually see entries like these near the start:

depth=2 CN=Example Root CA
depth=1 CN=Example Intermediate CA
depth=0 CN=api.example.com

depth=0 is the leaf certificate. Higher numbers move up the chain.

You will also see a certificate chain section:

Certificate chain
 0 s:CN=api.example.com
   i:CN=Example Intermediate CA

 1 s:CN=Example Intermediate CA
   i:CN=Example Root CA

Here, s means subject and i means issuer.

This section shows the certificates sent by the server. It is not a fully verified chain, and it does not include certificates that the client may use from its local trust store.

At the end, you may see:

Verify return code: 0 (ok)

That means OpenSSL accepted the certificate path using the trust configuration available in that environment.

Inspecting an individual certificate

If you save one of the certificates to a PEM file, you can inspect it in more detail:

openssl x509 \
  -in certificate.pem \
  -noout \
  -subject \
  -issuer \
  -dates \
  -fingerprint \
  -text

The most useful fields are usually:

  • Subject Alternative Name
  • Not Before and Not After
  • Basic Constraints
  • Key Usage
  • Extended Key Usage
  • Subject Key Identifier
  • Authority Key Identifier
  • SHA 256 fingerprint

For hostname checks, pay attention to Subject Alternative Name. That is what modern clients use for domain matching.

OpenSSL is not iOS

A successful OpenSSL check is useful, but it does not prove that every iOS version will trust the same server.

OpenSSL uses the trust configuration of the machine where the command runs. iOS uses Apple’s Root Store. The two environments may contain different root certificates and may build different validation paths.

That leads to a few possible outcomes:

  • OpenSSL trusts the chain, but a target iOS version does not
  • iOS trusts the chain, but the local OpenSSL installation does not
  • both trust the server, but through different paths
  • a managed device trusts an extra root installed through a configuration profile

So Verify return code: 0 is a good signal, but it is not an iOS compatibility test.

Checking certificates on an iPhone

iOS provides a few places where you can inspect certificate-related settings.

To view installed configuration profiles, open:

Settings
→ General
→ VPN & Device Management

A profile may contain certificates used by corporate Wi-Fi, VPN connections, device management, or private server trust. Opening a profile shows the payloads it installed. Apple documents this path for viewing and removing configuration profiles.

To view manually installed root certificates that can be trusted for TLS, open:

Settings
→ General
→ About
→ Certificate Trust Settings

This screen shows user-installed root certificates for which full trust can be enabled or disabled. A certificate installed manually through a profile is not automatically trusted for SSL. The user normally has to enable full trust on this screen. Certificates installed through supported device-management mechanisms may be handled differently.

The same screen also shows the version of the Root Store installed on the device. However, it does not provide a browsable list of every root certificate included in Apple’s system store. To inspect that list, use Apple’s published Root Store documentation for the relevant iOS version.

How to check compatibility with iOS

A practical workflow looks like this.

First, retrieve the server chain with openssl s_client.

Second, record the important details for each certificate:

  • subject
  • issuer
  • expiration dates
  • SAN entries
  • SHA 256 fingerprint

Third, follow the chain until you identify the possible trust anchor. Be careful with cross signed certificates. A certificate that looks like a root may still continue to another authority.

Fourth, compare the possible trust anchor with Apple’s published Root Store for every iOS version your app supports. Use the SHA-256 fingerprint for exact identification because certificate names are not always unique.

Fifth, check whether the app uses custom server trust handling or certificate pinning. If it does, the result may differ from standard system validation.

Finally, test the endpoint on a clean installation of each supported iOS version. A simulator is useful for an initial check, but a physical device gives the strongest confirmation, especially if configuration profiles or managed trust settings may be involved.

Common certificate problems

A few issues may arise:

  • the hostname is missing from Subject Alternative Name
  • the leaf certificate has expired
  • an intermediate certificate has expired
  • the server does not send a required intermediate
  • the chain ends at a root the target iOS version does not trust
  • the server sends an unsuitable cross signed certificate
  • pinning contains an outdated certificate or public key
  • custom trust handling rejects a valid chain or accepts an invalid one

These issues can affect only some iOS versions because Root Stores and path building behavior change over time.

Final thoughts

In most cases, iOS handles TLS certificate validation for you. The app does not need to load certificates or verify the chain manually.

When you need to check compatibility, look at the full chain, not just the leaf certificate. Identify the possible trust anchor, compare it with Apple’s Root Store, check whether the app changes trust evaluation through pinning or custom logic, and confirm the result on the iOS versions you support.

OpenSSL is a useful inspection tool. It is not the final answer. The final answer comes from how iOS evaluates that server in the environments your app actually supports.