As imagens do sistema operacional Android usam assinaturas criptográficas em dois lugares:
- Cada arquivo
.apk
dentro da imagem deve ser assinado. O Gerenciador de Pacotes do Android usa uma assinatura.apk
de duas maneiras:- Quando um aplicativo é substituído, ele deve ser assinado pela mesma chave do aplicativo antigo para obter acesso aos dados do aplicativo antigo. Isso vale tanto para atualizar aplicativos do usuário substituindo o
.apk
quanto para substituir um aplicativo do sistema por uma versão mais recente instalada em/data
. - Se dois ou mais aplicativos quiserem compartilhar um ID de usuário (para que possam compartilhar dados, etc.), eles deverão ser assinados com a mesma chave.
- Quando um aplicativo é substituído, ele deve ser assinado pela mesma chave do aplicativo antigo para obter acesso aos dados do aplicativo antigo. Isso vale tanto para atualizar aplicativos do usuário substituindo o
- Os pacotes de atualização OTA devem ser assinados com uma das chaves esperadas pelo sistema ou o processo de instalação os rejeitará.
Liberar teclas
A árvore do Android inclui chaves de teste em build/target/product/security
. Construir uma imagem do sistema operacional Android usando make
assinará todos os arquivos .apk
usando as chaves de teste. Como as chaves de teste são conhecidas publicamente, qualquer pessoa pode assinar seus próprios arquivos .apk com as mesmas chaves, o que pode permitir substituir ou sequestrar aplicativos do sistema integrados à imagem do sistema operacional. Por esse motivo, é fundamental assinar qualquer imagem do sistema operacional Android lançada ou implantada publicamente com um conjunto especial de chaves de liberação às quais somente você tem acesso.
Para gerar seu próprio conjunto exclusivo de chaves de liberação, 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
$subject
deve ser alterado para refletir as informações da sua organização. Você pode usar qualquer diretório, mas tome cuidado para escolher um local com backup e seguro. Alguns fornecedores optam por criptografar sua chave privada com uma senha forte e armazenar a chave criptografada no controle de origem; outros armazenam suas chaves de liberação em algum outro lugar, como em um computador isolado.
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
pega um .zip
de arquivos de destino como entrada e produz um novo .zip
de arquivos de destino no qual todos os arquivos .apk
foram assinados com novas chaves. As imagens recém-assinadas podem ser encontradas em IMAGES/
signed-target_files.zip
.
Assine 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 sideload
O sideload não ignora o mecanismo normal de verificação de assinatura de pacote da recuperação – antes de instalar um pacote, a recuperação verificará 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 através do pacote. -ar.
Os pacotes de atualização recebidos do sistema principal normalmente são verificados duas vezes: uma vez pelo sistema principal, usando o método RecoverySystem.verifyPackage()
na API do Android e, novamente, por 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, os arquivos de destino .zip
produzidos pela compilação definem o certificado OTA para corresponder à chave de teste. Numa imagem divulgada, deve ser utilizado um certificado diferente para que os dispositivos possam verificar a autenticidade do pacote de atualização. Passar o sinalizador -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 seu diretório certs.
Normalmente, a imagem do sistema e a imagem de recuperação armazenam o mesmo conjunto de chaves públicas OTA. Ao adicionar uma chave apenas ao conjunto de chaves de recuperação, é possível assinar pacotes que podem ser instalados apenas por meio de sideload (assumindo que o mecanismo principal de download de atualização do sistema esteja fazendo a verificação corretamente em relação ao otacerts.zip). Você pode especificar chaves extras a serem incluídas somente 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 possa instalar pacotes assinados com ele. A chave extra não está incluída em otacerts.zip, portanto, os sistemas que verificam corretamente os pacotes baixados não invocam a recuperação de pacotes assinados com esta chave.
Certificados e chaves privadas
Cada chave vem em dois arquivos: o certificado , que possui a extensão .x509.pem, e a chave privada , que possui a extensão .pk8. A chave privada deve ser mantida em segredo e é necessária para assinar um pacote. A própria chave pode ser protegida por uma senha. O certificado, por outro lado, contém apenas a metade pública da chave, portanto pode ser amplamente distribuído. É usado para verificar se um pacote foi assinado pela chave privada correspondente.
A compilação padrão do Android usa cinco chaves, todas residindo em build/target/product/security
:
- chave de teste
- Chave padrão genérica para pacotes que não especificam uma chave.
- plataforma
- Chave de teste para pacotes que fazem parte da plataforma principal.
- compartilhado
- Chave de teste para itens compartilhados no processo inicial/contatos.
- meios de comunicação
- Chave de teste para pacotes que fazem parte do sistema de mídia/download.
Pacotes individuais especificam uma dessas chaves definindo LOCAL_CERTIFICATE em seu arquivo Android.mk. (testkey é usado se esta variável não estiver definida.) Você também pode especificar uma chave totalmente diferente por nome de caminho, por exemplo:
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
Agora a compilação usa a chave device/yoyodyne/security/special.{x509.pem,pk8}
para assinar SpecialApp.apk. A compilação pode usar apenas chaves privadas que não sejam protegidas por senha.
Opções avançadas de assinatura
Substituição da chave de assinatura do APK
O script de assinatura sign_target_files_apks
funciona nos arquivos de destino gerados para uma compilação. Todas as informações sobre certificados e chaves privadas usadas no momento da construção estão incluídas nos arquivos de destino. Ao executar o script de assinatura para assinar a liberação, as chaves de assinatura podem ser substituídas com base no nome da chave ou do APK.
Use os sinalizadores --key_mapping
e --default_key_mappings
para especificar a substituição de chaves com base nos nomes das chaves:
- O sinalizador
--key_mapping src_key = dest_key
especifica a substituição de uma chave por vez. - O sinalizador
--default_key_mappings dir
especifica um diretório com cinco chaves para substituir todas as chaves embuild/target/product/security
; é equivalente a usar--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 o sinalizador --extra_apks apk_name1,apk_name2,... = key
para especificar as substituições de chave de assinatura com base nos nomes do APK. Se key
for deixada em branco, o script tratará os APKs especificados como pré-assinados.
Para o hipotético produto 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
exigida por SpecialApp no exemplo acima. Se as chaves estivessem 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
Então você assinaria todos os aplicativos 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 traz o 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 ao usuário as senhas de todas as chaves protegidas por senha, o script assina novamente todos os arquivos APK no destino de entrada .zip
com as chaves de liberação. Antes de executar o comando, você também pode definir a variável de ambiente ANDROID_PW_FILE
como um nome de arquivo temporário; o script então invoca seu editor para permitir que você insira senhas para todas as chaves (esta pode ser uma maneira mais conveniente de inserir senhas).
Substituição da chave de assinatura APEX
O Android 10 apresenta o formato de arquivo APEX para instalação de módulos de sistema de nível inferior. Conforme explicado em Assinatura APEX , cada arquivo APEX é assinado com duas chaves: uma para a imagem do mini sistema de arquivos dentro de um APEX e outra para todo o APEX.
Ao assinar para liberação, as duas chaves de assinatura de um arquivo APEX são substituídas por chaves de liberação. A chave de carga útil do sistema de arquivos é especificada com o sinalizador --extra_apex_payload
e toda a chave de assinatura do arquivo APEX é especificada com o sinalizador --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 liberação:
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 de com.android.runtime.release.apex
e com.android.tzdata.apex
durante a assinatura da versão. Em particular, com.android.runtime.release.apex
é assinado com as chaves de versão especificadas ( runtime_apex_container
para o arquivo APEX e runtime_apex_payload
para a carga útil da imagem do arquivo). com.android.tzdata.apex
é tratado como pré-assinado. Todos os outros arquivos APEX são tratados pela configuração padrão listada 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 fornece os seguintes logs:
[...] 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 da compilação e a impressão digital nos arquivos de propriedades da compilação para refletir que a compilação é assinada. O sinalizador --tag_changes
controla quais edições são feitas na impressão digital. Execute o script com -h
para ver a documentação de todos os sinalizadores.
Gerar chaves manualmente
O Android usa chaves RSA de 2.048 bits com expoente público 3. Você pode gerar pares de certificados/chaves privadas usando a ferramenta openssl em openssl.org :
# 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 fornecido acima cria um arquivo .pk8 sem senha, adequado para uso com o sistema de compilação. Para criar um .pk8 protegido por senha (o que você deve fazer para todas as chaves de lançamento reais), substitua o argumento -nocrypt
por -passout stdin
; então o openssl criptografará a chave privada com uma senha lida na entrada padrão. Nenhum prompt é impresso, portanto, se stdin for o terminal, o programa parecerá travado quando estiver apenas esperando que você insira uma senha. Outros valores podem ser usados para o argumento-passout para ler a senha de outros locais; para obter detalhes, consulte a documentação do openssl .
O arquivo intermediário temp.pem contém a chave privada sem qualquer tipo de proteção por senha, portanto, descarte-a com cuidado ao gerar chaves de liberação. Em particular, o utilitário GNUshred pode não ser eficaz em sistemas de arquivos de rede ou com diário. Você pode usar 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 inadvertidamente.
Crie arquivos de imagem
Depois de signed-target_files.zip
, você precisa criar a imagem para poder colocá-la em um dispositivo. Para criar a imagem assinada a partir dos arquivos de destino, execute o seguinte comando na raiz da árvore 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 fastboot da seguinte forma:fastboot update signed-img.zip