Post

Exporting Apple Distribution Certificates for CI/CD the Right Way

Exporting Apple Distribution Certificates for CI/CD the Right Way

When you’re setting up CI/CD for iOS projects using GitHub Actions, one of the first real blockers is code signing. In particular, if you’re trying to use an exported .p12 file of your Apple Distribution certificate, chances are high you will run into an error that no valid code signing identity has been found in the keychain of your GitHub action.

If you output valid signing identities (which you should only for debugging purposes), you might see something like this:

1
2
3
4
5
6
Run security find-identity -p codesigning -v
    0 valid identities found
[a-guid].mobileprovision
[a-guid].mobileprovision
[a-guid].mobileprovision
[a-guid].mobileprovision

This happened to me. I exported my distribution certificate from my Mac, converted it to a base64 string and saved it into a GitHub action secret. I repeated the process, only to see the same result again - no valid signing certificate available, even though I provided it.

The Problem with .p12 Exported from Keychain Access

In the end, I asked ChatGPT to help me troubleshoot this problem instead of just searching the web. ChatGPT first recommended me to run this command on the exported .p12 file:

1
openssl pkcs12 -in your.p12 -info -nodes

And I saw this result:

1
2
3
4
5
MAC: sha1, Iteration 1
MAC length: 20, salt length: 8
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Error outputting keys and certificates
400847EC01000000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto/evp/evp_fetch.c:355:Global default library context, Algorithm (RC2-40-CBC : 0), Properties ()

As you can see, the export used the RC2-40-CBC cipher, which has been deprecated for good reasons — but macOS still exports .p12 files with it by default.

Why does this matter?

Modern versions of OpenSSL 3.0 and later — used by macOS and GitHub Actions runners — have disabled support for RC2 by default, because it is considered insecure and obsolete.

From the OpenSSL 3.0 release notes:

“Some cryptographic algorithms that were previously included in the default provider are now only available in the legacy provider. These include RC2, RC4, and others.”

OpenSSL 3.0 Legacy Algorithms Guide

So if you’re getting this error, it’s not your fault — it’s OpenSSL being (understandably) picky for security reasons.

When Did This Break?

If you’re wondering like me when this became a problem: it started with the release of OpenSSL 3.0, back in September 2021. That version introduced the concept of “providers” and moved several legacy algorithms — including RC2, RC4, and MD2 — into the legacy provider, which is disabled by default.

GitHub Actions runners (macos-12 and later) now use OpenSSL 3.x on macOS by default, so any .p12 file encrypted with RC2 (like those exported from macOS Keychain) will no longer be accepted unless you enable legacy support — or, better yet, repackage your cert with a supported cipher.

OpenSSL 3.0 release notes

The Solution: Repackage the Certificate Manually

The fix for the above problem is to extract the certificate and private key from the RC2-encrypted .p12, and then repackage them into a new .p12 using modern encryption.

Step 1: Extract PEM Files Using OpenSSL in Legacy Mode

Export your original .p12 from Keychain Access, and name it something like apple_dist_raw.p12.

Then, use OpenSSL with the -legacy flag to bypass the RC2 issue:

1
2
openssl pkcs12 -in apple_dist_raw.p12 -clcerts -nokeys -out cert.pem -legacy
openssl pkcs12 -in apple_dist_raw.p12 -nocerts -nodes -out key.pem -legacy

You’ll be prompted for your export password — use the one you originally set.

If everything works, you now have:

  • cert.pem — the distribution certificate
  • key.pem — your private key in plain text

Step 2: Repackage into a Clean .p12

Now, combine both into a fresh .p12 that avoids RC2 entirely:

1
2
3
4
5
6
openssl pkcs12 -export \
  -inkey key.pem \
  -in cert.pem \
  -out fixed_cert.p12 \
  -name "Apple Distribution: [YOUR (TEAM) NAME] (YOUR TEAM ID)" \
  -passout "pass:[YOUR_EXPORT_PASSWORD]"

Replace the values in angle brackets with your own values. Remember to save the password, as you will put that into a GitHub action secret.

Note that you won’t get any confirmation in your terminal. You have to verify the existence of the file in Finder.

Step 3: Validate the Final .p12

Before uploading to GitHub, make sure the final file works:

1
openssl pkcs12 -in fixed_cert.p12 -info -nodes

This time, you should see:

  • Your Apple Distribution identity
  • Your certificate data
  • Your private key data

If you see all that — you’re good to go.

Step 4: Base64 Encode for GitHub Actions

Finally, we are able to encode the new .p12 file for GitHub:

1
base64 -i fixed_cert.p12 -o cert_base64.txt

You can now paste the contents of cert_base64.txt into your DISTRIBUTION_CERT_P12 secret, and use the same password for DISTRIBUTION_CERT_P12_PW.

If you put out the signing identity in your GitHub action again, you should see now something like this:

1
2
3
4
5
6
7
Run security find-identity -p codesigning -v
  1) 0195BCD9574C7F93BFEDD19AFA640897 "Apple Distribution: [YOUR (TEAM) NAME] (YOUR TEAM ID)"
     1 valid identities found
[a-guid].mobileprovision
[a-guid].mobileprovision
[a-guid].mobileprovision
[a-guid].mobileprovision

Conclusion

This process took more digging than it should have. I’m writing this down not only for my future self, but also to save others from burning time on cryptic OpenSSL errors and code signing quirks.

If you’re setting up GitHub Actions for a .NET MAUI iOS project (or any iOS project, really), getting your certificate in shape is a foundational step. Hopefully, this post helps you get there faster.

Disclaimer: As stated already above, I had some help from ChatGPT to get to the root of the issue and shape this blog post. The final content is mine, but for the troubleshooting part, ChatGPT was an invaluable help. Additionally, the title image for this post was generated using AI.

This post is licensed under CC BY 4.0 by the author.