Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:
>> VER EL CURSO1. Información general
En este tutorial, veremos cómo cifrar y descifrar un archivo utilizando las API de JDK existentes.
2. Escribir una prueba primero
Comenzaremos escribiendo nuestra prueba, estilo TDD. Dado que vamos a trabajar con archivos aquí, una prueba de integración parece ser apropiada.
Como solo estamos usando la funcionalidad JDK existente, no se necesitan dependencias externas.
Primero, encriptaremos el contenido usando una clave secreta recién generada (estamos usando AES, Advanced Encryption Standard, como el algoritmo de encriptación simétrica en este ejemplo).
También tenga en cuenta que estamos definiendo la cadena de transformación completa en el constructor ( AES / CBC / PKCS5Padding ), que es una concatenación de cifrado usado, modo de cifrado de bloque y relleno ( algoritmo / modo / relleno ). Las implementaciones de JDK admiten una serie de transformaciones diferentes de forma predeterminada, pero tenga en cuenta que no todas las combinaciones pueden considerarse criptográficamente seguras según los estándares actuales.
Vamos a suponer que nuestra FileEncrypterDecrypter clase escribirá la salida a un archivo llamado baz.enc . Luego, desciframos este archivo usando la misma clave secreta y verificamos que el contenido descifrado sea igual al contenido original:
@Test public void whenEncryptingIntoFile_andDecryptingFileAgain_thenOriginalStringIsReturned() { String originalContent = "foobar"; SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey(); FileEncrypterDecrypter fileEncrypterDecrypter = new FileEncrypterDecrypter(secretKey, "AES/CBC/PKCS5Padding"); fileEncrypterDecrypter.encrypt(originalContent, "baz.enc"); String decryptedContent = fileEncrypterDecrypter.decrypt("baz.enc"); assertThat(decryptedContent, is(originalContent)); new File("baz.enc").delete(); // cleanup }
3. Cifrado
Inicializaremos el cifrado en el constructor de nuestra clase FileEncrypterDecrypter usando la cadena de transformación especificada .
Esto nos permite fallar temprano en caso de que se especifique una transformación incorrecta:
FileEncrypterDecrypter(SecretKey secretKey, String transformation) { this.secretKey = secretKey; this.cipher = Cipher.getInstance(transformation); }
Luego podemos usar el cifrado instanciado y la clave secreta proporcionada para realizar el cifrado:
void encrypt(String content, String fileName) { cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] iv = cipher.getIV(); try (FileOutputStream fileOut = new FileOutputStream(fileName); CipherOutputStream cipherOut = new CipherOutputStream(fileOut, cipher)) { fileOut.write(iv); cipherOut.write(content.getBytes()); } }
Java nos permite aprovechar la conveniente clase CipherOutputStream para escribir el contenido cifrado en otro OutputStream .
Tenga en cuenta que estamos escribiendo el IV (Vector de inicialización) al principio del archivo de salida. En este ejemplo, el IV se genera automáticamente al inicializar el cifrado .
El uso de un IV es obligatorio cuando se usa el modo CBC, para aleatorizar la salida cifrada. Sin embargo, el IV no se considera un secreto, por lo que está bien escribirlo al principio del archivo.
4. Descifrado
Para descifrar también tenemos que leer primero el IV. Luego, podemos inicializar nuestro cifrado y descifrar el contenido.
De nuevo, podemos hacer uso de una clase especial de Java, CipherInputStream , que se encarga de forma transparente del descifrado real :
String decrypt(String fileName) { String content; try (FileInputStream fileIn = new FileInputStream(fileName)) { byte[] fileIv = new byte[16]; fileIn.read(fileIv); cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv)); try ( CipherInputStream cipherIn = new CipherInputStream(fileIn, cipher); InputStreamReader inputReader = new InputStreamReader(cipherIn); BufferedReader reader = new BufferedReader(inputReader) ) { StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } content = sb.toString(); } } return content; }
5. Conclusión
Hemos visto que podemos realizar cifrado y descifrado básico utilizando clases JDK estándar, como Cipher , CipherOutputStream y CipherInputStream .
Como de costumbre, el código completo de este artículo está disponible en nuestro repositorio de GitHub.
Además, puede encontrar una lista de los cifrados disponibles en el JDK aquí.
Finalmente, tenga en cuenta que los ejemplos de código aquí no están pensados como código de grado de producción y los detalles específicos de su sistema deben considerarse detenidamente al usarlos.
Fondo de Java