2009/06/08

Encriptação de dados com AES

Nas últimas semanas tive que ir à net à procura de formas de encriptar dados (byte[], ficheiros) com AES. Como não foi imediato, deixo aqui o código de uma prova de conceito que compus, para o caso de ser útil para mais alguém. A base deste código é uma classe que encripta dados que estão em memória.

A classe, que tem o inspirado nome de AES, pode ser usada da forma seguinte:


// Texto para encriptar
String message = "Isto é um texto para ser encriptado.\n" +
"Tem duas linhas, e esta é a segunda.\n" +
"E afinal tem mais uma... :-)\n";

System.out.println("Original string: " + message);

// Primeira instância da classe, para encriptar
AES genio = new AES();

// Encriptação
byte[] coded = genio.dataEncrypt(message.getBytes());
System.out.println("encrypted string: " +
bytes2hex(coded) +
" - []size=" + coded.length);

// Testa a desencriptação
byte[] decoded = genio.dataDecrypt(coded);
System.out.println("Decrypted string: " +
new String(decoded));

// Pede-lhe a chave...
byte[] key = genio.getKeyEncoded();

// Nova instância da classe, que recebe a chave
AES genio2 = new AES(key);

// Tenta desencriptar de novo, a ver se sai bem
byte[] decoded2 = genio2.dataDecrypt(coded);
System.out.println("Decrypted2 string: " +
new String(decoded2));


Como vêem, é facílimo.

Agora a classe propriamente dita:


import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.*;
import javax.crypto.spec.*;

public class AES {

private KeyGenerator kgen;
private SecretKey skey;
private SecretKeySpec skeySpec;
private Cipher cipher;
private byte[] raw;
byte[] iv = new byte[]{
0x07, 0x00, 0x01, 0x03, 0x00, 0x06, 0x04, 0x03, 0x09, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);

public AES() throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException {

// Get the KeyGenerator
kgen = KeyGenerator.getInstance("AES");
kgen.init(128);

// Generate the secret key specs.
skey = kgen.generateKey();
raw = skey.getEncoded();
skeySpec = new SecretKeySpec(raw, "AES");

// Instantiate the cipher
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

}

public AES(String key) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {

raw = hex2bytes(key);
skeySpec = new SecretKeySpec(raw, "AES");

// Instantiate the cipher
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

}

public AES(byte[] key) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {

skeySpec = new SecretKeySpec(key, "AES");

// Instantiate the cipher
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

}

public String stringEncrypt(String message) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, paramSpec);
byte[] encrypted = cipher.doFinal(message.getBytes());
return bytes2hex(encrypted);
}

public String stringDecrypt(String code) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
cipher.init(Cipher.DECRYPT_MODE, skeySpec, paramSpec);
byte[] original = cipher.doFinal(hex2bytes(code));
return new String(original);
}

public byte[] dataEncrypt(byte[] data) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, paramSpec);
byte[] encrypted = cipher.doFinal(data);
return encrypted;
}

public byte[] dataDecrypt(byte[] code) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
cipher.init(Cipher.DECRYPT_MODE, skeySpec, paramSpec);
byte[] original = cipher.doFinal(code);
return original;
}

public static byte[] hex2bytes(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) +
Character.digit(s.charAt(i + 1), 16));
}
return data;
}

public static String bytes2hex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;

for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}

strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}

return strbuf.toString();
}

public String getKey() {
return bytes2hex(skeySpec.getEncoded());
}

public byte[] getKeyEncoded() {
return skeySpec.getEncoded();
}
}


Num próximo post publico o código necessário para encriptar e desencriptar um ficheiro, guardando a chave noutro.