APK Signature Scheme v2 é um esquema de assinatura de arquivo inteiro que aumenta a velocidade de verificação e fortalece as garantias de integridade ao detectar quaisquer alterações nas partes protegidas do APK.
A assinatura usando o esquema de assinatura APK v2 insere um bloco de assinatura APK no arquivo APK imediatamente antes da seção ZIP Central Directory. Dentro do APK Signature Block, as assinaturas v2 e as informações de identidade do signatário são armazenadas em um APK Signature Scheme v2 Block .
APK Signature Scheme v2 foi introduzido no Android 7.0 (Nougat). Para tornar um APK instalável no Android 6.0 (Marshmallow) e em dispositivos mais antigos, o APK deve ser assinado usando a assinatura JAR antes de ser assinado com o esquema v2.
Bloco de assinatura de APK
Para manter a compatibilidade com versões anteriores do formato APK v1, assinaturas APK v2 e mais recentes são armazenadas dentro de um bloco de assinatura APK, um novo contêiner introduzido para dar suporte ao esquema de assinatura APK v2. Em um arquivo APK, o APK Signing Block está localizado imediatamente antes do ZIP Central Directory, localizado no final do arquivo.
O bloco contém pares de valor de ID agrupados de forma a facilitar a localização do bloco no APK. A assinatura v2 do APK é armazenada como um par de valor de ID com ID 0x7109871a.
Formato
O formato do APK Signing Block é o seguinte (todos os campos numéricos são little-endian):
-
size of block
em bytes (excluindo este campo) (uint64) - Sequência de pares de ID-valor com prefixo de comprimento uint64:
-
ID
(uint32) -
value
(variável-comprimento: comprimento do par - 4 bytes)
-
-
size of block
em bytes — igual ao primeiro campo (uint64) -
magic
“APK Sig Block 42” (16 bytes)
O APK é analisado encontrando primeiro o início do diretório central ZIP (encontrando o registro ZIP End of Central Directory no final do arquivo e lendo o deslocamento inicial do diretório central do registro). O valor magic
fornece uma maneira rápida de estabelecer que o que precede o diretório central é provavelmente o bloco de assinatura do APK. O size of block
então aponta eficientemente para o início do bloco no arquivo.
Pares de valor de ID com IDs desconhecidos devem ser ignorados ao interpretar o bloco.
Bloco do Esquema de Assinatura APK v2
O APK é assinado por um ou mais signatários/identidades, cada um representado por uma chave de assinatura. Essas informações são armazenadas como um APK Signature Scheme v2 Block. Para cada signatário, as seguintes informações são armazenadas:
- (algoritmo de assinatura, resumo, assinatura) tuplas. O resumo é armazenado para dissociar a verificação de assinatura da verificação de integridade do conteúdo do APK.
- Cadeia de certificados X.509 que representa a identidade do assinante.
- Atributos adicionais como pares de valores-chave.
Para cada signatário, o APK é verificado usando uma assinatura compatível da lista fornecida. Assinaturas com algoritmos de assinatura desconhecidos são ignoradas. Cabe a cada implementação escolher qual assinatura usar quando várias assinaturas com suporte forem encontradas. Isso permite a introdução de métodos de assinatura mais fortes no futuro de maneira compatível com versões anteriores. A abordagem sugerida é verificar a assinatura mais forte.
Formato
O APK Signature Scheme v2 Block é armazenado dentro do APK Signing Block sob o ID 0x7109871a
.
O formato do bloco APK Signature Scheme v2 é o seguinte (todos os valores numéricos são little-endian, todos os campos com prefixo de comprimento usam uint32 para comprimento):
- sequência com prefixo de comprimento do
signer
com prefixo de comprimento :-
signed data
com prefixo de comprimento:- sequência com prefixo de comprimento de
digests
com prefixo de comprimento:-
signature algorithm ID
(uint32) -
digest
(com prefixo de comprimento) — consulte Conteúdo protegido por integridade
-
- sequência com prefixo de comprimento de
certificates
X.509:-
certificate
X.509 com prefixo de comprimento (formulário ASN.1 DER)
-
- sequência prefixada de comprimento de
additional attributes
prefixados por comprimento:-
ID
(uint32) -
value
(variable-length: comprimento do atributo adicional - 4 bytes)
-
- sequência com prefixo de comprimento de
- sequência com prefixo de comprimento de
signatures
com prefixo de comprimento:-
signature algorithm ID
(uint32) -
signature
com prefixo de comprimento sobresigned data
-
-
public key
com prefixo de comprimento (SubjectPublicKeyInfo, formulário ASN.1 DER)
-
IDs de algoritmo de assinatura
- 0x0101—RSASSA-PSS com SHA2-256 digest, SHA2-256 MGF1, 32 bytes de sal, trailer: 0xbc
- 0x0102—RSASSA-PSS com SHA2-512 digest, SHA2-512 MGF1, 64 bytes de sal, trailer: 0xbc
- 0x0103—RSASSA-PKCS1-v1_5 com resumo SHA2-256. Isso é para sistemas de compilação que exigem assinaturas determinísticas.
- 0x0104—RSASSA-PKCS1-v1_5 com resumo SHA2-512. Isso é para sistemas de compilação que exigem assinaturas determinísticas.
- 0x0201—ECDSA com resumo SHA2-256
- 0x0202—ECDSA com resumo SHA2-512
- 0x0301—DSA com resumo SHA2-256
Todos os algoritmos de assinatura acima são suportados pela plataforma Android. As ferramentas de assinatura podem suportar um subconjunto dos algoritmos.
Tamanhos de chave e curvas EC suportados:
- RSA: 1024, 2048, 4096, 8192, 16384
- EC: NIST P-256, P-384, P-521
- DSA: 1024, 2048, 3072
Conteúdo protegido por integridade
Para fins de proteção do conteúdo do APK, um APK consiste em quatro seções:
- Conteúdo das entradas ZIP (do deslocamento 0 até o início do APK Signing Block)
- Bloco de assinatura de APK
- Diretório Central ZIP
- ZIP Fim do Diretório Central
APK Signature Scheme v2 protege a integridade das seções 1, 3, 4 e os blocos de signed data
do APK Signature Scheme v2 Block contido na seção 2.
A integridade das seções 1, 3 e 4 é protegida por um ou mais resumos de seus conteúdos armazenados em blocos de signed data
que, por sua vez, são protegidos por uma ou mais assinaturas.
O resumo das seções 1, 3 e 4 é calculado da seguinte forma, semelhante a uma árvore Merkle de dois níveis. Cada seção é dividida em blocos consecutivos de 1 MB (2 20 bytes). O último pedaço em cada seção pode ser mais curto. O resumo de cada fragmento é calculado sobre a concatenação do byte 0xa5
, o comprimento do fragmento em bytes (little-endian uint32) e o conteúdo do fragmento. O resumo de nível superior é calculado sobre a concatenação do byte 0x5a
, o número de fragmentos (little-endian uint32) e a concatenação de resumos dos fragmentos na ordem em que os fragmentos aparecem no APK. O resumo é calculado em partes para permitir acelerar a computação paralelizando-a.
A proteção da seção 4 (ZIP End of Central Directory) é complicada pela seção que contém o deslocamento do ZIP Central Directory. O deslocamento muda quando o tamanho do bloco de assinatura do APK muda, por exemplo, quando uma nova assinatura é adicionada. Assim, ao calcular o resumo sobre o ZIP End of Central Directory, o campo que contém o deslocamento do ZIP Central Directory deve ser tratado como contendo o deslocamento do APK Signing Block.
Proteções de reversão
Um invasor pode tentar verificar um APK assinado por v2 como um APK assinado por v1 em plataformas Android compatíveis com a verificação de APK assinado por v2. Para mitigar esse ataque, os APKs assinados por v2 que também são assinados por v1 devem conter um atributo X-Android-APK-Signed na seção principal de seus arquivos META-INF/*.SF. O valor do atributo é um conjunto de IDs de esquema de assinatura de APK separados por vírgula (o ID desse esquema é 2). Ao verificar a assinatura v1, o verificador de APK é obrigado a rejeitar APKs que não têm uma assinatura para o esquema de assinatura de APK que o verificador prefere deste conjunto (por exemplo, esquema v2). Essa proteção se baseia no fato de que os arquivos META-INF/*.SF de conteúdo são protegidos por assinaturas v1. Consulte a seção sobre verificação de APK assinado por JAR .
Um invasor pode tentar remover assinaturas mais fortes do bloco APK Signature Scheme v2. Para mitigar esse ataque, a lista de IDs de algoritmos de assinatura com os quais o APK estava sendo assinado é armazenada no bloco signed data
que é protegido por cada assinatura.
Verificação
No Android 7.0 e posterior, os APKs podem ser verificados de acordo com o esquema de assinatura APK v2+ ou assinatura JAR (esquema v1). Plataformas mais antigas ignoram assinaturas v2 e verificam apenas assinaturas v1.
Verificação do esquema de assinatura do APK v2
- Localize o bloco de assinatura do APK e verifique se:
- Dois campos de tamanho do APK Signing Block contêm o mesmo valor.
- O ZIP Central Directory é imediatamente seguido pelo registro ZIP End of Central Directory.
- ZIP End of Central Directory não é seguido por mais dados.
- Localize o primeiro APK Signature Scheme v2 Block dentro do APK Signing Block. Se o bloco v2 estiver presente, vá para a etapa 3. Caso contrário, volte para verificar o APK usando o esquema v1 .
- Para cada
signer
no bloco APK Signature Scheme v2:- Escolha o
signature algorithm ID
suportado mais forte designatures
. A ordem de força depende de cada versão de implementação/plataforma. - Verifique a
signature
correspondente dassignatures
em relação aossigned data
usandopublic key
. (Agora é seguro analisarsigned data
.) - Verifique se a lista ordenada de IDs de algoritmo de assinatura em
digests
esignatures
é idêntica. (Isso é para evitar remoção/adição de assinatura.) - Calcule o resumo do conteúdo do APK usando o mesmo algoritmo de resumo usado pelo algoritmo de assinatura.
- Verifique se o resumo calculado é idêntico ao
digest
correspondente dosdigests
. - Verifique se SubjectPublicKeyInfo do primeiro
certificate
decertificates
é idêntico àpublic key
.
- Escolha o
- A verificação é bem-sucedida se pelo menos um
signer
for encontrado e a etapa 3 for bem-sucedida para cadasigner
encontrado.
Observação : o APK não deve ser verificado usando o esquema v1 se ocorrer uma falha na etapa 3 ou 4.
Verificação de APK assinado por JAR (esquema v1)
O APK assinado por JAR é um JAR assinado padrão , que deve conter exatamente as entradas listadas em META-INF/MANIFEST.MF e onde todas as entradas devem ser assinadas pelo mesmo conjunto de assinantes. Sua integridade é verificada da seguinte forma:
- Cada signatário é representado por uma entrada JAR META-INF/<signer>.SF e META-INF/<signer>.(RSA|DSA|EC).
- <signer>.(RSA|DSA|EC) é um PKCS #7 CMS ContentInfo com estrutura SignedData cuja assinatura é verificada no arquivo <signer>.SF.
- O arquivo <signer>.SF contém um resumo de todo o arquivo do META-INF/MANIFEST.MF e resumos de cada seção do META-INF/MANIFEST.MF. O resumo de todo o arquivo do MANIFEST.MF é verificado. Se isso falhar, o resumo de cada seção MANIFEST.MF será verificado.
- O META-INF/MANIFEST.MF contém, para cada entrada JAR protegida por integridade, uma seção com o nome correspondente contendo o resumo do conteúdo descompactado da entrada. Todos esses resumos são verificados.
- A verificação do APK falha se o APK contiver entradas JAR que não estão listadas no MANIFEST.MF e não fazem parte da assinatura JAR.
A cadeia de proteção é, portanto, <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> conteúdo de cada entrada JAR protegida por integridade.