iOS SCEP enrollment fails: “Couldn’t create certificate signing request” (smallstep CA + NGINX)

Author: ismailunsallCreated Sep 19, 2025Updated Jun 23, 2026

SCEP Enrollment Setup & Issue Summary (DevOps Write‑Up)

Network / Context

  • The local smallstep CA and the iOS device under test are on the same network.
  • Hostname resolution is configured via /etc/hosts and dnsmasq. The iOS device resolves the domain to the local IP (same subnet).
  • I can download the CA and SCEP configuration profiles over HTTPS (even when TLS verification is not enforced).

Docker Compose Environment

services: step-ca: image: smallstep/step-ca:0.28.4 container_name: step-ca restart: unless-stopped environment: DOCKER_STEPCA_INIT_NAME: "Lab Root CA" DOCKER_STEPCA_INIT_DNS_NAMES: "step-ca,localhost" DOCKER_STEPCA_INIT_SSH: "false" DOCKER_STEPCA_INIT_ACME: "false" DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT: "false" volumes: - ./step:/home/step ports: - "8080:8080" healthcheck: test: ["CMD-SHELL", "wget -qO- --no-check-certificate https://localhost:8080/health || exit 1"] interval: 10s timeout: 5s retries: 6 start_period: 15s

nginx: image: nginx:1.27-alpine container_name: step-edge depends_on: - step-ca restart: unless-stopped ports: - "9001:9001" # HTTPS for iOS - "9000:9000" # optional HTTP for local testing volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./edge:/etc/nginx/ssl:ro - ./out:/usr/share/nginx/html/profiles:ro

What the System Does

  • Runs step-ca in Docker and copies the Root/Intermediate certificates from the container into the out/ directory.
  • Edge TLS: generates edge/tls.key (RSA‑2048) + a CSR; signs it with step-ca (admin) → produces edge/tls.crt and edge/tls.fullchain.crt.
  • Creates/updates a SCEP provisioner with:
    • challenge (hex)
    • caps: SCEPStandard, POSTPKIOperation, SHA‑256, Renewal
    • minimum RSA key size 2048
    • encryption‑algorithm‑identifier = 2
    • X.509 template: clientAuth, KeyUsage: sign+enc → restarts step-ca and verifies health
  • Generates profiles:
    • ca.mobileconfig (Root + Intermediate, base64‑embedded)
    • scep.mobileconfig (URL: https://<EDGE_HOST>:/scep/<SCEP_NAME>, RSA‑2048, Usage=5, Challenge)
    • combined profile (CA + SCEP) anchoring via PayloadCertificateAnchorUUID to the Root/Intermediate
  • Profile signing: if needed, issues a short‑lived “Profile Signer” leaf (24h) with step ca certificate; signs all .mobileconfig files via OpenSSL SMIME/DER (nodetach) using signer.crt/key/chain.
  • Starts nginx: serves signed profiles from /profiles/; proxies /scep/ requests to step-ca:8080.
  • Verifies the TLS chain and prints ready URLs.
  • Versions (from compose): step-ca 0.28.4, nginx 1.27‑alpine (CLI logs note 0.28.7).

iOS Installation Order

  1. Install the CA‑only profile first.
    • The CA profile downloads; even if iOS shows “Not Verified,” proceed.
    • Then enable trust: Settings → General → About → Certificate Trust Settings → enable Full Trust for “Lab Root CA”.
  2. Download and install the SCEP configuration profile afterwards.

Observed Error

  • When installing the SCEP enrollment profile (enroll-profile.signed.mobileconfig) or the all‑in‑one profile, iOS shows: “Couldn’t create certificate signing request”
  • The SCEP profile does not install on the iOS device.

Question

Given this local smallstep CA setup (same network, host resolved via /etc/hosts and dnsmasq, CA/SCEP configs retrievable over HTTPS), how can I successfully install the SCEP profile on the iOS mobile device? What am I missing or doing incorrectly that causes the “Couldn’t create certificate signing request” error?