En esta respuesta, elijo abordar el tema principal del "Ejemplo simple de cifrado / descifrado AES de Java" y no la pregunta de depuración específica porque creo que esto beneficiará a la mayoría de los lectores.
Este es un resumen simple de mi publicación de blog sobre el cifrado AES en Java por lo que recomiendo leerlo antes de implementar cualquier cosa. Sin embargo, seguiré brindando un ejemplo simple para usar y dar algunos consejos sobre qué tener en cuenta.
En este ejemplo, elegiré utilizar cifrado autenticado con el modo Galois / Counter o el modo GCM . La razón es que, en la mayoría de los casos, desea integridad y autenticidad en combinación con confidencialidad (lea más en el blog ).
Tutorial de cifrado / descifrado AES-GCM
Estos son los pasos necesarios para cifrar / descifrar con AES-GCM con Java Cryptography Architecture (JCA) . No mezclar con otros ejemplos , ya que las diferencias sutiles pueden hacer que su código sea completamente inseguro.
1. Crear clave
Como depende de su caso de uso, asumiré el caso más simple: una clave secreta aleatoria.
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");
Importante:
2. Cree el vector de inicialización
Se utiliza un vector de inicialización (IV) para que la misma clave secreta cree diferentes textos cifrados .
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
Importante:
3. Cifrar con IV y Key
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);
Importante:
- use una etiqueta de autenticación de 16 bytes / 128 bits (utilizada para verificar la integridad / autenticidad)
- la etiqueta de autenticación se agregará automáticamente al texto cifrado (en la implementación de JCA)
- dado que GCM se comporta como un cifrado de flujo, no se requiere relleno
- se utiliza
CipherInputStream
al cifrar grandes cantidades de datos
- ¿Quiere que se verifiquen datos adicionales (no secretos) si se modificaron? Es posible que desee utilizar datos asociados con
cipher.updateAAD(associatedData);
Más aquí.
3. Serializar en un solo mensaje
Solo agregue IV y texto cifrado. Como se indicó anteriormente, el IV no tiene por qué ser secreto.
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
Opcionalmente, codifique con Base64 si necesita una representación de cadena. Utilice la implementación incorporada de Android o Java 8 (no use Apache Commons Codec, es una implementación terrible). La codificación se utiliza para "convertir" matrices de bytes en una representación de cadena para que sea ASCII seguro, por ejemplo:
String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);
4. Preparar el descifrado: deserializar
Si ha codificado el mensaje, primero decodifíquelo en una matriz de bytes:
byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)
Importante:
5. Descifrar
Inicialice el cifrado y establezca los mismos parámetros que con el cifrado:
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);
Importante:
- no olvide agregar datos asociados con
cipher.updateAAD(associatedData);
si los agregó durante el cifrado.
Se puede encontrar un fragmento de código funcional en esta esencia.
Tenga en cuenta que las implementaciones más recientes de Android (SDK 21+) y Java (7+) deben tener AES-GCM. Es posible que las versiones anteriores no lo tengan. Sigo eligiendo este modo, ya que es más fácil de implementar además de ser más eficiente en comparación con un modo similar de Encrypt-then-Mac (por ejemplo, con AES-CBC + HMAC ). Consulte este artículo sobre cómo implementar AES-CBC con HMAC .