Accessing fastlane match certificates manually
I’ve recently worked a lot with fastlane match for managing iOS code-signing certificates and provisioning profiles in a CI/CD environment. It’s a fantastic tool that simplifies the process significantly. However, there are scenarios where you might need to access the certificates and profiles manually, outside of fastlane’s automated workflows — for instance, when debugging issues or integrating with other tools.
Here’s a quick guide on how to access fastlane match certificates manually:
-
Set up
pry, the interactive Ruby shell, by adding it to your Gemfile:gem 'pry' gem 'fastlane'Then run
bundle installto install the gem. -
Open a terminal and start a new interactive Ruby session using
bundle exec pry, so you have access to the fastlane dependencies:$ bundle exec pry -
Load the dependencies
matchandfastlane_corein the interactive shell:[1] pry(main)> require 'fastlane_core' [2] pry(main)> require 'match' -
Configure variables to access your match repository. These include the repository URL, branch, and the
MATCH_PASSWORDenvironment variable for decrypting the certificates:[3] pry(main)> git_url = "[email protected]:yourusername/your-match-repo.git" # Your match repository URL [4] pry(main)> git_branch = "main" # or your specific branch [5] pry(main)> ENV['MATCH_PASSWORD'] = "your_match_password" # Your match passwordWe define the match password by setting it as an environment variable so that the decryption logic can pick it up.
-
Create a
Match::Storageinstance of the typegitto interact with the match repository:# Create the storage for git (you can also use 'google_cloud', 's3', or 'azure' based on your setup) [6] pry(main)> storage = Match::Storage.from_params({ storage_mode: 'git', git_url: git_url, git_branch: git_branch }) => #<Match::Storage::GitStorage:0x0000000125d7e940 @branch="main", @clone_branch_directly=nil, @git_basic_authorization=nil, @git_bearer_authorization=nil, @git_full_name=nil, @git_private_key=nil, @git_url="[email protected]:yourusername/your-match-repo.git", @git_user_email=nil, @platform="", @shallow_clone=nil, @skip_docs=nil, @type=""> # Clone the repository to a temporary directory [7] pry(main)> storage.download [14:38:59]: Cloning remote git repo... [14:39:01]: Checking out branch main... => ["git checkout main"] # Access the working directory where the certificates and profiles are stored [8] pry(main)> storage.working_directory => "/var/folders/41/rdlp7tmj2x1_vwmp0b_gy9yh0000gn/T/d20251115-3103-av9s91" -
Create a
Match::Encryptioninstance to handle decryption of the certificates and profiles:# Create the encryption handler for git storage [9] pry(main)> encryption = Match::Encryption.for_storage_mode("git", { :working_directory=>storage.working_directory }) => #<Match::Encryption::OpenSSL:0x0000000125cb4938 @force_legacy_encryption=nil, @keychain_name=nil, @working_directory="/var/folders/41/rdlp7tmj2x1_vwmp0b_gy9yh0000gn/T/d20251115-3103-av9s91"> # Decrypt the files in the working directory [10] pry(main)> encryption.decrypt_files [14:45:44]: 🔓 Successfully decrypted certificates repo => ["/var/folders/41/rdlp7tmj2x1_vwmp0b_gy9yh0000gn/T/d20251115-3103-av9s91/certs/distribution/S7V6FQBH47.cer", "/var/folders/41/rdlp7tmj2x1_vwmp0b_gy9yh0000gn/T/d20251115-3103-av9s91/certs/distribution/S7V6FQBH47.p12", "/var/folders/41/rdlp7tmj2x1_vwmp0b_gy9yh0000gn/T/d20251115-3103-av9s91/profiles/appstore/AppStore_dev.philprime.app.mobileprovision"] -
Now you can access the decrypted certificates by opening the working directory.
Bonus: List all branches in the match repository
If you are managing certificates in multiple branches (e.g., for different teams or environments), you can list all branches in the match repository using the following code:
[11] pry(main)> Dir.chdir(storage.working_directory) do
FastlaneCore::CommandExecutor.execute(
command: "git --no-pager branch --list --no-color -r",
print_all: true,
print_command: true
)
end.split("\n").map { |b| b.strip.gsub("origin/", "") }
[15:09:47]: $ git --no-pager branch --list --no-color -r
[15:09:47]: ▸ origin/HEAD -> origin/main
[15:09:47]: ▸ origin/main
[15:09:47]: ▸ origin/foo
[15:09:47]: ▸ origin/bar
[15:09:47]: ▸ origin/foobar
=> ["HEAD -> main",
"main",
"foo",
"bar",
"foobar"]