Las arquitecturas de seguridad en el ecosistema Java se basan fundamentalmente en la Java Cryptography Architecture (JCA) y la Java Cryptography Extension (JCE). Estos frameworks proporcionan las enterfaces y proveedores necesarios para implementar cifrado simétrico, asimétrico y algoritmos de resumen criptográfico.
Cifrado Simétrico con AES
El cifrado simétrico utiliza una única clave secreta tanto para ofuscar como para recuperar los datos. Para garantizar un nivel de seguridad adecuado en producción, es imperativo utilizar un modo de operación que incluya un Vector de Inicialización (IV), como el modo CBC.
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
import java.util.Base64;
public class CifradoSimetrico {
public static void main(String[] args) {
try {
String datosOriginales = "Informacion sensible a proteger";
// Generación de clave y vector de inicialización (IV)
KeyGenerator generadorClave = KeyGenerator.getInstance("AES");
generadorClave.init(256);
SecretKey claveSecreta = generadorClave.generateKey();
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// Proceso de cifrado con modo CBC y relleno PKCS5
Cipher cifrador = Cipher.getInstance("AES/CBC/PKCS5Padding");
cifrador.init(Cipher.ENCRYPT_MODE, claveSecreta, ivSpec);
byte[] datosCifrados = cifrador.doFinal(datosOriginales.getBytes("UTF-8"));
String resultadoCifrado = Base64.getEncoder().encodeToString(datosCifrados);
System.out.println("Payload cifrado: " + resultadoCifrado);
// Proceso de descifrado utilizando el mismo IV
cifrador.init(Cipher.DECRYPT_MODE, claveSecreta, ivSpec);
byte[] datosRecuperados = cifrador.doFinal(Base64.getDecoder().decode(resultadoCifrado));
System.out.println("Payload descifrado: " + new String(datosRecuperados, "UTF-8"));
} catch (Exception e) {
System.err.println("Fallo en la operación simétrica: " + e.getMessage());
}
}
}
Cifrado Asimétrico con RSA
La criptografía de clave pública emplea un par de claves matemáticamente vinculadas: una pública para el cifrado y una privada para el descifrado. Este enfoque es fundamental para el intercambio seguro de claves y las firmas digitales.
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class CifradoAsimetrico {
public static void main(String[] args) {
try {
String mensajePlano = "Credenciales de acceso";
// Creación del par de claves asimétricas de 4096 bits
KeyPairGenerator generadorRSA = KeyPairGenerator.getInstance("RSA");
generadorRSA.initialize(4096);
KeyPair parClaves = generadorRSA.generateKeyPair();
PublicKey clavePublica = parClaves.getPublic();
PrivateKey clavePrivada = parClaves.getPrivate();
// Cifrado utilizando la clave pública
Cipher motorCifrado = Cipher.getInstance("RSA/ECB/PKCS1Padding");
motorCifrado.init(Cipher.ENCRYPT_MODE, clavePublica);
byte[] bloqueCifrado = motorCifrado.doFinal(mensajePlano.getBytes("UTF-8"));
String dataEncriptada = Base64.getEncoder().encodeToString(bloqueCifrado);
System.out.println("Data encriptada: " + dataEncriptada);
// Descifrado utilizando la clave privada
motorCifrado.init(Cipher.DECRYPT_MODE, clavePrivada);
byte[] bloqueDescifrado = motorCifrado.doFinal(bloqueCifrado);
System.out.println("Data original recuperada: " + new String(bloqueDescifrado, "UTF-8"));
} catch (Exception ex) {
System.err.println("Error en operación RSA: " + ex.getMessage());
}
}
}
Generación de Hashes con SHA-256
Las funciones hash criptográficas transforman datos de longitud arbitraria en una cadena de longitud fija. Son operaciones unidireccionales diseñadas para verificar la integridad de la información.
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class GeneradorHash {
public static void main(String[] args) {
try {
String textoEntrada = "Validacion de integridad de datos";
MessageDigest algoritmoHash = MessageDigest.getInstance("SHA-256");
byte[] resumenDigital = algoritmoHash.digest(textoEntrada.getBytes(StandardCharsets.UTF_8));
// Conversión del arreglo de bytes a representación hexadecimal
StringBuilder constructorHex = new StringBuilder();
for (byte b : resumenDigital) {
constructorHex.append(String.format("%02x", b));
}
System.out.println("Resumen SHA-256: " + constructorHex.toString());
} catch (Exception error) {
error.printStackTrace();
}
}
}
Prácticas Recomendadas de Implementcaión
- Gestión de Claves: Evite codificar claves directamente en el código fuente. Utilice almacenes de claves seguros como
KeyStoreo soluciones de gestión de secretos externas (KMS). - Modos de Operación: Nunca utilice el modo ECB (Electronic Codebook) para AES, ya que expone patrones en los datos. Prefiera modos autenticados como GCM o al manos CBC con un IV aleatorio.
- Relleno (Padding): Asegúrese de definir explícitamente el esquema de relleno (ej.
PKCS5Padding,PKCS1Padding) para evitar vulnerabilidades como los ataques de relleno oracle. - Manejo de Excepciones: Las operaciones criptográficas lanzan múltiples excepciones comprobadas (
NoSuchAlgorithmException,InvalidKeyException, etc.). Deben ser capturadas y gestionadas sin exponer información sensible en los logs.
Comparativa de Algoritmos Comunes
| Categoría | Algoritmo | Características Principales |
|---|---|---|
| Cifrado Simétrico | AES | Estándar moderno, alto rendimiento y seguridad robusta. |
| Cifrado Simétrico | DES / 3DES | Estándares heredados, actualmente considerados obsoletos e inseguros. |
| Cifrado Asimétrico | RSA | Basado en factorización de enteros, ideal para intercambio de claves. |
| Cifrado Asimétrico | ECC | Criptografía de curva elíptica, ofrece seguridad equivalente con claves más cortas. |
| Función Hash | SHA-256 | Algoritmo de la familia SHA-2, produce un resumen de 256 bits resistente a colisiones. |
| Función Hash | MD5 | Generador de hashes de 128 bits, altamente vulnerable y en desuso. |