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:
| 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 |
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.p12Android
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:
- Settings → Security & privacy → More security & privacy → Encryption & credentials.
- Install a certificate → VPN & app user certificate.
- Select
user1.p12and enter its password. - 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.
adb push user1.p12 /sdcard/Download/
adb shell am start -a android.settings.SECURITY_SETTINGSThen 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
- Send
user1.p12to the device via AirDrop, email or the Files app. Open the file; iOS reports Profile Downloaded. - 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.
- Tap Install, enter the device passcode, then tap Install again.
- Enter the
.p12password 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:
#!/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>
EOFchmod +x mobileconfig.sh
./mobileconfig.sh user1The .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.mobileconfigOn 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
.p12with the-legacyoption (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_certificatein the nginx configuration, and that the CA and the client have different Organization Name fields.


