Las imágenes del SO Android usan firmas criptográficas en dos lugares:
- Cada archivo
.apk
dentro de la imagen debe estar firmado. El administrador de paquetes de Android usa una firma.apk
de dos maneras:- Cuando se reemplaza una aplicación, se debe firmar con la misma clave que la aplicación anterior para acceder a los datos de esta. Esto se aplica tanto a la actualización de apps del usuario con la sobreescritura de
.apk
como a la anulación de una app del sistema con una versión más reciente instalada en/data
. - Si dos o más aplicaciones quieren compartir un ID de usuario (para poder compartir datos, etc.), deben firmarse con la misma clave.
- Cuando se reemplaza una aplicación, se debe firmar con la misma clave que la aplicación anterior para acceder a los datos de esta. Esto se aplica tanto a la actualización de apps del usuario con la sobreescritura de
- Los paquetes de actualización OTA deben firmarse con una de las claves que espera el sistema, o el proceso de instalación los rechazará.
Claves de versión
El árbol de Android incluye test-keys en build/target/product/security
. Compilar una imagen del SO Android con make
firmará todos los archivos .apk
con las claves de prueba. Dado que las claves de prueba son de conocimiento público, cualquier persona puede firmar sus propios archivos .apk con las mismas claves, lo que puede permitirle reemplazar o secuestrar apps del sistema integradas en la imagen del SO. Por este motivo, es fundamental firmar cualquier imagen del SO Android que se lance o implemente de forma pública con un conjunto especial de claves de lanzamiento a las que solo tú tienes acceso.
Para generar tu propio conjunto único de claves de lanzamiento, ejecuta estos comandos desde la raíz de tu árbol de 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
se debe cambiar para reflejar la información de tu organización. Puedes usar cualquier directorio, pero ten cuidado de elegir una ubicación segura y con copias de seguridad. Algunos proveedores eligen encriptar su clave privada con una frase de contraseña segura y almacenar la clave encriptada en el control de código fuente. Otros almacenan sus claves de lanzamiento en otro lugar, como en una computadora aislada.
Para generar una imagen de versión, usa el siguiente comando:
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
La secuencia de comandos sign_target_files_apks
toma un .zip
de archivos de destino como entrada y produce un nuevo .zip
de archivos de destino en el que todos los archivos .apk
se firmaron con claves nuevas. Las imágenes recién firmadas se pueden encontrar en IMAGES/
en signed-target_files.zip
.
Cómo firmar paquetes de OTA
Un archivo ZIP de target-files firmado se puede convertir en un archivo ZIP de actualización OTA firmado con el siguiente procedimiento:
ota_from_target_files \
-k (--package_key)
signed-target_files.zip \
signed-ota_update.zip
Firmas y carga lateral
La carga lateral no omite el mecanismo normal de verificación de firma de paquetes de recuperación. Antes de instalar un paquete, la recuperación verificará que esté firmado con una de las claves privadas que coincidan con las claves públicas almacenadas en la partición de recuperación, tal como lo haría con un paquete entregado de forma inalámbrica.
Por lo general, los paquetes de actualización que se reciben del sistema principal se verifican dos veces: una vez por el sistema principal, con el método RecoverySystem.verifyPackage()
en la API de Android, y otra vez por la recuperación. La API de RecoverySystem verifica la firma con las claves públicas almacenadas en el sistema principal, en el archivo /system/etc/security/otacerts.zip
(de forma predeterminada). La recuperación verifica la firma con las claves públicas almacenadas en el disco RAM de la partición de recuperación, en el archivo /res/keys
.
De forma predeterminada, los .zip
archivos de destino que produce la compilación establecen el certificado de OTA para que coincida con la clave de prueba. En una imagen lanzada, se debe usar un certificado diferente para que los dispositivos puedan verificar la autenticidad del paquete de actualización. Pasar la marca -o
a sign_target_files_apks
, como se muestra en la sección anterior, reemplaza el certificado de clave de prueba por el certificado de clave de lanzamiento de tu directorio de certificados.
Normalmente, la imagen del sistema y la imagen de recuperación almacenan el mismo conjunto de claves públicas de OTA. Si se agrega una clave solo al conjunto de claves de recuperación, es posible firmar paquetes que solo se pueden instalar a través de la carga lateral (suponiendo que el mecanismo de descarga de actualizaciones del sistema principal realice correctamente la verificación con otacerts.zip). Puedes especificar claves adicionales para que se incluyan solo en la recuperación configurando la variable PRODUCT_EXTRA_RECOVERY_KEYS en la definición del producto:
vendor/yoyodyne/tardis/products/tardis.mk
[...] PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload
Esto incluye la clave pública vendor/yoyodyne/security/tardis/sideload.x509.pem
en el archivo de claves de recuperación para que pueda instalar paquetes firmados con ella. Sin embargo, la clave adicional no se incluye en otacerts.zip, por lo que los sistemas que verifican correctamente los paquetes descargados no invocan la recuperación para los paquetes firmados con esta clave.
Certificados y claves privadas
Cada clave viene en dos archivos: el certificado, que tiene la extensión .x509.pem, y la clave privada, que tiene la extensión .pk8. La clave privada debe mantenerse en secreto y es necesaria para firmar un paquete. La clave puede estar protegida por una contraseña. En cambio, el certificado solo contiene la mitad pública de la clave, por lo que se puede distribuir ampliamente. Se usa para verificar que un paquete se haya firmado con la clave privada correspondiente.
La compilación estándar de Android usa cinco claves, todas ellas ubicadas en
build/target/product/security
:
- testkey
- Es la clave predeterminada genérica para los paquetes que no especifican una clave de otro modo.
- de Android
- Es la clave de prueba para los paquetes que forman parte de la plataforma principal.
- compartido
- Es una clave de prueba para elementos que se comparten en el proceso de contactos o de la casa.
- contenido multimedia
- Es la clave de prueba para los paquetes que forman parte del sistema de medios o descargas.
Los paquetes individuales especifican una de estas claves configurando LOCAL_CERTIFICATE en su archivo Android.mk. (testkey se usa si no se establece esta variable). También puedes especificar una clave completamente diferente por ruta de acceso, p.ej.:
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
Ahora la compilación usa la clave device/yoyodyne/security/special.{x509.pem,pk8}
para firmar SpecialApp.apk. La compilación solo puede usar claves privadas que no estén protegidas con contraseña.
Opciones de firma avanzadas
Reemplazo de la clave de firma de APK
La secuencia de comandos de firma sign_target_files_apks
funciona en los archivos de destino generados para una compilación. Toda la información sobre los certificados y las claves privadas que se usan durante la compilación se incluye en los archivos de destino. Cuando ejecutas la secuencia de comandos de firma para firmar la versión de lanzamiento, las claves de firma se pueden reemplazar según el nombre de la clave o el nombre del APK.
Usa las marcas --key_mapping
y --default_key_mappings
para especificar el reemplazo de claves según los nombres de las claves:
- La marca
--key_mapping src_key=dest_key
especifica el reemplazo de una clave a la vez. - La marca
--default_key_mappings dir
especifica un directorio con cinco claves para reemplazar todas las claves enbuild/target/product/security
. Es equivalente a usar--key_mapping
cinco veces para especificar las asignaciones.
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
Usa la marca --extra_apks apk_name1,apk_name2,...=key
para especificar los reemplazos de claves de firma según los nombres de los APKs. Si key
se deja vacío, la secuencia de comandos trata los APKs especificados como pre-firmados.
Para el producto hipotético de la cabina telefónica, necesitas seis claves protegidas con contraseña: cinco para reemplazar las cinco de build/target/product/security
y una para reemplazar la clave adicional device/yoyodyne/security/special
que requiere SpecialApp en el ejemplo anterior. Si las claves estuvieran en los siguientes archivos:
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
Luego, firmarías todas las apps de la siguiente manera:
./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
Aparecerá lo siguiente:
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.
Después de solicitarle al usuario las contraseñas de todas las claves protegidas con contraseña, la secuencia de comandos vuelve a firmar todos los archivos APK del destino de entrada .zip
con las claves de lanzamiento. Antes de ejecutar el comando, también puedes establecer la variable de entorno ANDROID_PW_FILE
en un nombre de archivo temporal. Luego, la secuencia de comandos invocará tu editor para permitirte ingresar contraseñas para todas las claves (esta puede ser una forma más conveniente de ingresar contraseñas).
Reemplazo de la clave de firma de APEX
Android 10 introduce el formato de archivo APEX para instalar módulos del sistema de nivel inferior. Como se explica en Firma de APEX, cada archivo APEX se firma con dos claves: una para la imagen del sistema de archivos mini dentro de un APEX y otra para todo el APEX.
Cuando se firma para el lanzamiento, las dos claves de firma de un archivo APEX se reemplazan por claves de lanzamiento. La clave de carga útil del sistema de archivos se especifica con la marca --extra_apex_payload
, y la clave de firma del archivo APEX completo se especifica con la marca --extra_apks
.
Para el producto tardis, supón que tienes la siguiente configuración de claves para los archivos APEX com.android.conscrypt.apex
, com.android.media.apex
y 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"
Y tienes los siguientes archivos que contienen las claves de lanzamiento:
vendor/yoyodyne/security/runtime_apex_container.x509.pem vendor/yoyodyne/security/runtime_apex_container.pk8 vendor/yoyodyne/security/runtime_apex_payload.pem
El siguiente comando anula las claves de firma para com.android.runtime.release.apex
y com.android.tzdata.apex
durante la firma de la versión. En particular, com.android.runtime.release.apex
se firma con las claves de versión especificadas (runtime_apex_container
para el archivo APEX y runtime_apex_payload
para la carga útil de la imagen del archivo).
com.android.tzdata.apex
se trata como prefirmado. Todos los demás archivos APEX se controlan con la configuración predeterminada que se indica en los archivos 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
Cuando ejecutas el comando anterior, se generan los siguientes 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) [...]
Otras opciones
La secuencia de comandos de firma sign_target_files_apks
vuelve a escribir la descripción y la huella digital de la compilación en los archivos de propiedades de la compilación para reflejar que la compilación es una compilación firmada. La marca --tag_changes
controla qué ediciones se realizan en la huella dactilar. Ejecuta la secuencia de comandos con -h
para ver la documentación de todas las marcas.
Genera claves de forma manual
Android usa claves RSA de 2,048 bits con un exponente público de 3. Puedes generar pares de certificados y claves privadas con la herramienta openssl de 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
El comando openssl pkcs8 que se indicó anteriormente crea un archivo .pk8 sin contraseña, adecuado para usar con el sistema de compilación. Para crear un archivo .pk8 protegido con una contraseña (lo que debes hacer para todas las claves de lanzamiento reales), reemplaza el argumento -nocrypt
por -passout stdin
. Luego, OpenSSL encriptará la clave privada con una contraseña leída desde la entrada estándar. No se imprime ningún mensaje, por lo que, si stdin es la terminal, parecerá que el programa se bloquea cuando, en realidad, solo está esperando que ingreses una contraseña. Se pueden usar otros valores para el argumento -passout para leer la contraseña de otras ubicaciones. Para obtener más detalles, consulta la
documentación de openssl.
El archivo intermedio temp.pem contiene la clave privada sin ningún tipo de protección con contraseña, por lo que debes desecharlo con cuidado cuando generes claves de lanzamiento. En particular, es posible que la utilidad GNUshred no sea eficaz en sistemas de archivos de red o con registro. Puedes usar un directorio de trabajo ubicado en un disco RAM (como una partición tmpfs) cuando generes claves para asegurarte de que los elementos intermedios no se expongan de forma inadvertida.
Crea archivos de imagen
Cuando tienes signed-target_files.zip
, debes crear la imagen para poder colocarla en un dispositivo.
Para crear la imagen firmada a partir de los archivos de destino, ejecuta el siguiente comando desde la raíz del árbol de Android:
img_from_target_files signed-target_files.zip signed-img.zip
signed-img.zip
, contiene todos los archivos .img
.
Para cargar una imagen en un dispositivo, usa fastboot de la siguiente manera:
fastboot update signed-img.zip