Sistema Android Keystore

O sistema Android Keystore permite que você armazene chaves criptográficas em um contêiner para dificultar a extração do dispositivo. Quando as chaves estão no Keystore, elas podem ser usadas para operações criptográficas, e o material delas permanece não exportável. Além disso, esse recurso oferece instalações para restringir como e quando as chaves podem ser usadas, por exemplo, solicitando a autenticação do usuário para usar as chaves ou restringindo o uso das chaves apenas em certos modos criptográficos. Consulte a seção Recursos de segurança para saber mais.

O sistema Keystore é usado pela API KeyChain, introduzida no Android 4.0 (API de nível 14); o recurso provedor de Keystore do Android, introduzido no Android 4.3 (API de nível 18) e a Biblioteca de segurança, disponível como parte do Jetpack. Este documento explica quando e como usar o provedor Android Keystore.

Recursos de segurança

O sistema Android Keystore protege as chaves armazenadas contra o uso não autorizado. Em primeiro lugar, esse sistema mitiga o uso não autorizado das chaves fora do dispositivo Android evitando a extração delas dos processos do aplicativo e do dispositivo Android como um todo. Em segundo lugar, o Android Keystore mitiga o uso não autorizado das chaves no dispositivo Android fazendo com que os apps especifiquem usos autorizados das chaves e aplicando essas restrições fora dos processos dos apps.

Prevenção contra extração

As chaves armazenadas no Android Keystore são protegidas contra a extração por duas medidas de segurança:

  • As chaves nunca entram no processo de aplicativos. Quando um aplicativo faz operações criptográficas usando uma chave do Android Keystore, o texto simples, o texto criptografado e as mensagens a serem assinadas ou verificadas são alimentados, em segundo plano, a um processo de sistema que realiza as operações criptográficas. Se o processo do app for comprometido, o invasor poderá usar as chaves do app, mas não poderá extraí-las, por exemplo, para serem usadas fora do dispositivo Android.
  • O material da chave pode estar vinculado ao hardware seguro (por exemplo, Ambiente de execução confiável (TEE, na sigla em inglês) ou um Elemento de segurança (SE, na sigla em inglês) do dispositivo Android. Quando esse recurso é ativado para uma chave, ela nunca é exposta fora do hardware protegido. Se o sistema operacional Android for comprometido ou se um invasor conseguir ler o armazenamento interno do dispositivo, ele poderá usar as chaves do Android Keystore de qualquer app no dispositivo, mas não extraí-las do dispositivo. Esse recurso será ativado apenas se o hardware protegido do dispositivo for compatível com a combinação específica de algoritmo de chave, modos de bloqueio, esquemas de preenchimento e resumos com que a chave tem permissão para ser usada. Para verificar se o recurso está ativado para uma chave, obtenha um KeyInfo para a chave e verifique o valor de retorno de KeyInfo.isInsideSecurityHardware().

Módulo de segurança de hardware

Os dispositivos compatíveis que executam o Android 9 (API de nível 28) ou versão posterior podem ter um StrongBox Keymaster, uma implementação da Keymaster HAL que reside em um módulo de segurança de hardware. O módulo contém:

  • a própria CPU;
  • armazenamento seguro;
  • um gerador de números aleatórios real;
  • outros mecanismos para resistir a violações do pacote e sideload não autorizado de apps.

Ao verificar as chaves armazenadas no StrongBox Keymaster, o sistema confirma a integridade de uma chave com o Ambiente de execução confiável (TEE, na sigla em inglês).

Para disponibilizar implementações de baixo consumo de bateria do StrongBox, um subconjunto de algoritmos e tamanhos de chave são compatíveis:

  • RSA 2048
  • AES 128 e 256
  • ECDSA P-256
  • HMAC-SHA256 (compatível com tamanhos de chave entre 8 a 64 bytes)
  • Triple DES 168

Ao gerar ou importar chaves usando o KeyStore, você indica uma preferência para armazenar a chave no Keymaster StrongBox transmitindo true para o método setIsStrongBoxBacked().

Autorizações de uso de chaves

Para mitigar o uso não autorizado de chaves em um dispositivo Android, o Android Keystore permite que os apps especifiquem usos autorizados das próprias chaves ao gerá-las ou importá-las. Quando uma chave é gerada ou importada, as autorizações dela não podem ser alteradas. Dessa forma, as autorizações são aplicadas pelo Android Keystore sempre que a chave é usada. Esse é um recurso de segurança avançado que geralmente é útil apenas se você precisa que um comprometimento do processo do seu aplicativo após uma geração/importação de chave (mas não antes ou durante) não cause usos não autorizados da chave.

As autorizações de uso de chaves compatíveis se enquadram nas seguintes categorias:

  • criptografia: algoritmos, operações ou propósitos de chaves autorizados (criptografar, descriptografar, assinar, verificar), esquemas de preenchimento, modos de bloqueio e resumos com os quais a chave pode ser usada.
  • intervalo de validade temporal: intervalo de tempo durante o qual a chave tem o uso autorizado;
  • autenticação do usuário: a chave só pode ser usada se o usuário tiver sido autenticado recentemente. Consulte Exigir a autenticação do usuário para o uso de chaves.

Como medida de segurança extra, para chaves cujo material se encontra em um hardware protegido (consulte KeyInfo.isInsideSecurityHardware()), algumas autorizações de uso de chaves podem ser aplicadas pelo hardware protegido, dependendo do dispositivo Android. As autorizações criptográfica e de autenticação do usuário provavelmente serão aplicadas pelo hardware protegido. É improvável que o hardware protegido aplique autorizações de intervalo de validade temporal, porque ele normalmente não tem um relógio em tempo real protegido e independente.

É possível consultar se a autorização de autenticação do usuário de uma chave é aplicada pelo hardware protegido usando KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware().

Escolher entre um conjunto de chaves ou o provedor Android Keystore

Use a API KeyChain quando quiser credenciais de sistema. Quando um app solicita o uso de alguma credencial com a API KeyChain, os usuários podem escolher, por meio de uma IU fornecida pelo sistema, quais das credenciais instaladas o app pode acessar. Isso permite que vários apps usem o mesmo conjunto de credenciais com o consentimento do usuário.

Use o provedor Android Keystore para permitir que um app individual armazene as credenciais que apenas o próprio app pode acessar. Isso oferece uma maneira para que os apps gerenciem credenciais que apenas eles podem usar, com os mesmos benefícios de segurança que a API KeyChain fornece para credenciais de sistema. Esse método não exige interação do usuário para selecionar as credenciais.

Usar o provedor Android Keystore

Para usar esse recurso, use as classes padrão KeyStore e KeyPairGenerator ou KeyGenerator com o provedor de AndroidKeyStore introduzido no Android 4.3 (API de nível18).

O AndroidKeyStore é registrado como um tipo KeyStore para uso com o método KeyStore.getInstance(type) e como um provedor para uso com os métodos KeyPairGenerator.getInstance(algorithm, provider) e KeyGenerator.getInstance(algorithm, provider).

Gerar uma nova chave privada

Para gerar uma nova PrivateKey, você também precisa especificar os atributos X.509 iniciais que o certificado autoassinado terá.

A Biblioteca de segurança fornece uma implementação padrão para gerar uma chave simétrica válida, conforme mostrado no snippet a seguir:

Kotlin

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
    

Java

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC;
    String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
    

Como alternativa, você pode usar KeyStore.setKeyEntry para substituir o certificado posteriormente com um certificado assinado por uma autoridade de certificação.

Para gerar a chave, use um KeyPairGenerator com KeyPairGeneratorSpec.

Kotlin

    /*
     * Generate a new EC key pair entry in the Android Keystore by
     * using the KeyPairGenerator API. The private key can only be
     * used for signing or verification and only with SHA-256 or
     * SHA-512 as the message digest.
     */
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC,
            "AndroidKeyStore"
    )
    val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
            alias,
            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
    ).run {
        setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
        build()
    }

    kpg.initialize(parameterSpec)

    val kp = kpg.generateKeyPair()
    

Java

    /*
     * Generate a new EC key pair entry in the Android Keystore by
     * using the KeyPairGenerator API. The private key can only be
     * used for signing or verification and only with SHA-256 or
     * SHA-512 as the message digest.
     */
    KeyPairGenerator kpg = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
    kpg.initialize(new KeyGenParameterSpec.Builder(
            alias,
            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
            .setDigests(KeyProperties.DIGEST_SHA256,
                KeyProperties.DIGEST_SHA512)
            .build());

    KeyPair kp = kpg.generateKeyPair();
    

Gerar uma nova chave secreta

Para gerar a chave, siga o mesmo processo usado para gerar uma nova chave privada. Use a Biblioteca de segurança em cada caso.

Importar chaves criptografadas com mais segurança

O Android 9 (API de nível 28) e versões posteriores permitem que você importe chaves criptografadas com segurança para o Keystore usando um formato de chave codificado por ASN.1. Em seguida, o Keymaster descriptografa as chaves no Keystore para que o conteúdo delas nunca apareça como texto simples na memória do host do dispositivo. Esse processo oferece maior segurança de descriptografia de chave.

Para oferecer compatibilidade com a importação segura de chaves criptografadas para o Keystore, faça o seguinte:

  1. Gere um par de chaves que use a finalidade PURPOSE_WRAP_KEY. O ideal é adicionar também um atestado a esse par de chaves.

  2. Em um servidor ou máquina confiável, gere a mensagem ASN.1 que o SecureKeyWrapper precisa conter.

    O wrapper tem o seguinte esquema:

    KeyDescription ::= SEQUENCE {
            keyFormat INTEGER,
            authorizationList AuthorizationList
        }
    
        SecureKeyWrapper ::= SEQUENCE {
            wrapperFormatVersion INTEGER,
            encryptedTransportKey OCTET_STRING,
            initializationVector OCTET_STRING,
            keyDescription KeyDescription,
            secureKey OCTET_STRING,
            tag OCTET_STRING
        }
        
  3. Crie um objeto WrappedKeyEntry, transmitindo a mensagem ASN.1 como uma matriz de bytes.

  4. Transmita esse objeto WrappedKeyEntry para a sobrecarga de setEntry() que aceita um objeto Keystore.Entry.

Trabalhar com entradas de armazenamento de chaves

O uso do provedor de AndroidKeyStore é realizado por pelo de todas as APIs padrão do KeyStore.

Listar entradas

Liste entradas no Keystore chamando o método aliases():

Kotlin

    /*
     * Load the Android KeyStore instance using the
     * "AndroidKeyStore" provider to list out what entries are
     * currently stored.
     */
    val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val aliases: Enumeration<String> = ks.aliases()
    

Java

    /*
     * Load the Android KeyStore instance using the
     * "AndroidKeyStore" provider to list out what entries are
     * currently stored.
     */
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    Enumeration<String> aliases = ks.aliases();
    

Assinar e verificar os dados

Assine dados recuperando KeyStore.Entry do armazenamento de chaves e usando as APIs de Signature, como sign():

Kotlin

    /*
     * Use a PrivateKey in the KeyStore to create a signature over
     * some data.
     */
    val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val entry: KeyStore.Entry = ks.getEntry(alias, null)
    if (entry !is KeyStore.PrivateKeyEntry) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry")
        return null
    }
    val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run {
        initSign(entry.privateKey)
        update(data)
        sign()
    }
    

Java

    /*
     * Use a PrivateKey in the KeyStore to create a signature over
     * some data.
     */
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    KeyStore.Entry entry = ks.getEntry(alias, null);
    if (!(entry instanceof PrivateKeyEntry)) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry");
        return null;
    }
    Signature s = Signature.getInstance("SHA256withECDSA");
    s.initSign(((PrivateKeyEntry) entry).getPrivateKey());
    s.update(data);
    byte[] signature = s.sign();
    

Da mesma forma, verifique dados com o método verify(byte[]):

Kotlin

    /*
     * Verify a signature previously made by a PrivateKey in our
     * KeyStore. This uses the X.509 certificate attached to our
     * private key in the KeyStore to validate a previously
     * generated signature.
     */
    val ks = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry
    if (entry == null) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry")
        return false
    }
    val valid: Boolean = Signature.getInstance("SHA256withECDSA").run {
        initVerify(entry.certificate)
        update(data)
        verify(signature)
    }
    

Java

    /*
     * Verify a signature previously made by a PrivateKey in our
     * KeyStore. This uses the X.509 certificate attached to our
     * private key in the KeyStore to validate a previously
     * generated signature.
     */
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    KeyStore.Entry entry = ks.getEntry(alias, null);
    if (!(entry instanceof PrivateKeyEntry)) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry");
        return false;
    }
    Signature s = Signature.getInstance("SHA256withECDSA");
    s.initVerify(((PrivateKeyEntry) entry).getCertificate());
    s.update(data);
    boolean valid = s.verify(signature);
    

Exigir autenticação do usuário para o uso de chaves

Ao gerar ou importar uma chave para o AndroidKeyStore, você pode especificar que a chave só poderá ser usada se o usuário tiver sido autenticado. O usuário é autenticado usando um subconjunto das credenciais dele para bloqueio de tela (padrão/PIN/senha, impressão digital).

Esse é um recurso de segurança avançado que geralmente é útil apenas se você precisar que um comprometimento do processo do seu aplicativo após uma geração/importação de chave (mas não antes ou durante) não possa ignorar a exigência de autenticação do usuário para usar a chave.

Quando uma chave está autorizada a ser usada apenas se o usuário tiver sido autenticado, ela é configurada para operar em um de dois modos:

  • A autenticação do usuário autoriza o uso das chaves por um período de tempo. Todas as chaves nesse modo podem ser usadas assim que o usuário desbloqueia a tela ou confirma as credenciais de bloqueio de tela usando o fluxo KeyguardManager.createConfirmDeviceCredentialIntent. A duração da autorização é especificada para cada chave usando setUserAuthenticationValidityDurationSeconds durante a geração ou importação da chave. Essas chaves só poderão ser geradas ou importadas se a tela de bloqueio segura estiver ativada (consulte KeyguardManager.isDeviceSecure()). Essas chaves se tornam permanentemente invalidadas quando a tela de bloqueio segura é desativada (reconfigurada para "Nenhum", "Deslizar" ou outro modo que não autentica o usuário) ou é redefinida à força (por exemplo, por um administrador do dispositivo).
  • A autenticação do usuário autoriza uma operação criptográfica específica associada a uma chave. Nesse modo, cada operação que envolve a chave em questão precisa ser autorizada individualmente pelo usuário. No momento, a única forma de autorização desse tipo é a autenticação por impressão digital: FingerprintManager.authenticate. Essas chaves só podem ser geradas ou importadas se pelo menos uma impressão digital estiver inscrita (consulte FingerprintManager.hasEnrolledFingerprints). Essas chaves se tornam permanentemente invalidadas quando uma nova impressão digital é registrada ou todas as impressões digitais são canceladas.

Algoritmos compatíveis

Cipher

Algoritmo Compatibilidade (níveis de API) Observações
AES/CBC/NoPadding 23 e posteriores
AES/CBC/PKCS7Padding 23 e posteriores
AES/CTR/NoPadding 23 e posteriores
AES/ECB/NoPadding 23 e posteriores
AES/ECB/PKCS7Padding 23 e posteriores
AES/GCM/NoPadding 23 e posteriores Somente IVs de 12 bytes são compatíveis.
RSA/ECB/NoPadding 18+
RSA/ECB/PKCS1Padding 18+
RSA/ECB/OAEPWithSHA-1AndMGF1Padding 23 e posteriores
RSA/ECB/OAEPWithSHA-224AndMGF1Padding 23 e posteriores
RSA/ECB/OAEPWithSHA-256AndMGF1Padding 23 e posteriores
RSA/ECB/OAEPWithSHA-384AndMGF1Padding 23 e posteriores
RSA/ECB/OAEPWithSHA-512AndMGF1Padding 23 e posteriores
RSA/ECB/OAEPPadding 23 e posteriores

KeyGenerator

Algoritmo Compatibilidade (níveis de API) Observações
AES 23 e posteriores Tamanhos compatíveis: 128, 192, 256
HmacSHA1 23 e posteriores
  • Tamanhos compatíveis: 8 a 1.024, precisam ser múltiplos de 8
  • Tamanho padrão: 160
HmacSHA224 23 e posteriores
  • Tamanhos compatíveis: 8 a 1.024, precisam ser múltiplos de 8
  • Tamanho padrão: 224
HmacSHA256 23 e posteriores
  • Tamanhos compatíveis: 8 a 1.024, precisam ser múltiplos de 8
  • Tamanho padrão: 256
HmacSHA384 23 e posteriores
  • Tamanhos compatíveis: 8 a 1.024, precisam ser múltiplos de 8
  • Tamanho padrão: 384
HmacSHA512 23 e posteriores
  • Tamanhos compatíveis: 8 a 1.024, precisam ser múltiplos de 8
  • Tamanho padrão: 512

KeyFactory

Algoritmo Compatibilidade (níveis de API) Observações
EC 23 e posteriores Especificações de chaves com suporte: KeyInfo (somente chave privada), ECPublicKeySpec (somente chave pública), X509EncodedKeySpec (somente chave pública)
RSA 23 e posteriores Especificações de chaves com suporte: KeyInfo (somente chave privada), RSAPublicKeySpec (somente chave pública), X509EncodedKeySpec (somente chave pública)

KeyStore

O KeyStore oferece suporte aos mesmos tipos de chaves do que o KeyPairGenerator e o KeyGenerator.

KeyPairGenerator

Algoritmo Compatibilidade (níveis de API) Observações
DSA 19–22
EC 23 e posteriores
  • Tamanhos compatíveis: 224, 256, 384, 521
  • Curvas nomeadas compatíveis: P-224 (secp224r1), P-256 (também conhecida como secp256r1 e prime256v1), P-384 (também conhecida como secp384r1), P-521 (também conhecida como secp521r1)

Em APIs anteriores à de nível 23, as chaves EC podem ser geradas usando o KeyPairGenerator do KeyPairGeneratorSpec inicializado pelo algoritmo "RSA", cujo tipo de chave é definido como "EC" usando setKeyType(String). O nome de curva EC não pode ser especificado usando esse método. Uma curva NIST P é automaticamente escolhida com base no tamanho de chave solicitado.

RSA 18+
  • Tamanhos compatíveis: 512, 768, 1.024, 2.048, 3.072, 4.096
  • Expoentes públicos compatíveis: 3, 65.537
  • Expoente público padrão: 65.537

Mac

Algoritmo Compatibilidade (níveis de API) Observações
HmacSHA1 23 e posteriores
HmacSHA224 23 e posteriores
HmacSHA256 23 e posteriores
HmacSHA384 23 e posteriores
HmacSHA512 23 e posteriores

Assinatura

Algoritmo Compatibilidade (níveis de API) Observações
MD5withRSA 18+
NONEwithECDSA 23 e posteriores
NONEwithRSA 18+
SHA1withDSA 19–22
SHA1withECDSA 19 e posteriores
SHA1withRSA 18+
SHA1withRSA/PSS 23 e posteriores
SHA224withDSA 20–22
SHA224withECDSA 20 e posteriores
SHA224withRSA 20 e posteriores
SHA224withRSA/PSS 23 e posteriores
SHA256withDSA 19–22
SHA256withECDSA 19 e posteriores
SHA256withRSA 18+
SHA256withRSA/PSS 23 e posteriores
SHA384withDSA 19–22
SHA384withECDSA 19 e posteriores
SHA384withRSA 18+
SHA384withRSA/PSS 23 e posteriores
SHA512withDSA 19–22
SHA512withECDSA 19 e posteriores
SHA512withRSA 18+
SHA512withRSA/PSS 23 e posteriores

SecretKeyFactory

Algoritmo Compatibilidade (níveis de API) Observações
AES 23 e posteriores Especificações de chaves com suporte: KeyInfo
HmacSHA1 23 e posteriores Especificações de chaves com suporte: KeyInfo
HmacSHA224 23 e posteriores Especificações de chaves com suporte: KeyInfo
HmacSHA256 23 e posteriores Especificações de chaves com suporte: KeyInfo
HmacSHA384 23 e posteriores Especificações de chaves com suporte: KeyInfo
HmacSHA512 23 e posteriores Especificações de chaves com suporte: KeyInfo

Artigos de blog

Consulte a entrada do blog Unificar o acesso ao armazenamento de chaves no ICS (link em inglês).