#!/usr/bin/env bash set -euo pipefail # macOS signing identity sanity check for: # - Developer ID (outside Mac App Store) # - Mac App Store (Apple Distribution + 3rd Party Mac Developer Installer) ts() { date +"%H:%M:%S"; } if [[ "$(uname -s)" != "Darwin" ]]; then echo "Error: This script is for macOS only." >&2 exit 1 fi echo "==> [$(ts)] Strawberry macOS signing identity check" echo "==> [$(ts)] Host: $(sw_vers -productName 2>/dev/null || true) $(sw_vers -productVersion 2>/dev/null || true)" echo echo "==> [$(ts)] Keychains searched by 'security' (user)" security list-keychains -d user || true echo echo "==> [$(ts)] Valid code signing identities (must include private key)" codesigning_out="$(security find-identity -p codesigning -v 2>&1 || true)" echo "$codesigning_out" echo echo "==> [$(ts)] Valid installer/pkg identities (must include private key)" basic_out="$(security find-identity -p basic -v 2>&1 || true)" echo "$basic_out" echo echo "==> [$(ts)] Note" cat <<'EOF' - Apple uses multiple certificate types. The "basic" identity list can include certificates that are not usable for signing a Mac App Store upload package. - For App Store Connect uploads via .pkg, you typically need an *Installer* identity (e.g. "3rd Party Mac Developer Installer" or "Mac Installer Distribution") and it must have a private key on this Mac. EOF echo list_cert_labels() { local query="$1" # Extract "labl" lines like: "labl"="Apple Distribution: ..." security find-certificate -a -c "$query" 2>/dev/null \ | sed -n 's/.*"labl"="\(.*\)".*/\1/p' \ | sort -u } check_label_in_identities() { local label="$1" local out="$2" if echo "$out" | grep -Fq "$label"; then echo "YES" else echo "NO" fi } check_label_in_installer_identities() { local label="$1" local out="$2" # Only treat as installer-capable if the cert label itself is an installer cert. case "$label" in *Installer*|*installer*) ;; *) echo "NO"; return 0 ;; esac if echo "$out" | grep -Fq "$label"; then echo "YES" else echo "NO" fi } print_section() { local title="$1" shift local queries=("$@") echo "==> [$(ts)] ${title}" local any=0 local q for q in "${queries[@]}"; do local labels labels="$(list_cert_labels "$q" || true)" if [[ -z "$labels" ]]; then continue fi any=1 while IFS= read -r label; do [[ -z "$label" ]] && continue local in_codesign in_basic in_codesign="$(check_label_in_identities "$label" "$codesigning_out")" in_basic="$(check_label_in_installer_identities "$label" "$basic_out")" printf -- "- %s\n" "$label" printf -- " - codesigning identity: %s\n" "$in_codesign" printf -- " - installer identity: %s\n" "$in_basic" if [[ "$in_codesign" == "NO" && "$in_basic" == "NO" ]]; then printf -- " - note: certificate exists, but it is NOT a usable identity on this Mac (almost always missing private key)\n" fi done <<<"$labels" done if [[ "$any" -eq 0 ]]; then echo "(no matching certificates found)" fi echo } print_section "Expected for Developer ID (outside Mac App Store)" \ "Developer ID Application" \ "Developer ID Installer" print_section "Expected for Mac App Store submissions" \ "Apple Distribution" \ "Mac App Distribution" \ "3rd Party Mac Developer Application" \ "3rd Party Mac Developer Installer" \ "Mac Installer Distribution" echo "==> [$(ts)] Quick interpretation" cat <<'EOF' - If a certificate label appears above, but both: - codesigning identity: NO - installer identity: NO then the certificate is present but NOT usable for signing on this Mac. The most common cause is: the private key is missing. Fix: - Open Keychain Access → login → "My Certificates" - Expand the certificate. You must see a private key underneath it. - If there is no private key: - Recreate the certificate on this Mac via Xcode (Accounts → Manage Certificates), OR - Import a .p12 that includes the private key from the machine where it was created. EOF echo echo "==> [$(ts)] Provisioning profiles (Mac App Store builds require one)" prof_dir="${HOME}/Library/MobileDevice/Provisioning Profiles" if [[ -d "${prof_dir}" ]]; then ls -la "${prof_dir}" | head -n 50 else echo "(none found at '${prof_dir}')" fi