Firmar compilaciones para su lanzamiento

Las imágenes del sistema operativo Android usan firmas criptográficas en dos lugares:

  1. 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, debe estar firmada con la misma clave que la aplicación anterior para poder acceder a los datos de la aplicación anterior. Esto es válido tanto para actualizar aplicaciones de usuario sobrescribiendo el .apk como para anular una aplicación del sistema con una versión más nueva instalada en /data .
    • Si dos o más aplicaciones quieren compartir una identificación de usuario (para que puedan compartir datos, etc.), deben estar firmadas con la misma clave.
  2. Los paquetes de actualización OTA deben estar firmados con una de las claves esperadas por el sistema o el proceso de instalación los rechazará.

Liberar teclas

El árbol de Android incluye claves de prueba en build/target/product/security . La creación de una imagen del sistema operativo Android con make firmará todos los archivos .apk con las claves de prueba. Dado que las claves de prueba son de conocimiento público, cualquiera puede firmar sus propios archivos .apk con las mismas claves, lo que puede permitirles reemplazar o secuestrar aplicaciones del sistema integradas en la imagen de su sistema operativo. Por esta razón, es fundamental firmar cualquier imagen del sistema operativo Android lanzada o implementada públicamente con un conjunto especial de claves de lanzamiento a las que solo usted tiene acceso.

Para generar su propio conjunto único de claves de lanzamiento, ejecute estos comandos desde la raíz de su á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 debe cambiarse para reflejar la información de su organización. Puede usar cualquier directorio, pero tenga cuidado de elegir una ubicación que tenga una copia de seguridad y sea segura. Algunos proveedores optan por cifrar su clave privada con una frase de contraseña segura y almacenar la clave cifrada en el control de código fuente; otros almacenan sus claves de liberación en otro lugar completamente diferente, como en una computadora con espacio de aire.

Para generar una imagen de lanzamiento, 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

El script 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 han firmado con claves nuevas. Las imágenes recién firmadas se pueden encontrar en IMAGES/ en signed-target_files.zip .

Firma de paquetes OTA

Un zip de archivos de destino firmado se puede convertir en un zip de actualización OTA firmado mediante el siguiente procedimiento:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Firmas y sideloading

La instalación de prueba no pasa por alto el mecanismo normal de verificación de la firma del paquete 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 a través de Internet. -aire.

Los paquetes de actualización recibidos del sistema principal generalmente se verifican dos veces: una vez por el sistema principal, usando el método RecoverySystem.verifyPackage() en la API de Android, y luego otra vez por recuperación. La API de RecoverySystem comprueba 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 compara 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, el archivo .zip de los archivos de destino producido por la compilación configura el certificado OTA para que coincida con la clave de prueba. En una imagen publicada, se debe usar un certificado diferente para que los dispositivos puedan verificar la autenticidad del paquete de actualización. Pasar el indicador -o a sign_target_files_apks , como se muestra en la sección anterior, reemplaza el certificado de clave de prueba con el certificado de clave de versión de su directorio de certificados.

Normalmente, la imagen del sistema y la imagen de recuperación almacenan el mismo conjunto de claves públicas OTA. Al agregar una clave solo al conjunto de claves de recuperación, es posible firmar paquetes que solo se pueden instalar a través de la instalación de prueba (suponiendo que el mecanismo de descarga de actualizaciones del sistema principal realiza correctamente la verificación con otacerts.zip). Puede especificar claves adicionales para que se incluyan solo en la recuperación configurando la variable PRODUCT_EXTRA_RECOVERY_KEYS en la definición de su producto:

vendor/yoyodyne/tardis/products/tardis.mk
 [...]

PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload

Esto incluye el vendor/yoyodyne/security/tardis/sideload.x509.pem en el archivo de claves de recuperación para que pueda instalar paquetes firmados con él. Sin embargo, la clave adicional no está incluida en otacerts.zip, por lo que los sistemas que verifican correctamente los paquetes descargados no invocan la recuperación de 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. El certificado, por el contrario, contiene solo la mitad pública de la clave, por lo que puede distribuirse ampliamente. Se utiliza para verificar que un paquete ha sido firmado por la clave privada correspondiente.

La compilación estándar de Android usa cinco claves, todas las cuales residen en build/target/product/security :

clave de prueba
Clave predeterminada genérica para paquetes que de otro modo no especifican una clave.
plataforma
Clave de prueba para paquetes que forman parte de la plataforma principal.
compartido
Clave de prueba para las cosas que se comparten en el proceso de inicio/contactos.
medios de comunicación
Clave de prueba para paquetes que forman parte del sistema de medios/descargas.
pila de red
Clave de prueba para paquetes que forman parte del sistema de red. La clave de la pila de red se utiliza para firmar archivos binarios diseñados como componentes del sistema modular . Si las actualizaciones de su módulo se construyen por separado y se integran como preconstruidos en la imagen de su dispositivo, es posible que no necesite generar una clave de pila de red en el árbol fuente de Android.

Los paquetes individuales especifican una de estas claves configurando LOCAL_CERTIFICATE en su archivo Android.mk. (testkey se usa si esta variable no está configurada). También puede especificar una clave completamente diferente por nombre de ruta, por ejemplo:

device/yoyodyne/apps/SpecialApp/Android.mk
 [...]

LOCAL_CERTIFICATE := device/yoyodyne/security/special

Ahora la compilación usa la 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

El script de firma sign_target_files_apks funciona en los archivos de destino generados para una compilación. Toda la información sobre certificados y claves privadas utilizadas en el momento de la compilación se incluye en los archivos de destino. Al ejecutar el script de firma para firmar el lanzamiento, las claves de firma se pueden reemplazar según el nombre de la clave o el nombre del APK.

Utilice las banderas --key_mapping y --default_key_mappings para especificar el reemplazo de claves en función de los nombres de las claves:

  • El --key_mapping src_key = dest_key especifica el reemplazo de una clave a la vez.
  • El indicador de --default_key_mappings dir especifica un directorio con cinco claves para reemplazar todas las claves en build/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

Use el --extra_apks apk_name1,apk_name2,... = key para especificar los reemplazos de clave de firma según los nombres de APK. Si key se deja vacía, la secuencia de comandos trata los APK especificados como prefirmados.

Para el producto tardis hipotético, necesita seis claves protegidas con contraseña: cinco para reemplazar las cinco en build/target/product/security y una para reemplazar la clave adicional device/yoyodyne/security/special requerida por 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

Entonces firmarías todas las aplicaciones así:

./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

Esto trae 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 solicitar al usuario las contraseñas para todas las claves protegidas con contraseña, la secuencia de comandos vuelve a firmar todos los archivos APK en el .zip de destino de entrada con las claves de liberación. Antes de ejecutar el comando, también puede establecer la variable de entorno ANDROID_PW_FILE en un nombre de archivo temporal; la secuencia de comandos luego invoca a su editor para permitirle ingresar contraseñas para todas las claves (esta puede ser una forma más conveniente de ingresar contraseñas).

Reemplazo de clave de firma APEX

Android 10 presenta el formato de archivo APEX para instalar módulos de sistema de nivel inferior. Como se explica en la firma de APEX , cada archivo APEX se firma con dos claves: una para la imagen del mini sistema de archivos dentro de un APEX y la otra para todo el APEX.

Al firmar para la liberación, las dos claves de firma para un archivo APEX se reemplazan con claves de liberación. La clave de carga útil del sistema de archivos se especifica con el indicador --extra_apex_payload y toda la clave de firma del archivo APEX se especifica con el indicador --extra_apks .

Para el producto tardis, suponga que tiene la siguiente configuración clave 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 lanzamiento. 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 son manejados por la configuración predeterminada como 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

Ejecutar el comando anterior da 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

El script de firma sign_target_files_apks reescribe la descripción de la compilación y la huella digital en los archivos de propiedades de la compilación para reflejar que la compilación es una compilación firmada. El indicador --tag_changes controla qué ediciones se realizan en la huella digital. Ejecute el script con -h para ver la documentación de todos los indicadores.

Generación manual de claves

Android usa claves RSA de 2048 bits con exponente público 3. Puede generar pares de certificado/clave privada utilizando la herramienta openssl de openssl.org :

# generate RSA key
openssl 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 key
openssl 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 key
openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt

# securely delete the temp.pem file
shred --remove temp.pem

El comando openssl pkcs8 proporcionado anteriormente crea un archivo .pk8 sin contraseña, adecuado para usar con el sistema de compilación. Para crear un .pk8 protegido con una contraseña (lo que debe hacer para todas las claves de versión reales), reemplace el argumento -nocrypt con -passout stdin ; luego, openssl cifrará 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 el terminal, el programa parecerá colgarse cuando en realidad solo está esperando que ingrese una contraseña. Se pueden usar otros valores para el argumento passout para leer la contraseña desde otras ubicaciones; para obtener más información, consulte la documentación de openssl .

El archivo intermedio temp.pem contiene la clave privada sin ningún tipo de protección de contraseña, así que deséchelo cuidadosamente cuando genere claves de liberación. En particular, la utilidad GNUshred puede no ser efectiva en la red o en los sistemas de archivos registrados. Puede usar un directorio de trabajo ubicado en un disco RAM (como una partición tmpfs) al generar claves para asegurarse de que los intermedios no queden expuestos sin darse cuenta.

Creación de archivos de imagen

Una vez que haya firmado-target-files.zip, debe crear la imagen para poder colocarla en un dispositivo. Para crear la imagen firmada a partir de los archivos de destino, ejecute el siguiente comando desde la raíz del árbol de Android:

img_from_target_files signed-target-files.zip signed-img.zip
El archivo resultante, signed-img.zip , contiene todos los archivos .img. Para cargar una imagen en un dispositivo, use fastboot de la siguiente manera:
fastboot update signed-img.zip