Skip to content

Re-signing & Crash Prevention

Why Apps May Crash After Data Migration

macOS's code signing mechanism (codesign) verifies the integrity of the application package, including file path structure. When AppPorts migrates an app's data directory to external storage and replaces it with a symbolic link, the signing seal is broken, causing the following issues:

  • Gatekeeper Block: codesign --verify --deep --strict detects signature failure; the system displays a "Damaged" or "from an unidentified developer" dialog, blocking app launch
  • Keychain Access Disruption: Apps relying on Keychain access groups cannot read stored credentials due to signature identity changes
  • Entitlements Failure: Some app entitlements are bound to the signing identity; after signature changes, entitlements mismatch

High-Risk App Types

App TypeRisk LevelReason
Sparkle self-updating appsHighUpdater may delete or replace the app, damaging symbolic links
Electron self-updating appsHighelectron-updater may also interfere with apps on external storage
Keychain-dependent appsHighAd-hoc signing changes the signature identity; Keychain access groups fail
Mac App Store appsHighSIP protection; cannot be re-signed
Native self-updating apps (Chrome, Edge)MediumSelf-update may replace external copy, invalidating local entry
iOS apps (Mac version)LowUses Stub Portal or whole symlink; fewer signing issues

High-Risk Data Directory Types

Data TypeRisk LevelReason
~/Library/Application Support/MediumApp may use file locks, SQLite WAL logs, or extended attributes; may behave abnormally across symbolic links
~/Library/Group Containers/MediumShared by multiple apps under the same Team; symbolic links may interfere with other apps
~/Library/Preferences/Low-Mediumcfprefsd caches plist files; symbolic links may cause reading stale data
~/Library/Caches/LowCaches are rebuildable; most apps handle cache absence gracefully

Re-signing Mechanism

Ad-hoc Signing

AppPorts uses Ad-hoc signing (certificate-less local signing) to fix app signatures after migration. Execution command:

bash
codesign --force --deep --sign - <app path>

Where - indicates Ad-hoc signing (without a developer certificate).

Signing Flow

mermaid
flowchart TD
    A[Start re-signing] --> B[Backup original signature identity]
    B --> C{Is app locked?}
    C -->|Yes| D[Temporarily unlock uchg flag]
    C -->|No| E{Is app writable?}
    D --> E
    E =>|Not writable & root-owned| F[Try to change ownership with admin]
    E =>|Writable| G[Clean extended attributes]
    F --> G
    F -->|Failed & MAS app| H[Skip signing - SIP protection]
    G --> I[Clean bundle root directory clutter]
    I --> J{Is Contents a symlink?}
    J =>|Yes| K[Temporarily replace with real directory copy]
    J =>|No| L[Execute deep signing]
    K --> L
    L =>|Failed| M[Fallback to shallow signing]
    L =>|Success| N{Was Contents temporarily replaced?}
    M --> N
    N =>|Yes| O[Restore symbolic link]
    N =>|No| P[Re-lock uchg flag]
    O --> P
    P => Q[Signing complete]

Key Steps

  1. Backup original signature identity: Before signing, read the app's current signature identity (parse Authority= lines via codesign -dvv), save to ~/Library/Application Support/AppPorts/signature-backups/<BundleID>.plist

  2. Clean extended attributes: Execute xattr -cr to remove resource forks, Finder info, etc., avoiding "detritus not allowed" errors during signing

  3. Clean bundle root directory: Remove .DS_Store, __MACOSX, .git, .svn, and other clutter

  4. Handle symbolic link Contents: If Contents/ is a symbolic link (Deep Contents Wrapper strategy), temporarily replace it with a real directory copy, then restore the symbolic link after signing

  5. Deep signing → shallow signing fallback: Prefer --deep signing (covering all nested components); if it fails due to permission or resource fork issues, fall back to shallow signing without --deep

  6. Retry mechanism: When codesign produces "internal error" or is terminated by SIGKILL, retry up to 2 times

Signature Backup & Restore

Backup

Backup files are stored in ~/Library/Application Support/AppPorts/signature-backups/ directory, named BundleID.plist:

FieldDescription
bundleIdentifierApp's Bundle ID
signingIdentityOriginal signature identity (e.g., Developer ID Application: ... or ad-hoc)
originalPathOriginal app path
backupDateBackup timestamp

Backups are triggered at these times:

  • Before data directory migration (if auto-re-signing is enabled)
  • Before any signing operation (idempotent; does not overwrite existing backups)

Restore

When restoring a signature, AppPorts executes different strategies based on the backed-up signature identity:

Backed-up Signature IdentityRestore Behavior
ad-hoc or emptyExecute codesign --remove-signature to remove signature; delete backup
Valid developer certificate identityCheck if certificate exists in Keychain. If present, re-sign with original identity
Valid developer certificate identity but certificate not on this machineFallback to Ad-hoc signing; original signature cannot be fully restored

Restore Failure Scenarios

The following scenarios cause signature restore failure or incompleteness:

ScenarioResult
Backup plist file does not existThrows noBackupFound error; cannot restore
Original developer certificate not in local KeychainFalls back to Ad-hoc signing. App can launch but Keychain access groups and some entitlements may fail
Mac App Store apps (SIP protection)Silently skipped. SIP prevents any modification to system app signatures
App directory not writable & root-ownedAttempts to change ownership via admin privileges. Fails if user cancels authorization prompt
Contents symbolic link target lostcopyItem fails in temporary replacement step; signing cannot be executed
User cancels admin authorizationThrows codesignFailed("User cancelled authorization")
Both deep and shallow signing failedError propagated upward; signing operation fails

⚠️ About Lost Developer Certificates

The most common real-world restore failure scenario is: the original app was signed by a third-party developer (e.g., Developer ID Application: Google LLC), but the current machine's Keychain does not have the corresponding private key. In this case, the restore operation can only generate an Ad-hoc signature; the original signature identity cannot be fully restored. For apps relying on specific signature identities for Keychain access groups or enterprise configuration profiles, this may cause functional anomalies.

最近更新