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 deKeyInfo.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:
Gere um par de chaves que use a finalidade
PURPOSE_WRAP_KEY
. O ideal é adicionar também um atestado a esse par de chaves.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 }
Crie um objeto
WrappedKeyEntry
, transmitindo a mensagem ASN.1 como uma matriz de bytes.Transmita esse objeto
WrappedKeyEntry
para a sobrecarga desetEntry()
que aceita um objetoKeystore.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 usandosetUserAuthenticationValidityDurationSeconds
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 (consulteKeyguardManager.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 (consulteFingerprintManager.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 |
|
HmacSHA224 | 23 e posteriores |
|
HmacSHA256 | 23 e posteriores |
|
HmacSHA384 | 23 e posteriores |
|
HmacSHA512 | 23 e posteriores |
|
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 oKeyPairGenerator
e o KeyGenerator
.
KeyPairGenerator
Algoritmo | Compatibilidade (níveis de API) | Observações |
---|---|---|
DSA | 19–22 | |
EC | 23 e posteriores |
Em APIs anteriores à de nível 23, as chaves EC podem ser geradas usando o KeyPairGenerator do |
RSA | 18+ |
|
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).