O sistema Android Keystore permite que você armazene chaves criptográficas em um contêiner para dificultar a extração delas do dispositivo. Quando as chaves estiverem no keystore, elas poderão ser usadas para operações criptográficas, mas não será possível exportá-las. Além disso, o sistema de keystore permite restringir quando e como as chaves podem ser usadas. Ele pode, por exemplo, exigir autenticação do usuário ou restringir o uso apenas em determinados 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 (nível 14 da API), e pelo recurso de provedor do Android Keystore,
introduzido no Android 4.3 (nível 18 da API). Este documento explica
quando e como usar o provedor Android Keystore.
Recursos de segurança
O sistema Android Keystore protege as chaves contra o uso não autorizado de duas maneiras. Primeiro, reduz o risco de uso não autorizado da chave de fora do dispositivo Android, impedindo a extração da chave dos processos do aplicativo e do dispositivo Android como um todo. Em segundo lugar, o sistema de keystore reduz o risco do uso não autorizado das chaves dentro do dispositivo Android, fazendo com que os apps especifiquem os usos autorizados e apliquem 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 app 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 enviados, 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 extraí-las (por exemplo, para uso fora do dispositivo Android).
- A chave pode ser vinculada ao hardware seguro do dispositivo Android, como o
ambiente de execução confiável (TEE) ou
o Elemento de segurança (SE, na sigla em inglês). Quando esse recurso é ativado para uma chave, ela
nunca é exposta fora do hardware protegido. Se o SO Android for comprometido ou se um
invasor conseguir ler o armazenamento interno do dispositivo, ele poderá usar as chaves do keystore do
Android de qualquer app no dispositivo, mas não extraí-las dele. Esse recurso será ativado
apenas se o hardware protegido do dispositivo oferecer suporte à 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, acesse o elemento
KeyInfo
dela. A próxima etapa depende da versão do SDK de destino do app:- Se o app for direcionado ao Android 10 (nível 29 da API) ou versões mais recentes, inspecione o valor de
retorno de
getSecurityLevel()
. Os valores de retorno correspondentes aKeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT
ouKeyProperties.SecurityLevelEnum.STRONGBOX
indicam que a chave está em um hardware seguro. - Caso o app seja destinado ao Android 9 (nível 28 da API) ou versões anteriores, inspecione o valor de retorno booleano
de
KeyInfo.isInsideSecurityHardware()
.
- Se o app for direcionado ao Android 10 (nível 29 da API) ou versões mais recentes, inspecione o valor de
retorno de
Módulo de segurança de hardware
Os dispositivos com suporte que usam o Android 9 (nível 28 da API) ou versões mais recentes podem ter um SafeBox Keymaster, uma implementação do Keymaster ou da HAL Keymint que reside em um elemento de segurança semelhante a um módulo de segurança de hardware. Embora os módulos de segurança de hardware possam se referir a muitas implementações diferentes de armazenamento de chaves em que um comprometimento do kernel do Linux não pode revelá-los, como o TEE, o StrongBox se refere explicitamente a dispositivos como Elementos de segurança incorporados (eSE, na sigla em inglês) ou unidades de processamento seguro (iSE, na sigla em inglês) on-SoC.
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.
- Um timer seguro.
- Um pin de notificação de reinicialização (ou equivalente), como entrada/saída de uso geral (GPIO, na sigla em inglês).
Para disponibilizar implementações de baixo consumo de bateria do StrongBox, um subconjunto de algoritmos e tamanhos de chave têm suporte:
- RSA 2048
- AES 128 e 256
- ECDSA, ECDH P-256
- HMAC-SHA256 (com suporte a tamanhos de chave entre 8 a 64 bytes)
- Padrão de criptografia de dados (DES) triplo
- APDUs de comprimento estendido
- Atestado de chaves
- Suporte da Alteração H para upgrade
Ao gerar ou importar chaves usando o KeyStore
,
você indica uma preferência para armazenar a chave no StrongBox Keymaster
transmitindo true
para o método setIsStrongBoxBacked()
.
O StrongBox é um pouco mais lento e tem limitações de recursos, ou seja, ele oferece suporte a menos operações simultâneas em comparação com o TEE. Porém, as garantias de segurança são melhores contra ataques físicos e de canal lateral. Se você quiser priorizar garantias de segurança melhores em relação à eficiência de recursos do app, recomendamos usar o StrongBox nos dispositivos em que ele estiver disponível. Sempre que o StrongBox não estiver disponível, seu app poderá usar o TEE para armazenar materiais de chaves.
Autorizações de uso de chaves
Para evitar o uso não autorizado de chaves no dispositivo, o Android Keystore permite que os apps especifiquem usos autorizados quando gerarem ou importarem as chaves. Depois que uma chave é gerada ou importada, as autorizações não podem ser modificadas. 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 leve a usos não autorizados da chave.
As autorizações de uso de chaves com suporte se encaixam nestas categorias:
- Criptografia: a chave só pode ser usada com algoritmos, operações ou finalidades de chave autorizados (criptografar, descriptografar, assinar, verificar), esquemas de preenchimento, modos de bloqueio ou resumos.
- Intervalo de validade temporal: a chave tem autorização para uso somente durante um intervalo de tempo definido.
- Autenticação do usuário: a chave só pode ser usada se o usuário tiver sido autenticado recentemente. Consulte Exigir autenticação do usuário para o uso de chaves.
Como medida de segurança extra para chaves que estão dentro de um hardware protegido (consulte
KeyInfo.isInsideSecurityHardware()
ou, para apps direcionados ao Android 10 (nível 29 da API) ou versões mais recentes,
KeyInfo.getSecurityLevel()
),
algumas autorizações de uso de chaves podem ser aplicadas pelo hardware seguro,
dependendo do dispositivo Android.
O hardware seguro normalmente aplica autorizações criptográficas e de autenticação do
usuário. No entanto, o hardware seguro não costuma aplicar autorizações
de intervalo de validade temporal porque normalmente não tem um relógio em
tempo real seguro e independente.
É possível consultar se a autorização de autenticação do usuário de uma chave é aplicada pelo
hardware seguro 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 uma interface fornecida pelo sistema, quais das credenciais instaladas
um 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 próprias
credenciais, que só ele pode acessar.
Isso permite que os apps gerenciem credenciais que só 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 que o usuário selecione 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
lançado no Android 4.3 (nível 18 da API).
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)
.
Como as operações criptográficas podem ser demoradas, os apps precisam evitar o uso de
AndroidKeyStore
na linha de execução principal para garantir que a interface do app continue
responsiva. O StrictMode
pode ajudar você a encontrar lugares em que isso não acontece.
Gerar uma nova chave privada ou secreta
Para gerar um novo KeyPair
contendo uma
PrivateKey
,
especifique os atributos X.509 iniciais do certificado. Você pode usar
KeyStore.setKeyEntry()
para substituir o certificado posteriormente por um certificado assinado
por uma autoridade certificadora (AC).
Para gerar o par de chaves, use um KeyPairGenerator
com KeyGenParameterSpec
.
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();
Importar chaves criptografadas para hardware seguro
O Android 9 (nível 28 da API) e versões mais recentes 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 suporte à 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
. Recomendamos que você também adicione um atestado a esse par de chaves.Em um servidor ou máquina confiável, gere a mensagem ASN.1 para o
SecureKeyWrapper
.O wrapper tem este 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
É possível acessar o provedor AndroidKeyStore
usando
todas as APIs KeyStore
padrão.
Listar entradas
Liste entradas no Keystore chamando o método aliases()
:
Kotlin
/* * Load the Android KeyStore instance using the * AndroidKeyStore provider to list the currently stored entries. */ 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 the currently stored entries. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
Assinar e verificar os dados
Assine dados buscando 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 private key in the * KeyStore. This uses the X.509 certificate attached to the * 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 private key in the * KeyStore. This uses the X.509 certificate attached to the * 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 seguras da tela de bloqueio (padrão/PIN/senha, credenciais biométricas).
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 só está autorizada a ser usada se o usuário tiver sido autenticado, você pode chamar
setUserAuthenticationParameters()
para configurá-la para operar em um destes modos:
- Autorizar por um período
- Todas as chaves poderão ser usadas assim que o usuário for autenticado usando uma das credenciais especificadas.
- Autorizar pela duração de uma operação criptográfica específica
Cada operação que envolve uma chave específica precisa ser autorizada individualmente pelo usuário.
O app inicia esse processo chamando
authenticate()
em uma instância deBiometricPrompt
.
Para cada chave criada, é possível optar por oferecer suporte a uma
credencial
biométrica forte, uma
credencial de
tela de bloqueio ou ambos os tipos de credenciais. Para determinar se o usuário configurou as credenciais
necessárias para a chave do app, chame
canAuthenticate()
.
Se uma chave aceitar apenas credenciais biométricas, ela será invalidada por padrão sempre que novos
registros biométricos forem adicionados. É possível configurar a chave para que ela continue válida quando novos registros
biométricos forem adicionados. Para fazer isso, transmita false
para
setInvalidatedByBiometricEnrollment()
.
Saiba mais sobre como adicionar recursos de autenticação biométrica ao app, incluindo como mostrar uma caixa de diálogo de autenticação biométrica.
Algoritmos com suporte
Cipher
KeyGenerator
KeyFactory
KeyStore
(oferece suporte aos mesmos tipos de chaves queKeyGenerator
eKeyPairGenerator
)KeyPairGenerator
Mac
Signature
SecretKeyFactory
Artigos de blog
Consulte a postagem do blog Unificar o acesso ao armazenamento de chaves no ICS (link em inglês).