Esquema de firma APK v2

APK Signature Scheme v2 es un esquema de firma de archivos completos que aumenta la velocidad de verificación y fortalece las garantías de integridad al detectar cualquier cambio en las partes protegidas del APK.

La firma con APK Signature Scheme v2 inserta un bloque de firma de APK en el archivo APK inmediatamente antes de la sección Directorio central ZIP. Dentro del bloque de firma de APK, las firmas v2 y la información de identidad del firmante se almacenan en un bloque de esquema de firma de APK v2 .

APK antes y después de firmar

Figura 1. APK antes y después de firmar

APK Signature Scheme v2 se introdujo en Android 7.0 (Nougat). Para que un APK se pueda instalar en Android 6.0 (Marshmallow) y dispositivos más antiguos, el APK debe firmarse mediante la firma JAR antes de firmarse con el esquema v2.

Bloque de firma de APK

Para mantener la compatibilidad con versiones anteriores del formato APK v1, las firmas APK v2 y más recientes se almacenan dentro de un bloque de firma APK, un nuevo contenedor introducido para admitir el esquema de firma APK v2. En un archivo APK, el bloque de firma de APK se encuentra inmediatamente antes del directorio central ZIP, que se encuentra al final del archivo.

El bloque contiene pares ID-valor empaquetados de una manera que facilita la localización del bloque en el APK. La firma v2 del APK se almacena como un par ID-valor con ID 0x7109871a.

Formato

El formato del bloque de firma de APK es el siguiente (todos los campos numéricos son little-endian):

  • size of block en bytes (excluyendo este campo) (uint64)
  • Secuencia de pares ID-valor con prefijo de longitud uint64:
    • ID (uint32)
    • value (longitud variable: longitud del par - 4 bytes)
  • size of block en bytes: igual que el primer campo (uint64)
  • magic “APK Sig Block 42” (16 bytes)

El APK se analiza buscando primero el inicio del directorio central ZIP (buscando el registro ZIP de fin del directorio central al final del archivo y luego leyendo el desplazamiento inicial del directorio central desde el registro). El valor magic proporciona una forma rápida de establecer que lo que precede al Directorio central probablemente sea el bloque de firma de APK. El size of block apunta de manera eficiente al inicio del bloque en el archivo.

Los pares ID-valor con ID desconocidos deben ignorarse al interpretar el bloque.

Bloque de esquema de firma APK v2

El APK está firmado por uno o más firmantes/identidades, cada uno representado por una clave de firma. Esta información se almacena como un bloque APK Signature Scheme v2. Para cada firmante, se almacena la siguiente información:

  • (algoritmo de firma, resumen, firma) tuplas. El resumen se almacena para desacoplar la verificación de la firma de la verificación de la integridad del contenido del APK.
  • Cadena de certificados X.509 que representa la identidad del firmante.
  • Atributos adicionales como pares clave-valor.

Para cada firmante, el APK se verifica utilizando una firma compatible de la lista proporcionada. Se ignoran las firmas con algoritmos de firma desconocidos. Depende de cada implementación elegir qué firma usar cuando se encuentran varias firmas admitidas. Esto permitirá la introducción de métodos de firma más sólidos en el futuro de forma compatible con versiones anteriores. El enfoque sugerido es verificar la firma más fuerte.

Formato

El bloque APK Signature Scheme v2 se almacena dentro del bloque de firma APK con el ID 0x7109871a .

El formato del bloque APK Signature Scheme v2 es el siguiente (todos los valores numéricos son little-endian, todos los campos con prefijo de longitud usan uint32 para la longitud):

  • secuencia con prefijo de longitud signer con prefijo de longitud:
    • signed data con prefijo de longitud:
      • secuencia con prefijo de longitud de digests con prefijo de longitud:
      • secuencia con prefijo de longitud de certificates X.509:
        • certificate X.509 con prefijo de longitud (formulario ASN.1 DER)
      • secuencia con prefijo de longitud de additional attributes con prefijo de longitud:
        • ID (uint32)
        • value (longitud variable: longitud del atributo adicional - 4 bytes)
    • secuencia con prefijo de longitud signatures con prefijo de longitud:
      • signature algorithm ID (uint32)
      • signature con prefijo de longitud sobre signed data
    • public key con prefijo de longitud (SubjectPublicKeyInfo, formulario DER ASN.1)

ID de algoritmos de firma

  • 0x0101: RSASSA-PSS con resumen SHA2-256, SHA2-256 MGF1, 32 bytes de sal, avance: 0xbc
  • 0x0102: RSASSA-PSS con resumen SHA2-512, SHA2-512 MGF1, 64 bytes de sal, avance: 0xbc
  • 0x0103: RSASSA-PKCS1-v1_5 con resumen SHA2-256. Esto es para sistemas de compilación que requieren firmas deterministas.
  • 0x0104: RSASSA-PKCS1-v1_5 con resumen SHA2-512. Esto es para sistemas de compilación que requieren firmas deterministas.
  • 0x0201: ECDSA con resumen SHA2-256
  • 0x0202: ECDSA con resumen SHA2-512
  • 0x0301: DSA con resumen SHA2-256

Todos los algoritmos de firma anteriores son compatibles con la plataforma Android. Las herramientas de firma pueden admitir un subconjunto de algoritmos.

Tamaños de claves admitidos y curvas EC:

  • RSA: 1024, 2048, 4096, 8192, 16384
  • CE: NIST P-256, P-384, P-521
  • DSA: 1024, 2048, 3072

Contenidos protegidos por integridad

A los efectos de proteger el contenido de un APK, un APK consta de cuatro secciones:

  1. Contenido de las entradas ZIP (desde el desplazamiento 0 hasta el inicio del bloque de firma de APK)
  2. Bloque de firma de APK
  3. Directorio central ZIP
  4. ZIP Fin del Directorio Central

Secciones APK después de firmar

Figura 2. Secciones del APK después de firmar

APK Signature Scheme v2 protege la integridad de las secciones 1, 3, 4 y los bloques signed data del bloque APK Signature Scheme v2 contenidos dentro de la sección 2.

La integridad de las secciones 1, 3 y 4 está protegida por uno o más resúmenes de su contenido almacenados en bloques signed data que, a su vez, están protegidos por una o más firmas.

El resumen de las secciones 1, 3 y 4 se calcula de la siguiente manera, similar a un árbol Merkle de dos niveles. Cada sección se divide en fragmentos consecutivos de 1 MB (2 20 bytes). El último fragmento de cada sección puede ser más corto. El resumen de cada fragmento se calcula mediante la concatenación del byte 0xa5 , la longitud del fragmento en bytes (little-endian uint32) y el contenido del fragmento. El resumen de nivel superior se calcula mediante la concatenación del byte 0x5a , el número de fragmentos (little-endian uint32) y la concatenación de resúmenes de los fragmentos en el orden en que aparecen en el APK. El resumen se calcula en forma fragmentada para permitir acelerar el cálculo al paralelizarlo.

Resumen de APK

Figura 3. Resumen de APK

La protección de la sección 4 (ZIP final del directorio central) se complica por la sección que contiene el desplazamiento del directorio central ZIP. El desplazamiento cambia cuando cambia el tamaño del bloque de firma de APK, por ejemplo, cuando se agrega una nueva firma. Por lo tanto, al calcular el resumen sobre el extremo ZIP del directorio central, el campo que contiene el desplazamiento del directorio central ZIP debe tratarse como si contuviera el desplazamiento del bloque de firma APK.

Protecciones de reversión

Un atacante podría intentar verificar un APK firmado con v2 como un APK firmado con v1 en plataformas Android que admitan la verificación de APK firmado con v2. Para mitigar este ataque, los APK firmados con v2 que también están firmados con v1 deben contener un atributo X-Android-APK-Signed en la sección principal de sus archivos META-INF/*.SF. El valor del atributo es un conjunto de ID de esquema de firma de APK separados por comas (el ID de este esquema es 2). Al verificar la firma v1, el verificador de APK debe rechazar los APK que no tengan una firma para el esquema de firma de APK que el verificador prefiere de este conjunto (por ejemplo, esquema v2). Esta protección se basa en el hecho de que el contenido de los archivos META-INF/*.SF está protegido por firmas v1. Consulte la sección sobre verificación de APK firmado por JAR .

Un atacante podría intentar eliminar firmas más seguras del bloque APK Signature Scheme v2. Para mitigar este ataque, la lista de ID de algoritmos de firma con los que se estaba firmando el APK se almacena en el bloque signed data que está protegido por cada firma.

Verificación

En Android 7.0 y versiones posteriores, los APK se pueden verificar según el esquema de firma de APK v2+ o la firma JAR (esquema v1). Las plataformas más antiguas ignoran las firmas v2 y solo verifican las firmas v1.

Proceso de verificación de firma APK

Figura 4. Proceso de verificación de firma de APK (nuevos pasos en rojo)

Verificación del esquema de firma APK v2

  1. Localice el bloque de firma de APK y verifique que:
    1. Dos campos de tamaño del bloque de firma de APK contienen el mismo valor.
    2. El directorio central ZIP va seguido inmediatamente del registro de fin del directorio central ZIP.
    3. ZIP El final del directorio central no va seguido de más datos.
  2. Localice el primer bloque APK Signature Scheme v2 dentro del bloque de firma APK. Si el bloque v2 está presente, continúe con el paso 3. De lo contrario, vuelva a verificar el APK usando el esquema v1 .
  3. Para cada signer en el bloque APK Signature Scheme v2:
    1. Elija el signature algorithm ID compatible más potente entre signatures . El orden de potencia depende de cada versión de implementación/plataforma.
    2. Verifique la signature correspondiente de signatures con signed data utilizando public key . (Ahora es seguro analizar signed data ).
    3. Verifique que la lista ordenada de ID de algoritmos de firma en digests y signatures sea idéntica. (Esto es para evitar la eliminación o adición de firmas).
    4. Calcule el resumen del contenido del APK utilizando el mismo algoritmo de resumen que el algoritmo de resumen utilizado por el algoritmo de firma.
    5. Verifique que el resumen calculado sea idéntico al digest correspondiente de digests .
    6. Verifique que SubjectPublicKeyInfo del primer certificate de certificates sea idéntico a public key .
  4. La verificación se realiza correctamente si se encontró al menos un signer y el paso 3 se realizó correctamente para cada signer encontrado.

Nota : El APK no se debe verificar usando el esquema v1 si ocurre una falla en el paso 3 o 4.

Verificación de APK firmada por JAR (esquema v1)

El APK firmado por JAR es un JAR firmado estándar , que debe contener exactamente las entradas enumeradas en META-INF/MANIFEST.MF y donde todas las entradas deben estar firmadas por el mismo conjunto de firmantes. Su integridad se verifica de la siguiente manera:

  1. Cada firmante está representado por una entrada JAR META-INF/<firmante>.SF y META-INF/<firmante>.(RSA|DSA|EC).
  2. <firmante>.(RSA|DSA|EC) es un ContentInfo CMS PKCS #7 con estructura SignedData cuya firma se verifica sobre el archivo <firmante>.SF.
  3. El archivo <firmante>.SF contiene un resumen del archivo completo de META-INF/MANIFEST.MF y resúmenes de cada sección de META-INF/MANIFEST.MF. Se verifica el resumen completo del archivo MANIFEST.MF. Si eso falla, se verifica el resumen de cada sección MANIFEST.MF.
  4. META-INF/MANIFEST.MF contiene, para cada entrada JAR con integridad protegida, una sección con el nombre correspondiente que contiene el resumen del contenido sin comprimir de la entrada. Todos estos resúmenes están verificados.
  5. La verificación de APK falla si el APK contiene entradas JAR que no figuran en MANIFEST.MF y no forman parte de la firma JAR.

La cadena de protección es, por tanto, <firmante>.(RSA|DSA|EC) -> <firmante>.SF -> MANIFEST.MF -> contenido de cada entrada JAR protegida por integridad.