# Client SSL Certificate on Android and iOS


<!--more-->

This guide continues the article
[Configuring Client Certificate Authentication on Nginx]({{< relref "client-certificate-authentication-nginx" >}}).
You have a `user1.p12` file with the certificate and private key, and its password. Now it needs to be installed on
a phone or tablet.

On mobile systems the certificate is installed into the system and browsers take it from there. Not every browser
supports this, though:

| System | Works | Does not work |
|---|---|---|
| Android | Chrome, Edge, Samsung Internet, Firefox 138 and later | Firefox before 138 |
| iOS / iPadOS | Safari | Chrome, Firefox, Edge and other third-party browsers |

{{< admonition type=info open=true >}}
**If the file was created by OpenSSL 3.** Older Android and iOS versions may not accept a `.p12` with the encryption
algorithms OpenSSL 3 uses by default and report a wrong password. In that case recreate the file with the `-legacy`
option:

```shell
openssl pkcs12 -export -legacy -in user1.crt -inkey user1.key -out user1.p12
```
{{< /admonition >}}


## Android

### Through settings

Copy `user1.p12` to the device via USB, cloud storage or a messenger, and save it to **Downloads**. A screen lock
(PIN, pattern or password) must be enabled to install a certificate.

Menu names differ between manufacturers and Android versions. The quickest way is to search the settings for
"certificate". On Pixel and stock Android 14–16 the path is:

1. **Settings → Security & privacy → More security & privacy → Encryption & credentials**.
2. **Install a certificate → VPN & app user certificate**.
3. Select `user1.p12` and enter its password.
4. Give the certificate a name (it is shown in the list under this name) and tap **OK**.

On Samsung the path is similar: **Settings → Security and privacy → Other security settings → Install from device
storage → VPN and app user certificate**.

On the first visit to the site Chrome shows a **Select a certificate** dialog; pick the installed one and tap
**Select**.

To view and delete the certificate: **Encryption & credentials → User credentials**, tap the certificate →
**Remove**.

### From the command line (adb)

Android does not allow installing a client certificate without user confirmation; that is possible only through a
mobile device management (MDM) system. But you can copy the file from a computer and open the right settings screen
right away. **USB debugging** must be enabled in the developer options.

```shell
adb push user1.p12 /sdcard/Download/
adb shell am start -a android.settings.SECURITY_SETTINGS
```

Then follow the steps from the previous section on the phone.

### Firefox on Android

Since version 138 Firefox for Android uses certificates installed in the system. There is no need to install the
certificate into Firefox separately. If the certificate selection dialog does not appear the first time, reload the
page.


## iOS and iPadOS

On iPhone and iPad a client certificate can be used **only by Safari** (and Apple apps such as Mail). Third-party
browsers such as Chrome, Firefox and Edge have no access to certificates installed through profiles; this is an iOS
restriction.

### Through settings

1. Send `user1.p12` to the device via AirDrop, email or the **Files** app. Open the file; iOS reports
   **Profile Downloaded**.
2. Open **Settings**. A **Profile Downloaded** item appears at the top; tap it. If it is not there, go to
   **Settings → General → VPN & Device Management** and find the profile under **Downloaded Profile**.
3. Tap **Install**, enter the device passcode, then tap **Install** again.
4. Enter the `.p12` password and tap **Next → Done**.

When you open the site, Safari offers to choose the certificate.

To delete the certificate: **Settings → General → VPN & Device Management**, select the profile → **Remove Profile**.

### With a configuration profile and Apple Configurator

If there are many certificates or they need to be distributed centrally, it is more convenient to wrap the `.p12` in
a `.mobileconfig` configuration profile. Such a profile can be sent by email, published on a website, installed with
Apple Configurator or through MDM.

The `mobileconfig.sh` script builds a profile from a `.p12` file. It works on Linux and macOS:

```shell
#!/bin/sh

USER=$1
P12="$USER.p12"
ORG=com.example

if [ -z "$1" ]; then
  echo $0 \"User Name\"
  exit 1
fi

DATA=$(base64 < "$P12" | tr -d '\n')
UUID1=$(uuidgen)
UUID2=$(uuidgen)

cat > "$USER.mobileconfig" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>PayloadContent</key>
  <array>
    <dict>
      <key>PayloadType</key><string>com.apple.security.pkcs12</string>
      <key>PayloadVersion</key><integer>1</integer>
      <key>PayloadIdentifier</key><string>$ORG.client-cert.$UUID1</string>
      <key>PayloadUUID</key><string>$UUID1</string>
      <key>PayloadDisplayName</key><string>$USER</string>
      <key>PayloadCertificateFileName</key><string>$P12</string>
      <key>PayloadContent</key><data>$DATA</data>
    </dict>
  </array>
  <key>PayloadType</key><string>Configuration</string>
  <key>PayloadVersion</key><integer>1</integer>
  <key>PayloadIdentifier</key><string>$ORG.client-cert-profile.$UUID2</string>
  <key>PayloadUUID</key><string>$UUID2</string>
  <key>PayloadDisplayName</key><string>Certificate for example.com</string>
</dict>
</plist>
EOF
```

```shell
chmod +x mobileconfig.sh
./mobileconfig.sh user1
```

The `.p12` password is not stored in the profile; iOS asks for it during installation. Installing the profile works
the same way as installing the `.p12` file above.

On a Mac with **Apple Configurator** installed (the `cfgutil` command-line tool is installed from the
**Apple Configurator → Install Automation Tools...** menu), the profile can be sent to a device connected via USB:

```shell
cfgutil install-profile user1.mobileconfig
```

On a device that is not managed by an organization (not supervised), the profile still has to be confirmed manually
in **Settings**, as in the previous section.


## Troubleshooting

- **"Wrong password" on install.** If the password is correct, recreate the `.p12` with the `-legacy` option
  (see above).
- **The browser does not offer a certificate.** Check that the browser is on the supported list. On iOS use Safari.
  On Android check that the certificate was installed as a **VPN & app user certificate**, not as a CA certificate.
- **The wrong certificate was selected.** Chrome on Android remembers the choice until it restarts; close it from the
  recent apps list.
- **The server rejects the certificate.** Check that it is signed by the CA set in `ssl_client_certificate` in the
  nginx configuration, and that the CA and the client have different **Organization Name** fields.

Guides for other systems:
[Windows]({{< relref "client-certificate-windows" >}}),
[macOS]({{< relref "client-certificate-macos" >}}),
[Linux]({{< relref "client-certificate-linux" >}}).

