Pagine

2013-12-07

Firmare app e framework

Una delle novità di Mavericks è che non sia più possibile firmare un'applicazione con il proprio certificato sviluppatore se all'interno si trova un framework non firmato.
Ho scoperto questa "bella" novità quando ho deciso di re-introdurre l'utilizzo dell'utilissimo Sparkle per l'aggiornamento automatico delle applicazioni, per rilasci fuori Mac App Store. Giusto per completezza, la stessa situazione si trova comunque ogni volta che utilizziamo un framework esterno o mini-applicazioni per estendere le funzionalità della nostra app.
A pensarci, è anche giusto che sia stata introdotta questa sicurezza aggiuntiva: in effetti un framework potrebbe effettuare azioni potenzialmente dannose e nascondersi dentro alla nostra app; tuttavia, avrei preferito un bel banner che avvisasse di questo problema, prima di sbatterci il naso per un po' di tempo...

Il sintomo del problema compare quando tentiamo di compilare con il nostro bravo framework importato; il passaggio della firma fornisce un errore del genere:

codesign build/release.../myApp.app: code object is not signed at all
in subcomponent build/release..../Sparkle.framework 
codesign failed with exit code 1

La parte "in subcomponent" ci indica anche quale framework (o app) è responsabile della mancata firma.
Il suggerimento di alcuni siti di inserire il comando --deep non risolve: purtroppo questa opzione provoca sì la firma di tutti i bundle ai vari livelli, ma applicando le stesse proprietà richieste dall'app principale. Il risultato è che il procedimento di firma va a buon fine, ma quando un utente lancia la nostra applicazione, il Finder gli mostra un bel messaggio che lo invita a cestinarla, perché danneggiata. Il problema viene mostrato anche tramite il comando da Terminale spctl:

spctl --verbose=4 --assess myApp.app 
myApp.app: a sealed resource is missing or invalid

La soluzione, tutto sommato, è abbastanza semplice (una volta che la si conosce): basta infatti firmare, con lo stesso certificato dell'App, anche il framework, dopo che questo è stato copiato durante i passaggi della compilazione.
Per far questo, si seleziona il progetto e si va nella pagina delle Build Phases, dove avremo già aggiunto in precedenza una Copy Phase, per fare in modo che il framework sia copiato nel bundle dell'applicazione (precisamente, nella cartella Framework, che verrà quindi creata se non esiste). Dal menu Editor, scegliere Add Build Phase  Add Run Script Build Phase; lasciando la shell di default, scriviamo nel riquadro dedicato il seguente script (riferendosi al framework Sparkle):

LOCATION="${BUILT_PRODUCTS_DIR}"/"${FRAMEWORKS_FOLDER_PATH}" 
IDENTITY="Mac Developer: abcdef ghijk" 
codesign --verbose --force --sign "$IDENTITY" "$LOCATION/Sparkle.framework/Versions/A"

dove nella variabile IDENTITY andiamo ad inserire il nome del certificato da utilizzare (che dovrà essere lo stesso utilizzato per l'applicazione, altrimenti l'app verrà vista come danneggiata).
A questo punto il problema è risolto! Da notare che se avessimo altri framework o applicazioni da inserire nel bundle, avremo script aggiuntivi, uno per ciascuno di essi, da eseguire dopo la copia nel percorso adatto.

Per comodità, aggiungo a questo post anche i comandi shell per il controllo della firma.
Per verificare che tutto è a posto:

codesign --verify --verbose=4 myApp.app

Per essere sicuri che Gatekeeper accetti la nostra app (fuori dal Mac App Store):

spctl --verbose=4 --assess --type execute myApp.app

Per esaminare i requirement di una app:

codesign --display --requirements - myApp.app

Nessun commento:

Posta un commento