Criptografia AES

8 respostas
X

Boa tarde! Não sou expert em criptografia e estou com alguns problemas. Tem um programa Python que criptografa dados em um arquivo, usando AES, modo CBC e padrão PKCS7.
Preciso criar um aplicativo em Java que leia um arquivo criptografado pelo programa Python.
Sendo assim, já tenho minha senha/chave de segurança, preciso apenas ler os dados.

Tentei vários códigos, mas todos eles sem sucesso, por isso resolvi pedir ajuda pra ver se algum de vocês me indica o caminho. O aplicativo Python codifica a string criptografada usando Base64, portanto, tenho que decodificar antes de realizar a criptografia.

Agradeço a ajuda!
Abraços,
xis

EDIT: minha chave possui 16 bytes.

8 Respostas

E
  1. Para criar a chave a partir dos seus 16 bytes, crie um objeto javax.crypto.spec.SecretKeySpec, com os bytes, e especificando como algoritmo “AES”
  2. Você precisa, para criptografar a coisa, usar um objeto do tipo javax.crypto.Cipher:
    Cipher c = Cipher.getInstance(“AES/CBC/PKCS5Padding”);

Não se esqueça de passar a chave que você criou com SecretkeySpec para o método init:

http://docs.oracle.com/javase/6/docs/api/javax/crypto/Cipher.html#init(int,%20java.security.Key)

  1. Acho que você não está querendo o encapsulamento PKCS#7 (se precisar dele, é necessário usar o BouncyCastle, http://www.bouncycastle.org ) e sim apenas o padding PKCS#5, que foi o que estipulei acima.
X

Obrigado pela resposta. Peguei um pouco do que já havia estudado e tentei seguir seus passos, mas sem sucesso. Baixei o BouncyCastle pra usar o PKCS7 já, mas li que o PKCS5 é basicamente o mesmo padrão, que não haveria problemas em usá-lo.

Segue o que foi desenvolvido:

public String Decrypt(String strText)
	{
		try
		{
                        //bytKey já é minha senha convertida em bytes
			SecretKey key = new SecretKeySpec(bytKey, "AES");
			IvParameterSpec iv = new IvParameterSpec(bytKey);
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
			
			cipher.init(Cipher.DECRYPT_MODE, key, iv);
			
			byte[] decoded = new BASE64Decoder().decodeBuffer(strText);
			
			byte[] decrypted = cipher.doFinal(decoded);
			
			return new String(decrypted);
			
		}
		catch (Exception exc)
		{
			exc.printStackTrace();
			objLogger.writeToLog(exc.getMessage(), this.getClass().getCanonicalName(), logLevel.E);
		}
		
		return "";
		
	}

Quando chega no doFinal, retorna a exceção:

javax.crypto.BadPaddingException: Given final block not properly padded
	at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
	at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
	at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
	at javax.crypto.Cipher.doFinal(Cipher.java:2086)
	at meuPackage.Cryptography.Decrypt(Cryptography.java:55)
	at meuPackage.Cryptography.main(Cryptography.java:85)
E

O dado, depois de convertido para bytes, tem comprimento múltiplo de 16?
É que o bloco de uma mensagem AES tem comprimento de 16 bytes, e o PKCS#5 sempre faz padding de modo que se tenha um número inteiro de blocos na mensagem, portanto seu dado tem de ter comprimento múltiplo de 16.

X

Sim… quando faço a a decodificação do strText (que é meu texto criptografado), o bytearray tem o tamanho 5296.

E

“not properly padded” pode ocorrer, por exemplo, quando a chave está incorreta.

É que ocorre o seguinte. O padding PKCS#7 funciona da seguinte forma: digamos que os dados a serem codificados tenham 5290 bytes.

Só que você só pode codificar blocos, e cada bloco tem 16 bytes no AES.

Então a idéia é completar o último bloco (que tem 10 bytes) com bytes repetidos, que contém um valor fixo e igual ao número de bytes que faltam para completar o bloco. Vou dar um exemplo - digamos que o último pedaço da mensagem fosse

“rafia AES.”

(isso poderia acontecer se você tivesse, por exemplo, a string “Criptografia AES.”)

Ou seja, os bytes são:

0000    72 61 66 69 61 20 41 45  53 2E                     rafia AES.

A idéia é completar com 6 bytes 06, e o último bloco ficaria:

0000    72 61 66 69 61 20 41 45  53 2E 06 06 06 06 06 06   rafia AES.......

Uma forma muito tosca de verificar se a chave está correta é verificar se os últimos N bytes do último bloco, depois da decodificação, são valores repetidos (por exemplo, 1 byte 01, ou 2 bytes 02, ou 3 bytes 03, ou 6 bytes 06, ou 15 bytes 0F - você deve ter entendido a idéia.

Se a chave estiver incorreta, vai ocorrer o problema de que os últimos N bytes não são valores repetidos e vai dar esse erro de “padding” esquisito.

X

Bom… minha chave na verdade tem 11 caracteres. Então eu já completo ela com bytes zerados. Ou seja, eu crio um byte array com 16 posições zeradas e preencho as 11 primeiras com os bytes da minha chave. Por exemplo:
souXisvaldo vai me gerar a varíavel bytKey: (byte[]) [115, 111, 117, 88, 105, 115, 118, 97, 108, 100, 111, 0, 0, 0, 0, 0] tendo os 5 últimos bytes zerados.

Será que esse pode ser o problema?
O programa Python já usa essa chave, ficaria complicado de trocá-la.

E

Pelo que imagino, o programa Python gera uma chave a partir da senha usando algum algoritmo, mas como eu não conheço esses métodos de criptografia do Python, eu não sei como é que ele deriva uma chave a partir da senha.

X

O objeto do tipo AESCipher no Python é criado passando para o construtor a senha, em string mesmo e o modo como ele irá operar, ou seja, CBC.
Não sei se tem algo similar assim no Java, mas vou procurar outro tipo de solução… como o aplicativo Python irá chamar esse programa Java, como se fosse um serviço, durante a chamada acredito que irei pegar esse valores em Python, já descriptografados, e passar como parâmetro para o Java.

Não é a solução mais bonita e correta, mas por hora é a mais viável.

Obrigado pela ajuda!

Criado 21 de junho de 2012
Ultima resposta 22 de jun. de 2012
Respostas 8
Participantes 2