Criptografia

Este documento descreve a maneira correta de usar os recursos criptográficos do Android e inclui alguns exemplos de uso. Se o app exigir maior segurança de chave, use o sistema Android Keystore.

Especificar um provedor apenas com o sistema Android Keystore

Se você estiver usando o sistema Android Keystore, vai precisar especificar um provedor.

Em outras situações, no entanto, o Android não garante um provedor específico para um determinado algoritmo. Especificar um provedor sem usar o sistema Android Keystore pode causar problemas de compatibilidade em versões futuras.

Escolher um algoritmo recomendado

Quando você tem a liberdade de escolher qual algoritmo usar (por exemplo, quando não precisa de compatibilidade com um sistema de terceiros), recomendamos o uso destes algoritmos:

Classe Recomendação
Cipher AES no modo CBC ou GCM com chaves de 256 bits (como AES/GCM/NoPadding)
MessageDigest Família SHA-2 (como SHA-256)
Mac HMAC da família SHA-2 (como HMACSHA256)
Assinatura Família SHA-2 com ECDSA (como SHA256withECDSA)

Executar operações criptográficas comuns

As próximas seções incluem snippets que demonstram como você pode concluir operações criptográficas comuns no seu app.

Criptografar uma mensagem

Kotlin

val plaintext: ByteArray = ...
val keygen = KeyGenerator.getInstance("AES")
keygen.init(256)
val key: SecretKey = keygen.generateKey()
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, key)
val ciphertext: ByteArray = cipher.doFinal(plaintext)
val iv: ByteArray = cipher.iv

Java

byte[] plaintext = ...;
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(256);
SecretKey key = keygen.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] iv = cipher.getIV();

Gerar um resumo de mensagens

Kotlin

val message: ByteArray = ...
val md = MessageDigest.getInstance("SHA-256")
val digest: ByteArray = md.digest(message)

Java

byte[] message = ...;
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(message);

Gerar uma assinatura digital

Você precisa ter um objeto PrivateKey contendo a chave de assinatura, que pode ser gerada durante a execução, lida em um arquivo incluído no seu app ou adquirida em alguma outra origem, dependendo das suas necessidades.

Kotlin

val message: ByteArray = ...
val key: PrivateKey = ...
val s = Signature.getInstance("SHA256withECDSA")
        .apply {
            initSign(key)
            update(message)
        }
val signature: ByteArray = s.sign()

Java

byte[] message = ...;
PrivateKey key = ...;
Signature s = Signature.getInstance("SHA256withECDSA");
s.initSign(key);
s.update(message);
byte[] signature = s.sign();

Verificar uma assinatura digital

Você precisa ter um objeto PublicKey contendo a chave pública do assinante, que pode ser lida em um arquivo empacotado com seu app, extraída de um certificado ou adquirida em alguma outra origem, dependendo das suas necessidades.

Kotlin

val message: ByteArray = ...
val signature: ByteArray = ...
val key: PublicKey = ...
val s = Signature.getInstance("SHA256withECDSA")
        .apply {
            initVerify(key)
            update(message)
        }
val valid: Boolean = s.verify(signature)

Java

byte[] message = ...;
byte[] signature = ...;
PublicKey key = ...;
Signature s = Signature.getInstance("SHA256withECDSA");
s.initVerify(key);
s.update(message);
boolean valid = s.verify(signature);

Complexidades de implementação

Alguns detalhes da implementação de criptografia do Android parecem incomuns, mas estão presentes devido a preocupações de compatibilidade. Esta seção discute as que você provavelmente encontrará.

Resumo da mensagem OAEP MGF1

As criptografias RSA OAEP são parametrizadas por dois resumos de mensagens diferentes: o resumo “principal” e o resumo MGF1. Há identificadores Cipher que incluem nomes de resumo, como Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding"), que especificam o resumo principal e deixam o resumo MGF1 não especificado. Para o Android Keystore, o SHA-1 é usado para o resumo MGF1. Para outros provedores criptográficos do Android, os dois resumos são iguais.

Para ter mais controle sobre os resumos que seu app usa, solicite uma criptografia com OAEPPadding, como em Cipher.getInstance("RSA/ECB/OAEPPadding"), e forneça um OAEPParameterSpec a init() para escolher explicitamente os dois resumos. Isso é mostrado no código abaixo:

Kotlin

val key: Key = ...
val cipher = Cipher.getInstance("RSA/ECB/OAEPPadding")
        .apply {
            // To use SHA-256 the main digest and SHA-1 as the MGF1 digest
            init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT))
            // To use SHA-256 for both digests
            init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT))
        }

Java

Key key = ...;
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
// To use SHA-256 the main digest and SHA-1 as the MGF1 digest
cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT));
// To use SHA-256 for both digests
cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));

Funcionalidades descontinuadas

As próximas seções descrevem as funcionalidades descontinuadas. Não use-as no seu app.

Algoritmos do Bouncy Castle

As implementações do Bouncy Castle (link em inglês) de vários algoritmos foram descontinuadas. Isso só afeta casos em que você solicita explicitamente o provedor do Bouncy Castle, conforme mostrado no exemplo a seguir:

Kotlin

Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC")
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))

Java

Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"));

Conforme observado na seção sobre como especificar um provedor apenas com o sistema Android Keystore, não é recomendável solicitar um provedor específico. Se você segue essa diretriz, essa descontinuação não afeta você.

Criptografias baseadas em senha sem um vetor de inicialização

As criptografias baseadas em senha (PBE, na sigla em inglês) que exigem um vetor de inicialização (IV, na sigla em inglês) podem adquiri-lo na chave, se ela for construída de forma correta ou em um IV transmitido explicitamente. Se você transmitir uma chave de PBE sem um IV e não transmitir um IV explícito, atualmente, as criptografias PBE no Android vão presumir um IV de zero.

Ao usar criptografias PBE, sempre transmita um IV explícito, conforme mostrado neste snippet de código:

Kotlin

val key: SecretKey = ...
val cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC")
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))

Java

SecretKey key = ...;
Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

Provedor de criptografia

No Android 9 (nível 28 da API) e versões mais recentes, o provedor de criptografia Java Cryptography Architecture (JCA) foi removido. Se o app solicitar uma instância do provedor de criptografia, por exemplo, chamar o método a seguir, ocorrerá um NoSuchProviderException.

Kotlin

SecureRandom.getInstance("SHA1PRNG", "Crypto")

Java

SecureRandom.getInstance("SHA1PRNG", "Crypto");

Biblioteca de criptografia de segurança do Jetpack

A biblioteca de criptografia de segurança do Jetpack foi descontinuada. Isso só afeta casos em que há as seguintes dependências no arquivo build.gradle do módulo do app:

Groovy

dependencies {
    implementation "androidx.security:security-crypto:1.0.0"
}

Kotlin

dependencies {
    implementation("androidx.security:security-crypto:1.0.0")
}

Algoritmos com suporte

Estes são os identificadores de algoritmo JCA com suporte no Android: