Guia para a classe Cipher
1. Visão geral
Simplificando, criptografia é o processo de codificação de uma mensagem para que somente usuários autorizados possam entendê-la ou acessá-la.
A mensagem, conhecida comoplaintext, é criptografada usando um algoritmo de criptografia - acipher - gerandociphertext que só pode ser lido por usuários autorizados por meio de descriptografia.
Neste artigo, descrevemos em detalhesthe core Cipher class, which provides cryptographic encryption and decryption functionality em Java.
2. Classe Cipher
Java Cryptography Extension (JCE) é opart of the Java Cryptography Architecture (JCA) que fornece um aplicativo com cifras criptográficas para criptografia e descriptografia de dados, bem como hash de dados privados.
A classeCipher - localizada no pacotejavax.crypto - forma o núcleo da estrutura JCE, fornecendo a funcionalidade para criptografia e descriptografia.
2.1. Instanciação de cifra
Para instanciar um objetoCipher, usamoscall the static getInstance method, passing the name of the requested transformation. Opcionalmente, o nome de um provedor pode ser especificado.
Vamos escrever um exemplo de classe ilustrando a instanciação de umCipher:
public class Encryptor {
public byte[] encryptMessage(byte[] message, byte[] keyBytes)
throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//...
}
}
A transformaçãoAES/ECB/PKCS5Padding diz ao métodogetInstance para instanciar o objetoCipher como uma cifraAES com ECBmode of operatione PKCS5padding scheme.
Também podemos instanciar o objetoCipher especificando apenas o algoritmo na transformação:
Cipher cipher = Cipher.getInstance("AES");
Nesse caso, são usados valores padrão específicos do provedor para o esquema de modo e preenchimento.
UmNoSuchAlgorithmException é lançado se a transformação fornull, vazia, em um formato inválido ou a implementação do algoritmo de criptografia especificado não estiver disponível no provedor especificado.
NoSuchPaddingException é lançado se a transformação contém um esquema de preenchimento que não está disponível.
2.2. Keys
A interfaceKey representa chaves para operações criptográficas. Chaves são recipientes opacos que contêm uma chave codificada, o formato de codificação da chave e seu algoritmo criptográfico.
Geralmente, as chaves são obtidas por meio dekey generators, certificados oukey specifications usandokey factory.
Vamos criar umKey simétrico a partir dos bytes de chave fornecidos:
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
2.3. Inicialização de cifra
We call the init() method to initialize the Cipher object com umKey ouCertificatee umopmode indicando o modo de operação da cifra.
Opcionalmente,we can pass in a source of randomness. Por padrão, uma implementaçãoSecureRandom do provedor instalado de maior prioridade é usada. Se nenhum for encontrado, uma fonte fornecida pelo sistema será usada.
We can specify a set of algorithm-specific parameters optionally. Por exemplo, podemos passar umIvParameterSpec paraspecify an initialization vector.
Aqui estão os modos de operação de cifra disponíveis:
-
ENCRYPT_MODE: inicializa o objetocipher para o modo de criptografia
-
DECRYPT_MODE: inicializa o objetocipher para o modo de descriptografia
-
WRAP_MODE: inicializa o objetocipher para o modokey-wrapping
-
UNWRAP_MODE: inicializa o objetocipher para o modokey-unwrapping
Vamos inicializar o objetoCipher:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// ...
UmInvalidKeyException é lançado se a chave fornecida for inadequada para inicializar a cifra, como quando um comprimento / codificação de chave é inválido.
Também é lançada quando a cifra requer certos parâmetros de algoritmo que não podem ser determinados a partir da chave, ou se a chave tem um tamanho de chave que excede o tamanho de chave máximo permitido (determinado a partir dos arquivos de políticaconfigured JCE jurisdiction).
Vejamos um exemplo usando umCertificate:
public byte[] encryptMessage(byte[] message, Certificate certificate)
throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, certificate);
// ...
}
O objetoCipher obtém a chave pública para criptografia de dados do certificado chamando o métodogetPublicKey.
2.4. Criptografia e descriptografia
Após inicializar o objetoCipher, chamamos o métododoFinal() para executar a operação de criptografia ou descriptografia. Este método retorna uma matriz de bytes que contém a mensagem criptografada ou descriptografada.
O métododoFinal() também redefine o objetoCipher ao estado em que estava quando foi inicializado anteriormente por meio de uma chamada ao métodoinit(), tornando o objetoCipher disponível para criptografar ou descriptografar mensagens.
Vamos chamardoFinal em nosso métodoencryptMessage:
public byte[] encryptMessage(byte[] message, byte[] keyBytes)
throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException,
BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(message);
}
Para realizar uma operação de descriptografia, alteramosopmode paraDECRYPT_MODE:
public byte[] decryptMessage(byte[] encryptedMessage, byte[] keyBytes)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(encryptedMessage);
}
2.5. Fornecedores
Projetado para usar umprovider-based architecture, oJCE allows for qualified cryptography libraries such as BouncyCastle to be plugged in as security providers and new algorithms to be added seamlessly.
Agora vamos adicionar BouncyCastle como um provedor de segurança. We can add a security provider either statically or dynamically.
To add BouncyCastle statically, we modify the java.security file localizado na pasta<JAVA_HOME>/jre/lib/security.
Nós adicionamos a linha no final da lista:
...
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
Ao adicionar a propriedade do provedor, a chave da propriedade está no formatosecurity.provider.N onde o númeroN é um a mais que o último da lista.
We can also add the BouncyCastle security provider dynamically sem ter que modificar o arquivo de segurança:
Security.addProvider(new BouncyCastleProvider());
Agora podemos especificar o provedor durante a inicialização da cifra:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");
BC especifica BouncyCastle como o provedor. Podemos obter a lista de provedores registrados por meio do métodoSecurity.getProviders().
3. Teste de criptografia e descriptografia
Vamos escrever um exemplo de teste para ilustrar a criptografia e descriptografia de mensagens.
Neste teste, usamos o algoritmo de criptografia AES com uma chave de 128 bits e afirmamos que o resultado descriptografado é igual ao texto da mensagem original:
@Test
public void whenIsEncryptedAndDecrypted_thenDecryptedEqualsOriginal()
throws Exception {
String encryptionKeyString = "thisisa128bitkey";
String originalMessage = "This is a secret message";
byte[] encryptionKeyBytes = encryptionKeyString.getBytes();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey secretKey = new SecretKeySpec(encryptionKeyBytes, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedMessageBytes = cipher.doFinal(message.getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedMessageBytes = cipher.doFinal(encryptedMessageBytes);
assertThat(originalMessage).isEqualTo(new String(decryptedMessageBytes));
}
4. Conclusão
Neste artigo, discutimos a classeCipher e apresentamos exemplos de uso. Mais detalhes sobre a classeCipher e o JCE Framework podem ser encontrados emclass documentationeJava Cryptography Architecture (JCA) Reference Guide.
Implementação de todos esses exemplos e trechos de códigocan be foundover on Github; este é um projeto baseado em Maven, então deve ser fácil importar e executar como está.