Skillshub axiom-code-signing-ref
Use when needing certificate CLI commands, provisioning profile inspection, entitlement extraction, Keychain management scripts, codesign verification, fastlane match setup, Xcode build settings for signing, or APNs .p8 vs .p12 decision. Covers complete code signing API and CLI surface.
git clone https://github.com/ComeOnOliver/skillshub
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/CharlesWiltgen/Axiom/axiom-code-signing-ref" ~/.claude/skills/comeonoliver-skillshub-axiom-code-signing-ref && rm -rf "$T"
skills/CharlesWiltgen/Axiom/axiom-code-signing-ref/SKILL.mdCode Signing API Reference
Comprehensive CLI and API reference for iOS/macOS code signing: certificate management, provisioning profile inspection, entitlement extraction, Keychain operations, codesign verification, fastlane match, and Xcode build settings.
Quick Reference
# Diagnostic flow — run these 3 commands first for any signing issue security find-identity -v -p codesigning # List valid signing identities security cms -D -i path/to/embedded.mobileprovision # Decode provisioning profile codesign -d --entitlements - MyApp.app # Extract entitlements from binary
Certificate Reference
Certificate Types
| Type | Purpose | Validity | Max Per Account |
|---|---|---|---|
| Apple Development | Debug builds on registered devices | 1 year | Unlimited (per developer) |
| Apple Distribution | App Store + TestFlight submission | 1 year | 3 per account |
| iOS Distribution (legacy) | App Store submission (pre-Xcode 11) | 1 year | 3 per account |
| iOS Development (legacy) | Debug builds (pre-Xcode 11) | 1 year | Unlimited |
| Developer ID Application | macOS distribution outside App Store | 5 years | 5 per account |
| Developer ID Installer | macOS package signing | 5 years | 5 per account |
| Apple Push Services | APNs .p12 certificate auth (legacy) | 1 year | 1 per App ID |
CSR Generation
# Generate Certificate Signing Request openssl req -new -newkey rsa:2048 -nodes \ -keyout CertificateSigningRequest.key \ -out CertificateSigningRequest.certSigningRequest \ -subj "/emailAddress=dev@example.com/CN=Developer Name/C=US"
Or use Keychain Access: Certificate Assistant → Request a Certificate From a Certificate Authority.
Certificate Inspection
# View certificate details (from .cer file) openssl x509 -in certificate.cer -inform DER -text -noout # View certificate from .p12 openssl pkcs12 -in certificate.p12 -nokeys -clcerts | openssl x509 -text -noout # List certificates in Keychain with SHA-1 hashes security find-identity -v -p codesigning # Example output: # 1) ABC123... "Apple Development: dev@example.com (TEAMID)" # 2) DEF456... "Apple Distribution: Company Name (TEAMID)" # 2 valid identities found # Find specific certificate by name security find-certificate -c "Apple Distribution" login.keychain-db -p # Check certificate expiration (pipe PEM output to openssl) security find-certificate -c "Apple Distribution" login.keychain-db -p | openssl x509 -noout -enddate
Certificate Installation
# Import .p12 into Keychain (interactive — prompts for password) security import certificate.p12 -k ~/Library/Keychains/login.keychain-db -P "$P12_PASSWORD" -T /usr/bin/codesign # Import .cer into Keychain security import certificate.cer -k ~/Library/Keychains/login.keychain-db # For CI: import into temporary keychain (see CI section below)
Provisioning Profile Reference
Profile Types
| Type | Contains | Use Case |
|---|---|---|
| Development | Dev cert + device UDIDs + App ID + entitlements | Debug builds on registered devices |
| Ad Hoc | Distribution cert + device UDIDs + App ID + entitlements | Testing on specific devices without TestFlight |
| App Store | Distribution cert + App ID + entitlements (no device list) | App Store + TestFlight submission |
| Enterprise | Enterprise cert + App ID + entitlements (no device list) | In-house distribution (Enterprise program only) |
Profile Contents
A provisioning profile (.mobileprovision) is a signed plist containing:
├── AppIDName — App ID name ├── ApplicationIdentifierPrefix — Team ID ├── CreationDate — When profile was created ├── DeveloperCertificates — Embedded signing certificates (DER-encoded) ├── Entitlements — Granted entitlements │ ├── application-identifier │ ├── aps-environment (development|production) │ ├── com.apple.developer.associated-domains │ ├── keychain-access-groups │ └── ... ├── ExpirationDate — When profile expires (1 year) ├── Name — Profile name in Apple Developer Portal ├── ProvisionedDevices — UDIDs (Development/Ad Hoc only) ├── TeamIdentifier — Team ID array ├── TeamName — Team display name ├── TimeToLive — Days until expiration ├── UUID — Unique profile identifier └── Version — Profile version (1)
Decode Provisioning Profile
# Decode and display full contents security cms -D -i path/to/embedded.mobileprovision # Extract specific fields security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - - # Check aps-environment (push notifications) security cms -D -i embedded.mobileprovision | grep -A1 "aps-environment" # Check expiration security cms -D -i embedded.mobileprovision | grep -A1 "ExpirationDate" # List provisioned devices (Development/Ad Hoc only) security cms -D -i embedded.mobileprovision | grep -A100 "ProvisionedDevices" # Check team ID security cms -D -i embedded.mobileprovision | grep -A1 "TeamIdentifier"
Profile Installation Paths
# Installed profiles (Xcode manages these) ~/Library/MobileDevice/Provisioning Profiles/ # List installed profiles ls ~/Library/MobileDevice/Provisioning\ Profiles/ # Decode a specific installed profile security cms -D -i ~/Library/MobileDevice/Provisioning\ Profiles/<UUID>.mobileprovision # Find profile embedded in app bundle find ~/Library/Developer/Xcode/DerivedData -name "embedded.mobileprovision" -newer . 2>/dev/null | head -5 # Install a profile manually (copy to managed directory) cp MyProfile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
Entitlements Reference
Common Entitlements
| Entitlement | Key | Values |
|---|---|---|
| App ID | | |
| Push Notifications | | / |
| App Groups | | |
| Keychain Sharing | | |
| Associated Domains | | |
| iCloud | | Container IDs |
| HealthKit | | |
| Apple Pay | | Merchant IDs |
| Network Extensions | | Array of types |
| Siri | | |
| Sign in with Apple | | |
Entitlement .plist Format
<?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>aps-environment</key> <string>development</string> <key>com.apple.security.application-groups</key> <array> <string>group.com.example.shared</string> </array> <key>com.apple.developer.associated-domains</key> <array> <string>applinks:example.com</string> </array> </dict> </plist>
Extraction and Comparison
# Extract entitlements from signed app binary codesign -d --entitlements - /path/to/MyApp.app # Extract to file for comparison codesign -d --entitlements entitlements.plist /path/to/MyApp.app # Compare entitlements between app and provisioning profile diff <(codesign -d --entitlements - MyApp.app 2>/dev/null) \ <(security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - -) # Extract entitlements from .ipa unzip -o MyApp.ipa -d /tmp/ipa_contents codesign -d --entitlements - /tmp/ipa_contents/Payload/*.app # Verify entitlements match between build and profile codesign -d --entitlements - --xml MyApp.app # XML format
CLI Command Reference
security
Commands
security# --- Identity & Certificate --- # List valid code signing identities security find-identity -v -p codesigning # -v: valid only, -p codesigning: code signing policy # Find certificate by common name security find-certificate -c "Apple Distribution" login.keychain-db -p # -c: common name substring, -p: output PEM, keychain is positional arg # Find certificate by SHA-1 hash security find-certificate -Z -a login.keychain-db | grep -B5 "ABC123" # --- Import/Export --- # Import .p12 (with password, allow codesign access) security import certificate.p12 -k login.keychain-db -P "$PASSWORD" -T /usr/bin/codesign -T /usr/bin/security # Import .cer security import certificate.cer -k login.keychain-db # Export certificate to .p12 security export -t identities -f pkcs12 -k login.keychain-db -P "$PASSWORD" -o exported.p12 # --- Provisioning Profile Decode --- # Decode provisioning profile (CMS/PKCS7 signed plist) security cms -D -i embedded.mobileprovision # --- Keychain Management --- # Create temporary keychain (for CI) security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain # Set as default keychain security default-keychain -s build.keychain # Add to search list (required for codesign to find certs) security list-keychains -d user -s build.keychain login.keychain-db # Unlock keychain (required in CI before signing) security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain # Set keychain lock timeout (0 = never lock during session) security set-keychain-settings -t 3600 -l build.keychain # -t: timeout in seconds, -l: lock on sleep # Allow codesign to access keys without UI prompt (critical for CI) security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain # Delete keychain (CI cleanup) security delete-keychain build.keychain
codesign
Commands
codesign# --- Signing --- # Sign with specific identity codesign -s "Apple Distribution: Company Name (TEAMID)" MyApp.app # -s: signing identity (name or SHA-1 hash) # Sign with entitlements file codesign -s "Apple Distribution" --entitlements entitlements.plist MyApp.app # Force re-sign (overwrite existing signature) codesign -f -s "Apple Distribution" MyApp.app # Sign with timestamp (required for notarization) codesign -s "Developer ID Application" --timestamp MyApp.app # Deep sign (sign all nested code — frameworks, extensions) codesign --deep -s "Apple Distribution" MyApp.app # Warning: --deep is unreliable for complex apps. Sign each component individually. # --- Verification --- # Verify signature is valid codesign --verify --verbose=4 MyApp.app # Verify deep (check nested code) codesign --verify --deep --strict MyApp.app # Display signing information codesign -dv MyApp.app # Shows: Identifier, Format, TeamIdentifier, Signing Authority chain # Display verbose signing info codesign -dvvv MyApp.app # Extract entitlements from signed binary codesign -d --entitlements - MyApp.app codesign -d --entitlements - --xml MyApp.app # XML format
openssl
Commands
opensslNote: macOS ships with LibreSSL, not OpenSSL. Some
commands may fail with "MAC verification failed" on stock macOS. Install OpenSSL viaopenssl pkcs12if needed, then use the full path (brew install openssl)./opt/homebrew/opt/openssl/bin/openssl
# --- Certificate Inspection --- # View .cer details openssl x509 -in certificate.cer -inform DER -text -noout # View .pem details openssl x509 -in certificate.pem -text -noout # Check certificate expiration openssl x509 -in certificate.cer -inform DER -noout -enddate # Extract public key openssl x509 -in certificate.cer -inform DER -pubkey -noout # --- PKCS12 (.p12) --- # Extract certificate from .p12 openssl pkcs12 -in certificate.p12 -nokeys -clcerts -out cert.pem # Extract private key from .p12 openssl pkcs12 -in certificate.p12 -nocerts -nodes -out key.pem # Create .p12 from cert + key openssl pkcs12 -export -in cert.pem -inkey key.pem -out certificate.p12 # Verify .p12 contents openssl pkcs12 -info -in certificate.p12 -nokeys
Xcode Build Settings Reference
| Setting | Key | Values |
|---|---|---|
| Code Signing Style | | / |
| Signing Identity | | / / |
| Development Team | | Team ID (10-char alphanumeric) |
| Provisioning Profile | | Profile name or UUID |
| Provisioning Profile (legacy) | | Profile UUID (deprecated, use SPECIFIER) |
| Other Code Signing Flags | | / |
| Code Sign Entitlements | | Path to .entitlements file |
| Enable Hardened Runtime | | / (macOS) |
xcodebuild Signing Overrides
# Automatic signing xcodebuild -scheme MyApp -configuration Release \ CODE_SIGN_STYLE=Automatic \ DEVELOPMENT_TEAM=YOURTEAMID # Manual signing xcodebuild -scheme MyApp -configuration Release \ CODE_SIGN_STYLE=Manual \ CODE_SIGN_IDENTITY="Apple Distribution: Company Name (TEAMID)" \ PROVISIONING_PROFILE_SPECIFIER="MyApp App Store Profile" # Archive for distribution xcodebuild archive -scheme MyApp \ -archivePath build/MyApp.xcarchive \ CODE_SIGN_STYLE=Manual \ CODE_SIGN_IDENTITY="Apple Distribution" \ PROVISIONING_PROFILE_SPECIFIER="MyApp App Store" # Export .ipa from archive xcodebuild -exportArchive \ -archivePath build/MyApp.xcarchive \ -exportOptionsPlist ExportOptions.plist \ -exportPath build/ipa
ExportOptions.plist
<?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>method</key> <string>app-store</string> <key>teamID</key> <string>YOURTEAMID</string> <key>signingStyle</key> <string>manual</string> <key>signingCertificate</key> <string>Apple Distribution</string> <key>provisioningProfiles</key> <dict> <key>com.example.myapp</key> <string>MyApp App Store Profile</string> </dict> <key>uploadSymbols</key> <true/> </dict> </plist>
Export method values:
app-store, ad-hoc, enterprise, development, developer-id.
fastlane match Reference
Setup
# Initialize match (interactive — choose storage type) fastlane match init # Options: git, google_cloud, s3, azure_blob # Generate certificates + profiles for all types fastlane match development fastlane match appstore fastlane match adhoc
Matchfile
# fastlane/Matchfile git_url("https://github.com/your-org/certificates.git") storage_mode("git") type("appstore") # Default type app_identifier(["com.example.app", "com.example.app.widget"]) username("dev@example.com") team_id("YOURTEAMID") # For multiple targets with different profiles # for_lane(:beta) do # type("adhoc") # end
Usage
# Generate or fetch development certs + profiles fastlane match development # Generate or fetch App Store certs + profiles fastlane match appstore # CI: read-only mode (never create, only fetch) fastlane match appstore --readonly # Force regenerate (revokes existing) fastlane match nuke distribution # Revoke all distribution certs fastlane match appstore # Generate fresh # Also: nuke development, nuke enterprise
Environment Variables for CI
MATCH_GIT_URL="https://github.com/your-org/certificates.git" MATCH_PASSWORD="encryption_password" # Encrypts the repo MATCH_KEYCHAIN_NAME="fastlane_tmp" MATCH_KEYCHAIN_PASSWORD="keychain_password" MATCH_READONLY="true" # CI should never create certs FASTLANE_USER="dev@example.com" FASTLANE_TEAM_ID="YOURTEAMID"
CI Fastfile Example
# fastlane/Fastfile lane :release do setup_ci # Creates temporary keychain match( type: "appstore", readonly: true, # Critical: never create certs in CI keychain_name: "fastlane_tmp", keychain_password: "" ) build_app( scheme: "MyApp", export_method: "app-store" ) upload_to_app_store(skip_metadata: true, skip_screenshots: true) end
Keychain Management for CI
Complete CI Keychain Setup Script
#!/bin/bash set -euo pipefail KEYCHAIN_NAME="ci-build.keychain-db" KEYCHAIN_PASSWORD="ci-temporary-password" # 1. Create temporary keychain security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME" # 2. Add to search list (MUST include login.keychain-db or it disappears) security list-keychains -d user -s "$KEYCHAIN_NAME" login.keychain-db # 3. Unlock keychain security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME" # 4. Prevent keychain from locking during build security set-keychain-settings -t 3600 -l "$KEYCHAIN_NAME" # 5. Import signing certificate security import "$P12_PATH" -k "$KEYCHAIN_NAME" -P "$P12_PASSWORD" \ -T /usr/bin/codesign -T /usr/bin/security # 6. Allow codesign access without UI prompt (CRITICAL) # Without this, CI gets errSecInternalComponent security set-key-partition-list -S apple-tool:,apple: -s \ -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME" echo "Keychain ready for code signing"
CI Keychain Cleanup Script
#!/bin/bash # Run in CI post-build (always, even on failure) KEYCHAIN_NAME="ci-build.keychain-db" # Delete temporary keychain security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true # Restore default keychain search list security list-keychains -d user -s login.keychain-db
GitHub Actions Example
- name: Install signing certificate env: P12_BASE64: ${{ secrets.P12_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} PROVISION_PROFILE_BASE64: ${{ secrets.PROVISION_PROFILE_BASE64 }} run: | # Decode certificate echo "$P12_BASE64" | base64 --decode > certificate.p12 # Decode provisioning profile echo "$PROVISION_PROFILE_BASE64" | base64 --decode > profile.mobileprovision # Create and configure keychain security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security list-keychains -d user -s build.keychain login.keychain-db security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security set-keychain-settings -t 3600 -l build.keychain security import certificate.p12 -k build.keychain -P "$P12_PASSWORD" \ -T /usr/bin/codesign -T /usr/bin/security security set-key-partition-list -S apple-tool:,apple: -s \ -k "$KEYCHAIN_PASSWORD" build.keychain # Install provisioning profile mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/ - name: Cleanup keychain if: always() run: security delete-keychain build.keychain 2>/dev/null || true
Xcode Cloud
Xcode Cloud manages signing automatically:
- Certificates are managed by Apple — no manual cert management needed
- Provisioning profiles are fetched from Developer Portal
- Configure signing in Xcode → cloud workflow settings
- Use
for custom keychain operations if neededci_post_clone.sh
APNs Authentication: .p8 vs .p12
| Aspect | .p8 (Token-Based) | .p12 (Certificate-Based) |
|---|---|---|
| Validity | Never expires (revoke to invalidate) | 1 year (must renew annually) |
| Scope | All apps in team | Single App ID |
| Max per team | 2 keys | 1 cert per App ID |
| Setup complexity | Lower (one key for all apps) | Higher (per-app certificate) |
| Server implementation | JWT token generation required | TLS client certificate |
| Recommended | Yes (Apple's current recommendation) | Legacy (still supported) |
.p8 Key Usage
# Generate JWT for APNs (simplified — use a library in production) # Header: {"alg": "ES256", "kid": "KEY_ID"} # Payload: {"iss": "TEAM_ID", "iat": TIMESTAMP} # Sign with .p8 private key # JWT is valid for 1 hour — cache and refresh before expiry
.p12 Certificate Usage
# Send push with certificate authentication curl -v \ --cert-type P12 --cert apns-cert.p12:password \ --header "apns-topic: com.example.app" \ --header "apns-push-type: alert" \ --data '{"aps":{"alert":"Hello"}}' \ --http2 https://api.sandbox.push.apple.com/3/device/$TOKEN # Production: https://api.push.apple.com/3/device/$TOKEN
Error Codes Reference
security Command Errors
| Error | Code | Cause |
|---|---|---|
| errSecInternalComponent | -2070 | Keychain locked or not called |
| errSecItemNotFound | -25300 | Certificate/key not in searched keychains |
| errSecDuplicateItem | -25299 | Certificate already exists in keychain |
| errSecAuthFailed | -25293 | Wrong keychain password |
| errSecInteractionNotAllowed | -25308 | Keychain locked, no UI available (CI without unlock) |
| errSecMissingEntitlement | -34018 | App missing required entitlement for keychain access |
codesign Errors
| Error | Cause | Fix |
|---|---|---|
| No valid identity in keychain | Import cert or check expiration |
| Multiple matching identities | Specify full identity name or SHA-1 hash |
| Cert type mismatch (dev vs dist) | Use correct certificate type |
| Modified resources after signing | Re-sign after all modifications |
| Binary tampered post-signing | Re-sign or rebuild |
ITMS (App Store Upload) Errors
| Code | Error | Cause | Fix |
|---|---|---|---|
| ITMS-90035 | Invalid Signature | Wrong certificate type or expired cert | Sign with valid Apple Distribution cert |
| ITMS-90161 | Invalid Provisioning Profile | Profile doesn't match app | Regenerate profile in Developer Portal |
| ITMS-90046 | Invalid Code Signing Entitlements | Entitlements not in profile | Add capability in portal, regenerate profile |
| ITMS-90056 | Missing Push Notification Entitlement | aps-environment not in profile | Enable Push Notifications capability |
| ITMS-90174 | Missing Provisioning Profile | No profile embedded | Archive with correct signing settings |
| ITMS-90283 | Invalid Provisioning Profile | Profile expired | Download fresh profile |
| ITMS-90426 | Invalid Swift Support | Swift libraries not signed correctly | Use Xcode's organizer to export (not manual) |
| ITMS-90474 | Missing Bundle Identifier | Bundle ID doesn't match profile | Align bundle ID across Xcode, portal, and profile |
| ITMS-90478 | Invalid Team ID | Team ID mismatch | Verify DEVELOPMENT_TEAM build setting |
| ITMS-90717 | Invalid App Store Distribution Certificate | Using Development cert for App Store | Switch to Apple Distribution certificate |
Resources
WWDC: 2021-10204, 2022-110353
Docs: /security, /bundleresources/entitlements, /xcode/distributing-your-app
Skills: axiom-code-signing, axiom-code-signing-diag