Criptografia AES

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.

  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.

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)

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.

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

“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.

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.

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.

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!