Contents

Client SSL Certificate on Android and iOS

This guide continues the article Configuring Client Certificate Authentication on 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:

SystemWorksDoes not work
AndroidChrome, Edge, Samsung Internet, Firefox 138 and laterFirefox before 138
iOS / iPadOSSafariChrome, Firefox, Edge and other third-party browsers
Info

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:

openssl pkcs12 -export -legacy -in user1.crt -inkey user1.key -out user1.p12

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.

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.

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.

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.

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.

  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.

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:

#!/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
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:

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.

  • “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, macOS, Linux.

Related Content