Esquema de firma APK v2

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

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

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 hacer 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 posteriores 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 de ID-valor envueltos de una manera que facilita la ubicació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 de valor de ID 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 encontrando primero el inicio del ZIP Central Directory (buscando el registro ZIP End of Central Directory al final del archivo y luego leyendo el desplazamiento de inicio del Central Directory del registro). El valor magic proporciona una forma rápida de establecer que lo que precede al Directorio central es probablemente el Bloque de firma de APK. El size of block apunta eficientemente 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

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 desvincular la verificación de firmas de la verificación de 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 mediante una firma admitida de la lista proporcionada. Las firmas con algoritmos de firma desconocidos se ignoran. Depende de cada implementación elegir qué firma utilizar cuando se encuentran varias firmas admitidas. Esto permite la introducción de métodos de firma más fuertes 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 de 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 de 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 ASN.1 DER)

ID de algoritmo de firma

  • 0x0101: RSASSA-PSS con resumen SHA2-256, SHA2-256 MGF1, 32 bytes de sal, tráiler: 0xbc
  • 0x0102: RSASSA-PSS con resumen SHA2-512, SHA2-512 MGF1, 64 bytes de sal, tráiler: 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 los algoritmos.

Tamaños de teclas compatibles 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

Con el fin de proteger los contenidos de 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 de 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 de signed data del bloque APK Signature Scheme v2 contenido 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 sus contenidos almacenados en bloques de 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 de 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 sobre 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 sobre la concatenación del byte 0x5a , la cantidad 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 End of Central Directory) se complica por la sección que contiene el desplazamiento de ZIP Central Directory. 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 ZIP End of Central Directory, el campo que contiene el desplazamiento del ZIP Central Directory debe tratarse como si contuviera el desplazamiento del bloque de firma de APK.

Protecciones de reversión

Un atacante podría intentar que un APK firmado con v2 se verifique como un APK firmado con v1 en plataformas Android que admiten 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, se requiere que el verificador de APK rechace los APK que no tienen 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 la verificación de APK firmada por JAR .

Un atacante podría intentar eliminar firmas más fuertes del bloque APK Signature Scheme v2. Para mitigar este ataque, la lista de identificadores 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 de acuerdo con el esquema de firma 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 de APK

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

Verificación del esquema de firma APK v2

  1. Ubique 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 ZIP Central Directory es seguido inmediatamente por el registro ZIP End of Central Directory.
    3. ZIP End of Central Directory no es seguido por más datos.
  2. Localice el primer bloque del esquema de firma de APK v2 dentro del bloque de firma de 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 de firma más compatible de las signatures . El orden de fuerza depende de cada versión de implementación/plataforma.
    2. Verifique la signature correspondiente de las signatures contra signed data usando 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/adición de firmas).
    4. Calcule el resumen de los contenidos del APK usando el mismo algoritmo de resumen que el algoritmo de resumen usado 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 tiene éxito si se encontró al menos un signer y el paso 3 se realizó correctamente para cada signer encontrado.

Nota : el APK no debe verificarse con el esquema v1 si se produce un error en el paso 3 o 4.

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

El APK firmado por JAR es un JAR estándar firmado , 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 PKCS #7 CMS ContentInfo con estructura SignedData cuya firma se verifica sobre el archivo <firmante>.SF.
  3. El archivo <signer>.SF contiene un resumen de archivo completo de META-INF/MANIFEST.MF y resúmenes de cada sección de META-INF/MANIFEST.MF. Se verifica el resumen de todo el archivo de MANIFEST.MF. Si eso falla, se verifica el resumen de cada sección MANIFEST.MF en su lugar.
  4. META-INF/MANIFEST.MF contiene, para cada entrada JAR protegida por integridad, 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 se enumeran en MANIFEST.MF y no forman parte de la firma JAR.

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