Sharing distribution certificate. automat.

One thing at a time

Importing and sharing iOS/macOS certificates and keys required for AppStore distribution of application can be pain 🔥 as we know it.

with a little command line, I can help myself a little bit.

Here, I'm building command line shell script that will load certificates if necessary.

What do I need:

  • certificate file
  • private key file
  • certificate identifier

Prepare

Download certificate file

Download certificate from https://developer.apple.com/account/ios/certificate/

see "Certificate identifier" section for more.

Export private key

The private key is probably in my Keychain. It was created when I created Certificate Signing Request (CSR) for certificate, as described at developer portal:

Search for private key in Keychain

then export key as Personal Information Exchange (.p12) file type.

and save next to certificate file.

As a result, I have two files saved locally:

  • G8DZVTHQJC.cer
  • G8DZVTHQJC.p12

Obtain certificate identifier

This step is optional, but I think it's good to keep things in order. Certificate have its own, unique identifier used by developer portal. This number is not exposed; I found it useful to identify certificates, though.

When it comes to extracting certificate identifier, it's not the easiest part. Unfortunately, the downloaded file is always named "ios_distribution.cer" 🤔 I will use some hackery to solve this puzzle.

"Copy Link Address" where I can find all identifiers I need:

...&teamId=67RAWLRX93&certificateId=G8DZVTHQJC&type=R58UK2EWSO

My certificate identifier is: G8DZVTHQJC

Script

With those two files, I can automate importing certificate and key to my local account.
First check if a certificate is already loaded, then import or discard.

With this command, I get fingerprint I can use to identify it in Keychain

$ openssl x509 -inform DER -in G8DZVTHQJC.cer -noout -fingerprint | sed 's/\://g' | sed 's/.*=//g'

> B373427D867D91A8C3EBE3EF55D239C126A0F2B4

then just import certificate and key

$ security -q import G8DZVTHQJC.cer -A -k ~/Library/Keychains/login.keychain -T /usr/bin/codesign -T /usr/bin/security 2>/dev/null   
$ security -q import G8DZVTHQJC.p12 -A -k ~/Library/Keychains/login.keychain -T /usr/bin/codesign -T /usr/bin/security 2>/dev/null

and the script (gist)

#!/bin/sh
# Usage: import.sh "directory/project/distribution/files" "G8DZVTHQJC"

ROOT_PATH="$1"
CER_FILENAME="$2".cer
P12_FILENAME="$2".p12

CERT_SHA1=`openssl x509 -inform DER -in $ROOT_PATH/$CER_FILENAME -noout -fingerprint | sed 's/\://g' | sed 's/.*=//g'`
if security find-identity -v -p codesigning | grep $CERT_SHA1 > /dev/null; then
    echo "Found valid certificate in Keychain"
else
    echo "Importing $CER_FILENAME"
    security -q import $ROOT_PATH/$CER_FILENAME -A -k ~/Library/Keychains/login.keychain -T /usr/bin/codesign -T /usr/bin/security 2>/dev/null   
    echo "Importing $P12_FILENAME"
    security -q import $ROOT_PATH/$P12_FILENAME -A -k ~/Library/Keychains/login.keychain -T /usr/bin/codesign -T /usr/bin/security 2>/dev/null
fi

echo "done."

Outstanding problem is that private key has to be shared among the developers. It's not safe to put it in project repo. It's a good idea to have separated git repository you can fetch and use files, then delete. The best option is add one security layer. Either create private key with a password (do not use an empty password) or encrypt files in external git repository using shared password. To do that I can use formula below and add it to the script:

encrypt:

$ openssl aes-256-cbc -in G8DZVTHQJC.p12 -out G8DZVTHQJC.p12.enc

decrypt:

$ openssl aes-256-cbc -d -in G8DZVTHQJC.p12.enc -out G8DZVTHQJC.p12

Installation

  1. create directory projectname/distribution
  2. download distribution certificate and save as projectname/distribution/TR234RGHSE.cer
  3. export private key and save as projectname/distribution/TR234RGHSE.p12
  4. run import script import.sh projectname/distribution TR234RGHSE

Summary

Shell script and stored certificates may automate in bootstrapping shared project.
I may use fastlane/match to archive the same goal. Simple script approach is a just simple tool to do one thing at a time, right.

What do you think? Let me know.