๐ Apple Certificate Installation Action
A comprehensive GitHub Action for installing Apple certificates and provisioning profiles in macOS workflows with secure keychain management and flexible configuration options.
โจ Features
- ๐ Secure Certificate Installation - Install Apple development/distribution certificates safely
- ๐ฑ Provisioning Profile Management - Install and configure iOS/macOS provisioning profiles
- ๐ Keychain Management - Create and configure temporary keychains with proper permissions
- ๐ก๏ธ Security-First Design - Secure handling of certificates, passwords, and private keys
- ๐ง Flexible Input Options - Support for base64 data, file paths, and multiple certificate types
- ๐ Detailed Outputs - Certificate fingerprints, profile UUIDs, and keychain information
- ๐งน Automatic Cleanup - Configurable keychain cleanup with secure file deletion
- ๐ Comprehensive Validation - Input validation and certificate verification
๐ Basic Usage
Install a certificate from base64 encoded data:
- name: "Install Apple Certificate"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: "Install certificate and provisioning profile"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE }}
certificate-password: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
provisioning-profile-data: ${{ secrets.IOS_PROVISIONING_PROFILE }}
certificate-type: "distribution"
- name: "Install from files"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-path: "certificates/ios_distribution.p12"
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
provisioning-profile-path: "profiles/app_store.mobileprovision"
๐ง Advanced Usage
Full configuration with all available options:
- name: "Complete iOS setup"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE }}
certificate-password: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
provisioning-profile-data: ${{ secrets.IOS_PROVISIONING_PROFILE }}
keychain-name: "ios-build.keychain"
keychain-password: ${{ secrets.KEYCHAIN_PASSWORD }}
delete-keychain: "false"
certificate-type: "distribution"
team-id: "ABCDEF1234"
allow-codesign-keychain-access: "true"
show-summary: "true"
๐ Permissions Required
This action requires standard repository permissions:
permissions:
contents: read # Required to checkout repository code
๐๏ธ CI/CD Example
Complete workflow for iOS app building with certificate installation:
name: "Build iOS App"
on:
push:
branches: ["main"]
tags: ["v*"]
pull_request:
branches: ["main"]
permissions:
contents: read
jobs:
build-ios:
runs-on: macos-latest
steps:
- name: "๐ฅ Checkout repository"
uses: actions/checkout@v4
- name: "๐ Install Apple Certificate"
id: certificate
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE }}
certificate-password: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
provisioning-profile-data: ${{ secrets.IOS_PROVISIONING_PROFILE }}
certificate-type: "distribution"
team-id: "ABCDEF1234"
keychain-name: "build.keychain"
delete-keychain: "false"
show-summary: "true"
- name: "๐ง Setup Xcode"
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: "๐ฆ Install dependencies"
run: |
cd ios
pod install --repo-update
- name: "๐จ Build iOS app"
run: |
xcodebuild archive \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-archivePath MyApp.xcarchive \
CODE_SIGN_IDENTITY="${{ steps.certificate.outputs.certificate-name }}" \
PROVISIONING_PROFILE_SPECIFIER="${{ steps.certificate.outputs.provisioning-profile-uuid }}" \
DEVELOPMENT_TEAM="${{ steps.certificate.outputs.team-id }}" \
-allowProvisioningUpdates
- name: "๐ฆ Export IPA"
run: |
xcodebuild -exportArchive \
-archivePath MyApp.xcarchive \
-exportPath ./build \
-exportOptionsPlist ExportOptions.plist
- name: "๐ค Upload build artifacts"
uses: actions/upload-artifact@v4
with:
name: "ios-app-${{ github.sha }}"
path: ./build/*.ipa
- name: "๐งน Cleanup keychain"
if: always()
run: |
security delete-keychain "${{ steps.certificate.outputs.keychain-path }}" || true
๐ Inputs
Required Inputs
| Input | Description | Example |
|---|---|---|
certificate-password |
Password for the certificate | ${{ secrets.CERT_PASSWORD }} |
Certificate Inputs (one required)
| Input | Description | Default | Example |
|---|---|---|---|
certificate-data |
Base64 encoded certificate (.p12) | '' |
${{ secrets.APPLE_CERT_DATA }} |
certificate-path |
Path to certificate file | '' |
certs/distribution.p12 |
Optional Inputs
| Input | Description | Default | Example |
|---|---|---|---|
provisioning-profile-data |
Base64 encoded provisioning profile | '' |
${{ secrets.PROFILE_DATA }} |
provisioning-profile-path |
Path to provisioning profile file | '' |
profiles/app.mobileprovision |
keychain-name |
Name for temporary keychain | build.keychain |
ios-build.keychain |
keychain-password |
Password for temporary keychain | build-keychain-password |
secure-password |
delete-keychain |
Delete keychain after installation | true |
false |
certificate-type |
Certificate type | development |
distribution |
team-id |
Apple Developer Team ID | '' |
ABCDEF1234 |
allow-codesign-keychain-access |
Allow codesign to access keychain | true |
false |
show-summary |
Show action summary | true |
false |
Certificate Types
development- Development certificates for testingdistribution- Distribution certificates for App Store/Ad Hocdeveloper-id- Developer ID certificates for macOS distribution
๐ค Outputs
| Output | Description | Example |
|---|---|---|
keychain-path |
Path to the created keychain | /Users/runner/Library/Keychains/build.keychain-db |
certificate-name |
Name of installed certificate | iPhone Distribution: My Company |
certificate-sha1 |
SHA1 fingerprint of certificate | 1234567890ABCDEF... |
provisioning-profile-name |
Name of installed profile | My App Store Profile |
provisioning-profile-uuid |
UUID of installed profile | 12345678-1234-1234-1234-123456789012 |
team-id |
Team ID from certificate or input | ABCDEF1234 |
๐ Related Actions
| Action | Purpose | Repository |
|---|---|---|
| ๐ข generate-version | Version generation for builds | laerdal/github_actions/generate-version |
| ๐ท๏ธ git-tag | Create build tags | laerdal/github_actions/git-tag |
| ๐ github-release | Create releases with binaries | laerdal/github_actions/github-release |
๐ก Examples
Complete iOS App Build Pipeline
- name: "Install development certificate"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.IOS_DEV_CERTIFICATE }}
certificate-password: ${{ secrets.IOS_CERT_PASSWORD }}
provisioning-profile-data: ${{ secrets.IOS_DEV_PROFILE }}
certificate-type: "development"
keychain-name: "dev.keychain"
- name: "Install distribution certificate"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.IOS_DIST_CERTIFICATE }}
certificate-password: ${{ secrets.IOS_CERT_PASSWORD }}
provisioning-profile-data: ${{ secrets.IOS_DIST_PROFILE }}
certificate-type: "distribution"
keychain-name: "dist.keychain"
macOS App Development
- name: "Install macOS developer certificate"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE }}
certificate-password: ${{ secrets.MACOS_CERT_PASSWORD }}
certificate-type: "developer-id"
team-id: "ABCDEF1234"
- name: "Build and sign macOS app"
run: |
xcodebuild archive \
-project MyMacApp.xcodeproj \
-scheme MyMacApp \
-configuration Release \
-archivePath MyMacApp.xcarchive \
CODE_SIGN_IDENTITY="Developer ID Application: My Company"
Persistent Keychain Setup
- name: "Setup persistent keychain"
id: keychain
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.CERTIFICATE_DATA }}
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
keychain-name: "persistent-build.keychain"
delete-keychain: "false"
- name: "Build app with persistent keychain"
run: |
# Your build commands here
xcodebuild -workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release
- name: "Manual keychain cleanup"
if: always()
run: |
security delete-keychain "${{ steps.keychain.outputs.keychain-path }}" || true
Multi-Certificate Installation
- name: "Install development certificate"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.DEV_CERTIFICATE }}
certificate-password: ${{ secrets.DEV_CERT_PASSWORD }}
provisioning-profile-data: ${{ secrets.DEV_PROFILE }}
certificate-type: "development"
keychain-name: "dev.keychain"
delete-keychain: "false"
- name: "Install distribution certificate"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.DIST_CERTIFICATE }}
certificate-password: ${{ secrets.DIST_CERT_PASSWORD }}
provisioning-profile-data: ${{ secrets.DIST_PROFILE }}
certificate-type: "distribution"
keychain-name: "dist.keychain"
delete-keychain: "false"
Environment-Specific Certificates
- name: "Install certificate for environment"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_CERT || secrets.DEVELOPMENT_CERT }}
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
provisioning-profile-data: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_PROFILE || secrets.DEVELOPMENT_PROFILE }}
certificate-type: ${{ github.ref == 'refs/heads/main' && 'distribution' || 'development' }}
๐ฑ Requirements
Prerequisites
- macOS runner - This action only works on macOS runners
- Apple Developer Account - Valid certificates and provisioning profiles
- GitHub Secrets - Securely store certificates and passwords
Dependencies
- macOS Security Framework - Built-in macOS tools (security, plutil)
- OpenSSL - For certificate information extraction (pre-installed on macOS)
Supported Platforms
- โ macOS (macos-latest, macos-13, macos-12)
- โ Linux (not supported)
- โ Windows (not supported)
๐ Security Best Practices
Storing Certificates and Passwords
Never commit certificates or passwords to your repository. Always use GitHub Secrets:
# โ
Secure - using secrets
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
# โ Insecure - never do this
certificate-data: "MIIKzAIBAzCCCogGCSqGSIb3DQ..."
certificate-password: "my-secret-password"
Preparing Certificates for GitHub Secrets
Export certificate from Keychain Access as .p12 with password protection
Convert to base64:
base64 -i certificate.p12 | pbcopyStore in GitHub Secrets:
- Go to repository Settings โ Secrets and variables โ Actions
- Add new secret with the base64 content
Provisioning Profile Setup
Download from Apple Developer Portal
Convert to base64:
base64 -i profile.mobileprovision | pbcopyStore in GitHub Secrets
Keychain Security Features
The action automatically:
- Creates temporary keychains with secure passwords
- Configures appropriate keychain settings for codesign access
- Enables codesign access when needed
- Cleans up keychains after use (if enabled)
- Sets restrictive file permissions (600) on temporary files
๐ Troubleshooting
Common Issues
Action Only Works on macOS
Problem: This action only works on macOS runners
Solution: Use a macOS runner:
runs-on: macos-latest # or macos-13, macos-12
Certificate Import Failures
Problem: security: SecKeychainItemImport: MAC verification failed during PKCS12 import
Solutions:
- Verify certificate password is correct
- Ensure certificate is not corrupted
- Check base64 encoding is valid
- name: "Validate certificate format"
run: |
echo "${{ secrets.CERTIFICATE_DATA }}" | base64 -d > temp-cert.p12
if ! openssl pkcs12 -in temp-cert.p12 -noout -passin pass:"${{ secrets.CERTIFICATE_PASSWORD }}"; then
echo "โ Invalid certificate or password"
exit 1
fi
rm temp-cert.p12
echo "โ
Certificate validation passed"
Provisioning Profile Issues
Problem: Provisioning profile file not found
Solutions:
- Verify base64 encoding of profile
- Check profile file exists at specified path
- Ensure profile is valid and not expired
- name: "Validate provisioning profile"
run: |
echo "${{ secrets.PROVISIONING_PROFILE_DATA }}" | base64 -d > temp-profile.mobileprovision
if ! plutil -lint temp-profile.mobileprovision; then
echo "โ Invalid provisioning profile"
exit 1
fi
rm temp-profile.mobileprovision
echo "โ
Provisioning profile validation passed"
Codesign Access Issues
Problem: User interaction is not allowed
Solutions:
- Ensure
allow-codesign-keychain-accessis set totrue - Check keychain password is correct
- Verify keychain is unlocked
- name: "Debug keychain access"
run: |
security list-keychains -d user
security find-identity -v -p codesigning
security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" "${{ steps.certificate.outputs.keychain-path }}"
Debug Mode
Enable verbose output for troubleshooting:
- name: "Debug certificate installation"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.CERTIFICATE_DATA }}
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
show-summary: "true"
env:
ACTIONS_STEP_DEBUG: true
Manual Verification
Verify installation manually:
- name: "Verify certificate installation"
run: |
security find-identity -v -p codesigning
security list-keychains -d user
ls -la "$HOME/Library/MobileDevice/Provisioning Profiles/"
๐ง Advanced Configuration
Custom Keychain Configuration
- name: "Install with custom keychain"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets.CERTIFICATE_DATA }}
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
keychain-name: "custom-build.keychain"
keychain-password: ${{ secrets.KEYCHAIN_PASSWORD }}
delete-keychain: "false"
allow-codesign-keychain-access: "true"
Multiple Team Support
strategy:
matrix:
team:
- { id: "TEAM1234", cert: "TEAM1_CERT", profile: "TEAM1_PROFILE" }
- { id: "TEAM5678", cert: "TEAM2_CERT", profile: "TEAM2_PROFILE" }
steps:
- name: "Install certificate for ${{ matrix.team.id }}"
uses: laerdal/github_actions/install-apple-certificate@main
with:
certificate-data: ${{ secrets[matrix.team.cert] }}
certificate-password: ${{ secrets.CERTIFICATE_PASSWORD }}
provisioning-profile-data: ${{ secrets[matrix.team.profile] }}
team-id: ${{ matrix.team.id }}
๐ License
This action is part of the GitHub Actions collection by Francois Raminosona.
๐ก Tip: Store your certificates and provisioning profiles as GitHub Secrets and use environment protection rules for production releases.