Como eu disse, basta mexer um único bit da chave, para que o resultado seja totalmente diferente do resultado original.
Em média, 50% dos bits são alterados, como havia previsto.
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Perturbacao {
public void testar() {
// estes são os algoritmos disponíveis para o Java 5.0
String[] nomesAlgoritmos = {
"AES/ECB/NoPadding",
"DES/ECB/NoPadding",
"DESede/ECB/NoPadding",
"Blowfish/ECB/NoPadding",
"RC2/ECB/NoPadding",
"RC4/ECB/NoPadding"
};
for (int i = 0; i < nomesAlgoritmos.length; ++i) {
testar (nomesAlgoritmos[i]);
}
}
public void alteraUmBit (byte[] bytes) {
// Vamos pegar o bit mais significativo, e alterá-lo
// (se for 0, alterá-lo para 1, e vice-versa.
bytes[0] ^= 0x80;
}
private Random r = new Random ();
public byte[] gerarDados (int n) {
byte[] dados = new byte [n];
r.nextBytes(dados);
return dados;
}
// Esta função retorna um byte 00 se os dados originais baterem com os dados decifrados
// nessa posição, e um byte diferente de 00 se os dados originais forem diferentes.
// Por exemplo, se o original for 'P' (0x50) e o decifrado for 'S' (0x53),
// então a comparação irá retornar 0x03, que é o xor de 0x50 e 0x53.
public byte[] comparar (byte[] dadosOriginais, byte[] dadosDecifrados) {
byte[] resultado = new byte[dadosOriginais.length];
for (int i = 0; i < dadosOriginais.length; ++i) {
resultado [i] = (byte) (dadosOriginais[i] ^ dadosDecifrados[i]);
}
return resultado;
}
// Se um bit houver sido alterado, ele vai ser 1, e se não foi alterado,
// ele vai ser zero.
public double calcularProporcaoBitsAlterados (byte[] comparados) {
int bitsUm = 0;
for (int i = 0; i < comparados.length; ++i) {
byte valor = comparados[i];
if ((valor & 0x01) == 0x01) bitsUm++;
if ((valor & 0x02) == 0x02) bitsUm++;
if ((valor & 0x04) == 0x04) bitsUm++;
if ((valor & 0x08) == 0x08) bitsUm++;
if ((valor & 0x10) == 0x10) bitsUm++;
if ((valor & 0x20) == 0x20) bitsUm++;
if ((valor & 0x40) == 0x40) bitsUm++;
if ((valor & 0x80) == 0x80) bitsUm++;
}
return (double) bitsUm/ (comparados.length * 8);
}
public void printHex (String mensagem, byte[] bytes) {
System.out.println (mensagem);
for (int i = 0; i < bytes.length; ++i) {
System.out.printf ("%02X ", 0xFF & bytes[i]);
}
System.out.println ();
}
public void testar (String nomeAlgoritmo) {
try {
System.out.println ("-----------------------");
System.out.println ("Nome do algoritmo: " + nomeAlgoritmo);
Cipher cipher = Cipher.getInstance (nomeAlgoritmo);
String nomeAbreviado = nomeAlgoritmo.split("/")[0];
KeyGenerator kg = KeyGenerator.getInstance(nomeAbreviado);
SecretKey skOriginal = kg.generateKey();
byte[] chave = skOriginal.getEncoded();
printHex ("Chave original: ", chave);
alteraUmBit (chave);
printHex ("Chave alterada: ", chave);
// Para simplificar, crie um dado cujo tamanho é múltiplo de 16.
byte[] dadosOriginais = gerarDados (128);
printHex ("Dados originais: ", dadosOriginais);
SecretKey skAlterada = new SecretKeySpec (chave, nomeAbreviado);
cipher.init (Cipher.ENCRYPT_MODE, skOriginal);
byte[] dadosCifrados = cipher.doFinal(dadosOriginais);
cipher.init (Cipher.DECRYPT_MODE, skOriginal);
byte[] dadosDecifrados = cipher.doFinal (dadosCifrados);
printHex ("Dados decifrados com a chave original: ", dadosDecifrados);
byte[] dadosComparados = comparar (dadosOriginais, dadosDecifrados);
printHex ("Comparação: ", dadosComparados);
System.out.printf ("A quantidade de bits que foi alterada é de %.2f %%.%n",
100.0 * calcularProporcaoBitsAlterados(dadosComparados));
cipher.init (Cipher.DECRYPT_MODE, skAlterada);
byte[] dadosDecifradosComChaveAlterada = cipher.doFinal (dadosCifrados);
printHex ("Dados decifrados com a chave alterada: ", dadosDecifradosComChaveAlterada);
dadosComparados = comparar (dadosOriginais, dadosDecifradosComChaveAlterada);
printHex ("Comparação: ", dadosComparados);
System.out.printf ("A quantidade de bits que foi alterada é de %.2f %%.%n",
100.0 * calcularProporcaoBitsAlterados(dadosComparados));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Perturbacao p = new Perturbacao();
p.testar();
}
}