Como assinar e notarizar um pacote de instalação com a Apple via linha de comando

Neste artigo você vai ver:

Os processos de assinatura e notarização de softwares com a Apple não são dos mais fáceis, ainda mais quando você não planeja usar o Mac Apple Store para distribuí-los.

Inclusive, existem várias fontes explicando como fazer esses processos para aplicativos que serão distribuídos através do Apple Store. Portanto, para pacotes que não são de aplicativos e pacotes de instalação, pode ser um pouco mais complicado, em particular se você precisar fazer aquilo via linha de comando, por exemplo, no seu pipeline de CI/CD.

Neste artigo, vamos explicar quais são os diversos passos necessários na assinatura e na notarização (“notarization” em inglês) de um pacote que você planeja distribuir. 

1. Glossário

Antes de começar, é bom lembrar o que entendemos por assinatura e notarização.

Assinatura 

A assinatura de código do seu instalador garante aos usuários que ele é de uma fonte conhecida e não foi modificado desde a última assinatura

Antes que seu instalador possa integrar serviços de distribuição, ser instalado em um dispositivo ou ser enviado à App Store, ele deve ser assinado com um certificado emitido pela Apple.

Notarização

Caso planeje distribuir seus softwares e/ou aplicativos em outros lugares que a Mac App Store, profissionais de desenvolvimento devem, além de usar um certificado de ID de desenvolvedor (Developer ID), enviar seu software para notarização pela Apple, também chamado de reconhecimento de firma.

O serviço de notarização da Apple é um sistema automatizado que verifica seu software em busca de conteúdo malicioso, verifica problemas de assinatura de código e retorna os resultados para você rapidamente. Se não houver problemas, o sistema gera um tíquete para você grampear em seu software; o serviço notarial também publica esse bilhete online onde o Gatekeeper pode encontrá-lo.

Novidades sobre processo de notarização: A partir do macOS 10.14.5, um software assinado com um certificado de ID do desenvolvedor deve ser notarizado para serem executadas. Caso não fizer isso, o sistema operacional não permitirá instalar o pacote, conforme o exemplo abaixo:


Exemplo de problema em instalar um pacote do spotify não notarizado pela Apple

Já, a partir do macOS 10.15, todos os softwares criados após 1º de junho de 2019 e distribuídos com o Developer ID devem ser notarizados. No entanto, você não é obrigado a autenticar o software que distribui pela Mac App Store, porque o processo de envio da App Store já inclui verificações de segurança equivalentes.

2. Premissas

  • uma conta Apple Developer Id (email + senha).
  • um Developer Id Installer (certificado).
  • um Developer Id Application (certificado).
  • um computador (ou uma máquina virtual / servidor) com o sistema operacional MacOS instalado.

3. Processo de assinatura

Primeiro, acredito que vale a pena destacar porque pode ser necessário dois certificados. 

O certificado de Developer Id Installer é usado para assinar o pacote do instalador do seu aplicativo/software para distribuição fora da Mac Apple Store.

Já o certificado de Developer Id Application é usado para assinar o código do seu aplicativo/software para distribuição fora da Mac Apple Store.

Tá, mas o que isso quer dizer?

Basicamente, se você assinar apenas o pacote de instalação, sem assinar o código dentro dele, o processo de notarização da Apple vai recusar seu pacote. Ou seja, para conseguir fazer o reconhecimento de firma da Apple para seu pacote de instalação, é preciso fazer uma assinatura INSIDE OUT, significando que é preciso assinar todos os arquivos usados no seu pacote (tais como binários, bibliotecas, scripts…).

Resumindo: o Developer Id Application vai ser usado para assinar o código, e o Developer Id Installer vai ser usado para assinar o pacote. É preciso dos dois para conseguir passar pelo fluxo de notarização da Apple.

Passo a passo para realizar o processo de assinatura

– Passo 1: Adicionar os dois certificados a máquina (ou servidor) que vai cuidar do processo de assinatura.

– Passo 2: Usar o Developer Id Application para assinar todos os arquivos que vão compor nosso pacote. Para fazer isso no MacOS, usamos o comando codesign:

codesign -s "$DEVELOPER_ID_APPLICATION_NAME" -f --timestamp -i $BUNDLE_ID -o runtime "$FILENAME"
  • o DEVELOPER_ID_APPLICATION_NAME é o nome do certificado, geralmente segue o padrão “organization or account name (123456789)”.
  • o BUNDLE_ID é um identificador, geralmente segue o padrão “com.org.nameapp”.
  • o FILENAME é o nome do arquivo (ou o path até ele) que vai ser assinado.

– Passo 3: Gerar o pacote de instalação usando os arquivos assinados no passo 2. Existem diversas maneiras de fazer isso de acordo com o contexto da aplicação. Por exemplo, para gerar um pacote PKG, recomendo usar o script localizado neste repositório.

– Passo 4: Assinar o pacote gerado no passo 3. Para fazer isso no MacOS, usamos o comando productsign:

productsign --sign "$DEVELOPER_ID_INSTALLER_NAME" $INPUT_FILE_PATH $OUTPUT_FILE_PATH
  • o DEVELOPER_ID_INSTALLER_NAME é o nome do certificado, geralmente segue o padrão “organization or account name (123456789)”.
  • o INPUT_FILE_PATH é o path até o pacote que vai ser assinado.
  • o OUTPUT_FILE_PATH é o path até o pacote assinado que vai ser gerado pelo comando.

Pronto! Após esses quatro passos, o seu pacote de instalação terá sido assinado corretamente e estará pronto para passar pelo processo de notarização.

4. Processo de notarização (reconhecimento de firma)

Para conseguir fazer o processo de notarização via linha de comando, é preciso ter o Xcode instalado.

Nota: Esse processo também pode ser realizado através da interface gráfica do Xcode seguindo a documentação oficial.

Via linha de comandos, precisamos usar o CLI (que vem junto com o pacote do Xcode) usando o comando xcrun.

– Passo 1: Adicionar o identificador da conta do Apple Developer Id ao Keychain.

xcrun altool --store-password-in-keychain-item "$ITEM_NAME" -u "$ACCOUNT_USERNAME_OR_EMAIL" -p '$ACCOUNT_PASSWORD'
  • o ITEM_NAME é o identificador do item, pode ser qualquer valor, só vamos precisar ele mais para frente.
  • o ACCOUNT_USERNAME_OR_EMAIL é o e-mail ou o nome da conta do Apple Developer ID (cf premissas).
  • o ACCOUNT_PASSWORD é a senha da conta do Apple Developer ID (cf premissas).

– Passo 2: Enviar o pacote de instalação para o sistema de notarização da Apple.

xcrun altool --notarize-app --primary-bundle-id "$BUNDLE_ID" --username "$ACCOUNT_USERNAME_OR_EMAIL" --password "@keychain:$ITEM_NAME" --file $INPUT_FILE_PATH --verbose
  • o BUNDLE_ID é um identificador, geralmente segue o padrão “com.org.nameapp.format”.
  • o ITEM_NAME é o identificador do item que adicionamos ao Keychain no passo 1.
  • o ACCOUNT_USERNAME_OR_EMAIL é o e-mail ou o nome da conta do Apple Developer ID (cf premissas).
  • o INPUT_FILE_PATH é o path até o pacote que vai ser notarizado.

Nota: Sugiro usar a flag verbose para acompanhar o que está ocorrendo e extrair o UUID da requisição de notarização.

– Passo 3: Acompanhar o processo de notarização.

Nota: A Apple costuma enviar um e-mail cadastrado na conta do Apple Developer ID para confirmar a notarização, mas em um fluxo automatizado, é possível acompanhar esse processo via linha de comandos também.

O comando xcrun altool –notarization-history 0 -p “@keychain:$ITEM_NAME retorna os UUID de todas as requisições que foram enviadas para o sistema da Apple. Caso você não tenha extraído o UUID no passo 2, seria necessário usar esse para identificar qual UUID corresponde ao seu processo.

Para ter os detalhes de um processo específico, usar o comando:

xcrun altool --notarization-info "$REQUEST_UUID" --username "$ACCOUNT_USERNAME_OR_EMAIL" --password "@keychain:$ITEM_NAME"  
  • o ITEM_NAME é o identificador do item que adicionamos ao Keychain no passo 1.
  • o ACCOUNT_USERNAME_OR_EMAIL é o e-mail ou o nome da conta do Apple Developer ID (cf premissas).
  • o REQUEST_UUID é o identificador do processo de notarização no sistema da Apple.

Nota: Usando a flag verbose, é possível até obter uma URL retornando um json onde tem os detalhes resumindo porque o processo pode ter sido reprovado pelo sistema da Apple.

– Passo 4: Carimbar o pacote de instalação. Esse processo é chamado de Staple em inglês. É quando associamos o tíquete gerado pelo sistema notarial da Apple no pacote de instalação, esse serviço também publica esse bilhete online onde o Gatekeeper de uma máquina MacOS pode encontrá-lo. Esse processo não é obrigatório, mas caso a máquina não tenha acesso ao Gatekeeper (online), garante que o pacote de instalação esteja aprovado.

xcrun stapler staple "$INPUT_FILE_PATH"

Para validar que foi carimbado com sucesso:

xcrun stapler validate  "$INPUT_FILE_PATH"

Pronto! Após esses quatro passos, o seu pacote de instalação terá sido notarizado corretamente e estará pronto a ser distribuído.

5. Processo completo em um fluxo de CI/CD, usando Github Actions

O exemplo abaixo vai demonstrar os processos de assinatura e notarização de um pacote de instalação no formato PKG dentro de um runner MacOS no Github Actions.

Premissas:

Além das premissas destacadas no capítulo 2. Premissas deste artigo, é recomendado adicionar os certificados encriptados como base64 nos secrets do repositório Github onde usaremos o Github Actions.

Também precisamos ter um job anterior que fez o upload dos arquivos que vão ser usados para gerar o pacote no formato PKG em um artefato, para a gente usá-los no processo.

– Passo 1: Cada certificado deve ser separado em dois base64, um do arquivo no formato .cer e o outro com a chave atrelada num arquivo no formato .key.

Isso poderia ser feito via shell direto usando:

----- Create certificate files from secrets base64 -----
echo ${{ secrets.DEVELOPER_ID_INSTALLER_CER }} | base64 --decode > certificate_installer.cer
echo ${{ secrets.DEVELOPER_ID_INSTALLER_KEY }} | base64 --decode > certificate_installer.key
echo ${{ secrets.DEVELOPER_ID_APPLICATION_CER }} | base64 --decode > certificate_application.cer
echo ${{ secrets.DEVELOPER_ID_APPLICATION_KEY }} | base64 --decode > certificate_application.key


Por que fazer isso?

– Passo 2: Como o runner MacOs gerado pelo Github Actions não tem o Keychain configurado por padrão, precisamos de alguns processos extras para adicionar os certificados em um Keychain temporário para conseguir usá-los para assinar o nosso código e o pacote de instalação.

Para adicionar os certificados no Keychain, é preciso converter eles para o formato .p12, para isso usamos o comando openssl:

----- Create p12 file -----
openssl pkcs12 -export -name $ORG -in certificate_installer.cer -inkey certificate_installer.key -out certificate_installer.p12 -passout pass:${{ secrets.P12_PASSWORD }}
openssl pkcs12 -export -name $ORG -in certificate_application.cer -inkey certificate_application.key -out certificate_application.p12 -passout pass:${{ secrets.P12_PASSWORD }}

Nota: Aqui o valor do P12_PASSWORD pode ser qualquer um, já que que definido e usado apenas no workflow.

Cuidados: Caso a chave do certificado seja encriptada, é preciso adicionar a flag -passin pass:${{ secrets.KEY_PASSWORD }} após a flag -inkey <FILE>.

Em sequência, é preciso configurar um Keychain temporário no nosso runner através dos comandos abaixo apresentados dentro da documentação oficial do Github Actions:

----- Configure Keychain ----- 
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db 
security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" $KEYCHAIN_PATH 
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH 
security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" $KEYCHAIN_PATH

Nota: Aqui o valor do KEYCHAIN_PASSWORD pode ser qualquer um, já que que definido e usado apenas no workflow.

E finalmente, adicionar nossos dois certificados no formato .p12 ao Keychain temporário do runner:

----- Import certificates on Keychain ----- 
security import certificate_installer.p12 -P "${{ secrets.P12_PASSWORD }}" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH 
security import certificate_application.p12 -P "${{ secrets.P12_PASSWORD }}" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH 
security list-keychain -d user -s $KEYCHAIN_PATH

Pronto, a partir desse momento, o Keychain está configurado com nossos dois certificados.

– Passo 3: Para a assinatura dos arquivos e do pacote PKG usando os certificados, vamos usar o mesmo processo que foi explicado nesse artigo nos capítulos anteriores: codesign, script de geração do pkg e product sign.

----- Codesign files with script -----
# Usar um script para assinar cada arquivo (ref: https://gist.github.com/GuillaumeFalourd/4efc73f1a6014b791c0ef223a023520a)

----- Generate PKG from codesigned files -----
# Usar o script de macos installer (ref: https://github.com/KosalaHerath/macos-installer-builder/tree/master/macOS-x64)

----- Sign PKG file ----- 
productsign --sign "${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}" $INPUT_FILE_PATH $OUTPUT_FILE_PATH

– Passo 4: Para fazer a notarização, o Github Action já possui dois actions no marketplace que podem nos ajudar com isso:

- name: "Notarize Release Build PKG" 
     uses: devbotsxyz/xcode-notarize@v1 
     with: 
          product-path: $PATH_TO_PKG 
          appstore-connect-username: ${{ secrets.APPLE_ACCOUNT_USERNAME }} 
          appstore-connect-password: ${{ secrets.APPLE_ACCOUNT_PASSWORD }} 
          primary-bundle-id: 'BUNDLE_ID' 

- name: "Staple Release Build" 
     uses: devbotsxyz/xcode-staple@v1 
     with: 
         product-path: $PATH_TO_PKG

Pronto! Após esses quatro passos, o seu pacote de instalação terá sido notarizado corretamente e estará pronto a ser distribuído, por exemplo, em um outro job interagindo com uma Cloud (neste caso, usar a action/upload-artifact para compartilhar no job seguinte).


A implementação do job completo no Github Actions pode ser encontrado abaixo:

Conclusão

Por hoje é isso galera!

Espero que não tenha ficado dúvidas sobre assinatura e notarização de pacotes de instalação Apple via linha de comando. Caso tenha alguma questão ou sugestão, fique a vontade para comentar ou enviar mensagem para mim.

Referências

Capa do artigo sobre Assinatura e notarização de pacotes de instalação Apple via linha de comando, onde existe um Macbook com linhas de comando em sua tela com mãos no teclado prestes a digitar.
foto Guillaume Falourd
Back-end Developer
Zupper tentando transformar o complexo em simples através de conteúdos diversificados, com intuito de impactar o mercado de TI e as pessoas ao seu redor da melhor forma possível.

Este site utiliza cookies para proporcionar uma experiência de navegação melhor. Consulte nossa Política de Privacidade.