As imagens do SO Android usam assinaturas criptográficas em dois lugares:
- Cada arquivo
.apk
dentro da imagem precisa ser assinado. O Gerenciador de pacotes do Android usa uma assinatura.apk
de duas maneiras:- Quando um aplicativo é substituído, ele precisa ser assinado pela mesma chave do
aplicativo antigo para ter acesso aos dados do aplicativo antigo. Isso é
verdade tanto para atualizar apps do usuário substituindo o
.apk
quanto para substituir um app do sistema com uma versão mais recente instalada em/data
. - Se dois ou mais aplicativos quiserem compartilhar um ID de usuário (para compartilhar dados etc.), eles precisam ser assinados com a mesma chave.
- Quando um aplicativo é substituído, ele precisa ser assinado pela mesma chave do
aplicativo antigo para ter acesso aos dados do aplicativo antigo. Isso é
verdade tanto para atualizar apps do usuário substituindo o
- Os pacotes de atualização OTA precisam ser assinados com uma das chaves esperadas pelo sistema, ou o processo de instalação os rejeita.
Chaves de lançamento
A árvore do Android inclui test-keys em
build/target/product/security
. Criar uma imagem do SO Android
usando make
assinará todos os arquivos .apk
usando as
chaves de teste. Como as chaves de teste são de conhecimento público, qualquer pessoa pode assinar os próprios
arquivos .apk com as mesmas chaves, o que pode permitir a substituição ou o sequestro de apps
do sistema integrados à imagem do SO. Por esse motivo, é fundamental assinar qualquer
imagem do SO Android lançada publicamente ou implantada com um conjunto especial de
chaves de lançamento a que somente você tenha acesso.
Para gerar seu próprio conjunto exclusivo de chaves de lançamento, execute estes comandos na raiz da sua árvore Android:
subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media networkstack; do \ ./development/tools/make_key ~/.android-certs/$x "$subject"; \ done
O $subject
precisa ser alterado para refletir as informações da sua
organização. Você pode usar qualquer diretório, mas escolha um
local com backup e seguro. Alguns fornecedores optam por criptografar
a chave privada com uma senha longa e armazenar a chave criptografada
no controle de origem. Outros armazenam as chaves de lançamento em outro lugar,
como em um computador com isolamento físico.
Para gerar uma imagem de lançamento, use:
make dist
sign_target_files_apks \ -o \ # explained in the next section --default_key_mappings ~/.android-certs out/dist/*-target_files-*.zip \ signed-target_files.zip
O script sign_target_files_apks
usa um .zip
de arquivos de destino
como entrada e produz um novo .zip
de arquivos de destino
em que todos os arquivos .apk
foram assinados com novas chaves. As imagens
recém-assinhadas podem ser encontradas em IMAGES/
em
signed-target_files.zip
.
Assinar pacotes OTA
Um ZIP de arquivos de destino assinado pode ser convertido em um ZIP de atualização OTA assinado usando o seguinte procedimento:
ota_from_target_files \
-k (--package_key)
signed-target_files.zip \
signed-ota_update.zip
Assinaturas e sideloading
O sideload não ignora o mecanismo de verificação de assinatura de pacote normal da recuperação. Antes de instalar um pacote, a recuperação verifica se ele está assinado com uma das chaves privadas que correspondem às chaves públicas armazenadas na partição de recuperação, assim como faria para um pacote entregue por OTA.
Os pacotes de atualização recebidos do sistema principal geralmente são verificados duas vezes:
uma pelo sistema principal, usando o
método
RecoverySystem.verifyPackage()
na API do Android, e outra pela
recuperação. A API RecoverySystem verifica a assinatura em relação às chaves públicas
armazenadas no sistema principal, no arquivo /system/etc/security/otacerts.zip
(por padrão). A recuperação verifica a assinatura em relação às chaves públicas armazenadas
no disco RAM da partição de recuperação, no arquivo /res/keys
.
Por padrão, o .zip
de arquivos de destino produzidos pelo build define o
certificado OTA para corresponder à chave de teste. Em uma imagem lançada, um certificado
diferente precisa ser usado para que os dispositivos possam verificar a autenticidade do
pacote de atualização. Transmitir a flag -o
para
sign_target_files_apks
, conforme mostrado na seção anterior, substitui
o certificado da chave de teste pelo certificado da chave de lançamento do diretório
de certificados.
Normalmente, a imagem do sistema e a imagem de recuperação armazenam o mesmo conjunto de chaves públicas do OTA. Ao adicionar uma chave apenas ao conjunto de chaves de recuperação, é possível assinar pacotes que só podem ser instalados por sideload, supondo que o mecanismo de download de atualização do sistema principal esteja verificando corretamente otacerts.zip. É possível especificar chaves extras para serem incluídas apenas na recuperação definindo a variável PRODUCT_EXTRA_RECOVERY_KEYS na definição do produto:
vendor/yoyodyne/tardis/products/tardis.mk
[...] PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload
Isso inclui a chave pública
vendor/yoyodyne/security/tardis/sideload.x509.pem
no arquivo de chaves
de recuperação para que ele possa instalar pacotes assinados
com ela. A chave extra não está incluída em otacerts.zip. Portanto, os sistemas que verificam corretamente os pacotes transferidos por download não invocam a recuperação para
pacotes assinados com essa chave.
Certificados e chaves privadas
Cada chave vem em dois arquivos: o certificado, que tem a extensão .x509.pem, e a chave privada, que tem a extensão .pk8. A chave privada precisa ser mantida em segredo e é necessária para assinar um pacote. A chave pode ser protegida por uma senha. O certificado, por outro lado, contém apenas a metade pública da chave, para que possa ser distribuído amplamente. Ele é usado para verificar se um pacote foi assinado pela chave privada correspondente.
O build padrão do Android usa cinco chaves, todas localizadas em
build/target/product/security
:
- chave de teste
- Chave padrão genérica para pacotes que não especificam uma chave.
- do Android
- Chave de teste para pacotes que fazem parte da plataforma principal.
- compartilhado
- Teste a chave para itens compartilhados no processo de casa/contatos.
- mídia
- Chave de teste para pacotes que fazem parte do sistema de mídia/download.
Pacotes individuais especificam uma dessas chaves definindo LOCAL_CERTIFICATE no arquivo Android.mk. A chave de teste será usada se a variável não estiver definida. Também é possível especificar uma chave totalmente diferente pelo caminho, por exemplo:
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
Agora o build usa a chave device/yoyodyne/security/special.{x509.pem,pk8}
para assinar o SpecialApp.apk. O build só pode usar chaves privadas que
não estão protegidas por senha.
Opções de assinatura avançadas
Substituição da chave de assinatura do APK
O script de assinatura sign_target_files_apks
funciona nos arquivos
de destino gerados para um build. Todas as informações sobre certificados e chaves privadas usados no tempo de build são incluídas nos arquivos de destino. Ao executar o
script de assinatura para assinar a versão, as chaves de assinatura podem ser substituídas com base no nome
da chave ou do APK.
Use as flags --key_mapping
e --default_key_mappings
para especificar a substituição de chaves com base nos nomes delas:
- A sinalização
--key_mapping src_key=dest_key
especifica a substituição de uma chave por vez. - A flag
--default_key_mappings dir
especifica um diretório com cinco chaves para substituir todas as chaves embuild/target/product/security
. Ela é equivalente ao uso de--key_mapping
cinco vezes para especificar os mapeamentos.
build/target/product/security/testkey = dir/releasekey build/target/product/security/platform = dir/platform build/target/product/security/shared = dir/shared build/target/product/security/media = dir/media build/target/product/security/networkstack = dir/networkstack
Use a flag
--extra_apks apk_name1,apk_name2,...=key
para especificar as substituições de chave de assinatura com base nos nomes de APK. Se
key
for deixado em branco, o script vai tratar os APKs especificados
como pré-assinados.
Para o produto hipotético tardis, você precisa de seis chaves protegidas por senha:
cinco para substituir as cinco em build/target/product/security
e uma
para substituir a chave adicional device/yoyodyne/security/special
necessária pela SpecialApp no exemplo acima. Se as chaves estiverem nos seguintes
arquivos:
vendor/yoyodyne/security/tardis/releasekey.x509.pem vendor/yoyodyne/security/tardis/releasekey.pk8 vendor/yoyodyne/security/tardis/platform.x509.pem vendor/yoyodyne/security/tardis/platform.pk8 vendor/yoyodyne/security/tardis/shared.x509.pem vendor/yoyodyne/security/tardis/shared.pk8 vendor/yoyodyne/security/tardis/media.x509.pem vendor/yoyodyne/security/tardis/media.pk8 vendor/yoyodyne/security/tardis/networkstack.x509.pem vendor/yoyodyne/security/tardis/networkstack.pk8 vendor/yoyodyne/security/special.x509.pem vendor/yoyodyne/security/special.pk8 # NOT password protected vendor/yoyodyne/security/special-release.x509.pem vendor/yoyodyne/security/special-release.pk8 # password protected
Em seguida, você assinaria todos os apps assim:
./build/make/tools/releasetools/sign_target_files_apks \
--default_key_mappings vendor/yoyodyne/security/tardis \
--key_mapping vendor/yoyodyne/security/special=vendor/yoyodyne/security/special-release \
--extra_apks PresignedApp= \
-o tardis-target_files.zip \
signed-tardis-target_files.zip
Isso resulta no seguinte:
Enter password for vendor/yoyodyne/security/special-release key> Enter password for vendor/yoyodyne/security/tardis/networkstack key> Enter password for vendor/yoyodyne/security/tardis/media key> Enter password for vendor/yoyodyne/security/tardis/platform key> Enter password for vendor/yoyodyne/security/tardis/releasekey key> Enter password for vendor/yoyodyne/security/tardis/shared key> signing: Phone.apk (vendor/yoyodyne/security/tardis/platform) signing: Camera.apk (vendor/yoyodyne/security/tardis/media) signing: NetworkStack.apk (vendor/yoyodyne/security/tardis/networkstack) signing: Special.apk (vendor/yoyodyne/security/special-release) signing: Email.apk (vendor/yoyodyne/security/tardis/releasekey) [...] signing: ContactsProvider.apk (vendor/yoyodyne/security/tardis/shared) signing: Launcher.apk (vendor/yoyodyne/security/tardis/shared) NOT signing: PresignedApp.apk (skipped due to special cert string) rewriting SYSTEM/build.prop: replace: ro.build.description=tardis-user Eclair ERC91 15449 test-keys with: ro.build.description=tardis-user Eclair ERC91 15449 release-keys replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys signing: framework-res.apk (vendor/yoyodyne/security/tardis/platform) rewriting RECOVERY/RAMDISK/default.prop: replace: ro.build.description=tardis-user Eclair ERC91 15449 test-keys with: ro.build.description=tardis-user Eclair ERC91 15449 release-keys replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys using: vendor/yoyodyne/security/tardis/releasekey.x509.pem for OTA package verification done.
Depois de solicitar senhas ao usuário de todas as chaves protegidas por senha, o
script assina novamente todos os arquivos APK no destino de entrada .zip
com as
chaves de lançamento. Antes de executar o comando, você também pode definir a
variável de ambiente ANDROID_PW_FILE
para um nome de arquivo temporário. O
script invoca o editor para permitir que você insira senhas para todas as chaves.
Essa pode ser uma maneira mais conveniente de inserir senhas.
Substituição da chave de assinatura do APEX
O Android 10 introduz o formato de arquivo APEX para instalar módulos de sistema de nível inferior. Conforme explicado em Assinatura do APEX, cada arquivo APEX é assinado com duas chaves: uma para a imagem do minisistema de arquivos em um APEX e a outra para todo o APEX.
Ao assinar para lançamento, as duas chaves de assinatura de um arquivo APEX são substituídas
por chaves de lançamento. A chave de payload do sistema de arquivos é especificada com a
flag --extra_apex_payload
, e toda a chave de assinatura de arquivo APEX é
especificada com a flag --extra_apks
.
Para o produto tardis, suponha que você tenha a seguinte configuração de chave
para os arquivos APEX com.android.conscrypt.apex
,
com.android.media.apex
e
com.android.runtime.release.apex
.
name="com.android.conscrypt.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" name="com.android.media.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" name="com.android.runtime.release.apex" public_key="vendor/yoyodyne/security/testkeys/com.android.runtime.avbpubkey" private_key="vendor/yoyodyne/security/testkeys/com.android.runtime.pem" container_certificate="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.x509.pem" container_private_key="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.pk8"
E você tem os seguintes arquivos que contêm as chaves de lançamento:
vendor/yoyodyne/security/runtime_apex_container.x509.pem vendor/yoyodyne/security/runtime_apex_container.pk8 vendor/yoyodyne/security/runtime_apex_payload.pem
O comando a seguir substitui as chaves de assinatura para
com.android.runtime.release.apex
e
com.android.tzdata.apex
durante a assinatura da versão. Mais especificamente,
com.android.runtime.release.apex
é assinado com as chaves de lançamento
especificadas (runtime_apex_container
para o arquivo APEX e
runtime_apex_payload
para o payload da imagem do arquivo).
com.android.tzdata.apex
é tratado como pré-assinado. Todos os outros arquivos APEX
são processados pela configuração padrão, conforme listado nos arquivos de destino.
./build/make/tools/releasetools/sign_target_files_apks \
--default_key_mappings vendor/yoyodyne/security/tardis \
--extra_apks com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_container \
--extra_apex_payload_key com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_payload.pem \
--extra_apks com.android.media.apex= \
--extra_apex_payload_key com.android.media.apex= \
-o tardis-target_files.zip \
signed-tardis-target_files.zip
A execução do comando acima gera os seguintes registros:
[...] signing: com.android.runtime.release.apex container (vendor/yoyodyne/security/runtime_apex_container) : com.android.runtime.release.apex payload (vendor/yoyodyne/security/runtime_apex_payload.pem) NOT signing: com.android.conscrypt.apex (skipped due to special cert string) NOT signing: com.android.media.apex (skipped due to special cert string) [...]
Outras opções
O script de assinatura sign_target_files_apks
reescreve a descrição
e a impressão digital do build nos arquivos de propriedades do build para refletir que o
build é assinado. A flag --tag_changes
controla quais edições
são feitas na impressão digital. Execute o script com -h
para conferir
a documentação de todas as flags.
Gerar chaves manualmente
O Android usa chaves RSA de 2048 bits com expoente público 3. É possível gerar pares de chaves privadas/certificados usando a ferramenta openssl de openssl.org (em inglês):
# generate RSA keyopenssl genrsa -3 -out temp.pem 2048
Generating RSA private key, 2048 bit long modulus ....+++ .....................+++ e is 3 (0x3) # create a certificate with the public part of the keyopenssl req -new -x509 -key temp.pem -out releasekey.x509.pem -days 10000 -subj '/C=US/ST=California/L=San Narciso/O=Yoyodyne, Inc./OU=Yoyodyne Mobility/CN=Yoyodyne/emailAddress=yoyodyne@example.com'
# create a PKCS#8-formatted version of the private keyopenssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt
# securely delete the temp.pem fileshred --remove temp.pem
O comando openssl pkcs8 acima cria um arquivo .pk8 com nenhuma
senha, adequado para uso com o sistema de build. Para criar um arquivo .pk8 protegido
com uma senha (o que você precisa fazer para todas as chaves de lançamento reais), substitua o
argumento -nocrypt
por -passout stdin
. Em seguida, o openssl
vai criptografar a chave privada com uma senha lida da entrada padrão. Nenhum
prompt é impresso. Portanto, se o stdin for o terminal, o programa vai parecer travado
quando estiver apenas aguardando que você insira uma senha. Outros valores podem ser
usados para o argumento de passagem para ler a senha de outros locais. Para
mais detalhes, consulte a
documentação do openssl.
O arquivo intermediário temp.pem contém a chave privada sem nenhum tipo de proteção por senha. Portanto, descarte-o com cuidado ao gerar chaves de lançamento. Em particular, o utilitário GNUshred pode não ser eficaz em sistemas de arquivos de rede ou registrados. Use um diretório de trabalho localizado em um disco RAM (como uma partição tmpfs) ao gerar chaves para garantir que os intermediários não sejam expostos acidentalmente.
Criar arquivos de imagem
Quando você tiver signed-target_files.zip
, será necessário
criar a imagem para poder colocá-la em um dispositivo.
Para criar a imagem assinada com base nos arquivos de destino, execute
o comando abaixo na raiz da árvore
do Android:
img_from_target_files signed-target_files.zip signed-img.zipO arquivo resultante,
signed-img.zip
, contém todos os arquivos .img
.
Para carregar uma imagem em um dispositivo, use o fastboot desta
maneira:
fastboot update signed-img.zip