Fundamentos de Ciberseguridad: Criptografía, Cifrado, Funciones Hash y Firmas Digitales
Este artículo es una introducción completa a los fundamentos de ciberseguridad, incluyendo criptografía, cifrado, funciones hash y firmas digitales con ejemplos prácticos.
In a Nutshell
La criptografía protege los datos mediante cifrado, las funciones hash aseguran la integridad y las firmas digitales garantizan la autenticidad. La seguridad moderna se basa en algoritmos matemáticos.
Descripción Técnica Compacta
Criptografía es la ciencia que estudia el cifrado y descifrado de información para protegerla del acceso no autorizado.
Áreas principales:
Cifrado Simétrico
- Concepto: La misma clave para cifrar y descifrar
- Algoritmos: AES, DES, 3DES, Blowfish
- Ventajas: Rápido, eficiente para grandes volúmenes de datos
- Desventajas: Distribución de claves problemática
Cifrado Asimétrico
- Concepto: Clave pública y clave privada
- Algoritmos: RSA, ECC, DSA, ElGamal
- Ventajas: Distribución segura de claves
- Desventajas: Más lento, computacionalmente intensivo
Funciones Hash
- Concepto: Función unidireccional para crear huellas digitales
- Algoritmos: SHA-256, SHA-3, MD5 (obsoleto), bcrypt
- Propiedades: Resistencia a colisiones, resistencia a preimagen
- Aplicaciones: Hash de contraseñas, integridad de datos
Firmas Digitales
- Concepto: Firma criptográfica que verifica autenticidad
- Proceso: Hash → Cifrar con clave privada
- Verificación: Descifrar con clave pública → Comparar hash
- Estándares: RSA, DSA, ECDSA
S/MIME
S/MIME es un estándar que te permite cifrar y firmar digitalmente correos electrónicos. Se basa en criptografía asimétrica y utiliza certificados para autenticar al remitente y al destinatario. Cuando cifras un correo con S/MIME, el contenido solo es legible con la clave privada del destinatario. Una firma digital garantiza que el mensaje procede de ti y no ha sido alterado. S/MIME se utiliza en muchas empresas porque protege la confidencialidad e integridad de la comunicación por correo.
Puntos Clave para Examen
- Criptografía: Ciencia de la comunicación segura
- Cifrado Simétrico: AES, misma clave para ambas direcciones
- Cifrado Asimétrico: RSA, pares de clave pública/privada
- Funciones Hash: SHA-256, hash unidireccional para integridad
- Firmas Digitales: RSA/ECDSA, autenticidad e integridad
- SSL/TLS: Comunicación web cifrada
- Relevancia IHK: Base de seguridad IT y protección de datos
Componentes Clave
- Cifrado: Protección de confidencialidad
- Funciones Hash: Aseguramiento de integridad
- Firmas Digitales: Garantía de autenticidad
- Infraestructura de Clave Pública: Gestión de claves
- SSL/TLS: Comunicación segura de red
- Protocolos Criptográficos: Transferencia segura de datos
- Gestión de Claves: Generación, almacenamiento, distribución
- Mejores Prácticas de Seguridad: Implementación y aplicación
Ejemplos Prácticos
1. Cifrado Simétrico con AES
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.util.Base64;
public class SymmetricEncryptionDemo {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static final int KEY_LENGTH = 256;
private static final int IV_LENGTH = 16;
// Generar clave AES
public static SecretKey generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(KEY_LENGTH);
return keyGenerator.generateKey();
}
// Generar vector de inicialización (IV)
public static byte[] generateIV() {
byte[] iv = new byte[IV_LENGTH];
new SecureRandom().nextBytes(iv);
return iv;
}
// Cifrar datos
public static String encryptAES(String plaintext, SecretKey key, byte[] iv)
throws Exception {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
// Combinar IV y datos cifrados
byte[] combined = new byte[iv.length + encryptedBytes.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);
return Base64.getEncoder().encodeToString(combined);
}
// Descifrar datos
public static String decryptAES(String ciphertext, SecretKey key) throws Exception {
byte[] combined = Base64.getDecoder().decode(ciphertext);
// Extraer IV
byte[] iv = new byte[IV_LENGTH];
System.arraycopy(combined, 0, iv, 0, iv.length);
// Extraer datos cifrados
byte[] encryptedBytes = new byte[combined.length - iv.length];
System.arraycopy(combined, iv.length, encryptedBytes, 0, encryptedBytes.length);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
// Demo de AES-256
public static void aesDemo() {
try {
System.out.println("=== Demo de Cifrado AES-256 ===");
// Generar clave
SecretKey aesKey = generateAESKey();
System.out.println("Clave AES-256 generada");
System.out.println("Clave (Base64): " + Base64.getEncoder().encodeToString(aesKey.getEncoded()));
// Datos de prueba
String plaintext = "Este es un mensaje secreto que será cifrado con AES-256.";
System.out.println("\nTexto claro: " + plaintext);
// Cifrar
byte[] iv = generateIV();
String ciphertext = encryptAES(plaintext, aesKey, iv);
System.out.println("\nCifrado: " + ciphertext);
// Descifrar
String decryptedText = decryptAES(ciphertext, aesKey);
System.out.println("\nDescifrado: " + decryptedText);
// Verificación
System.out.println("\nCifrado exitoso: " + plaintext.equals(decryptedText));
} catch (Exception e) {
System.err.println("Error en cifrado AES: " + e.getMessage());
}
}
// Comparación de rendimiento de modos AES
public static void compareAESModes() {
try {
System.out.println("\n=== Comparación de Rendimiento de Modos AES ===");
String[] modes = {"AES/ECB/PKCS5Padding", "AES/CBC/PKCS5Padding",
"AES/GCM/NoPadding", "AES/CFB/PKCS5Padding"};
SecretKey key = generateAESKey();
String testData = "Datos de prueba de rendimiento para diferentes modos AES. ".repeat(100);
for (String mode : modes) {
long startTime = System.nanoTime();
try {
Cipher cipher = Cipher.getInstance(mode);
if (mode.contains("ECB")) {
cipher.init(Cipher.ENCRYPT_MODE, key);
} else {
byte[] iv = generateIV();
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
}
byte[] encrypted = cipher.doFinal(testData.getBytes());
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1_000_000; // ms
System.out.printf("%-25s: %dms (%d bytes)%n",
mode, duration, encrypted.length);
} catch (Exception e) {
System.out.printf("%-25s: Error - %s%n", mode, e.getMessage());
}
}
} catch (Exception e) {
System.err.println("Error en comparación de rendimiento: " + e.getMessage());
}
}
public static void main(String[] args) {
aesDemo();
compareAESModes();
}
}
2. Cifrado asimétrico con RSA
import javax.crypto.*;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
public class AsymmetricEncryptionDemo {
private static final String ALGORITHM = "RSA";
private static final int KEY_SIZE = 2048;
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
// Generar par de claves RSA
public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(KEY_SIZE);
return keyGen.generateKeyPair();
}
// Cifrar con clave pública
public static String encryptRSA(String plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// Descifrar con clave privada
public static String decryptRSA(String ciphertext, PrivateKey privateKey) throws Exception {
byte[] encryptedBytes = Base64.getDecoder().decode(ciphertext);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
// Crear firma digital
public static String signData(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(data.getBytes());
byte[] signatureBytes = signature.sign();
return Base64.getEncoder().encodeToString(signatureBytes);
}
// Verificar firma digital
public static boolean verifySignature(String data, String signatureStr, PublicKey publicKey)
throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(data.getBytes());
byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
return signature.verify(signatureBytes);
}
// Demo RSA
public static void rsaDemo() {
try {
System.out.println("=== Demo de Cifrado RSA ===");
// Generar par de claves
KeyPair keyPair = generateRSAKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
System.out.println("Par de claves RSA-2048 generado");
System.out.println("Public Key: " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("Private Key: " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
// Datos de prueba
String plaintext = "Este mensaje se cifra con RSA-2048 y se firma digitalmente.";
System.out.println("\nTexto en claro: " + plaintext);
// Cifrar con clave pública
String ciphertext = encryptRSA(plaintext, publicKey);
System.out.println("\nCifrado (Clave Pública): " + ciphertext);
// Descifrar con clave privada
String decryptedText = decryptRSA(ciphertext, privateKey);
System.out.println("Descifrado (Clave Privada): " + decryptedText);
// Crear firma digital
String signature = signData(plaintext, privateKey);
System.out.println("\nFirma Digital: " + signature);
// Verificar firma
boolean isValid = verifySignature(plaintext, signature, publicKey);
System.out.println("Firma válida: " + isValid);
// Probar firma manipulada
String manipulatedData = plaintext + " (manipulado)";
boolean isManipulatedValid = verifySignature(manipulatedData, signature, publicKey);
System.out.println("Firma manipulada válida: " + isManipulatedValid);
} catch (Exception e) {
System.err.println("Error en cifrado RSA: " + e.getMessage());
}
}
// Cifrado híbrido (RSA + AES)
public static void hybridEncryptionDemo() {
try {
System.out.println("\n=== Demo de Cifrado Híbrido (RSA + AES) ===");
// Generar claves
KeyPair rsaKeyPair = generateRSAKeyPair();
SecretKey aesKey = SymmetricEncryptionDemo.generateAESKey();
// Gran volumen de datos
String largeData = "Este es un gran volumen de datos que se cifra con AES y la clave AES se cifra luego con RSA. ".repeat(50);
System.out.println("Tamaño de datos originales: " + largeData.length() + " caracteres");
// Paso 1: Cifrar datos con AES
byte[] iv = SymmetricEncryptionDemo.generateIV();
String encryptedData = SymmetricEncryptionDemo.encryptAES(largeData, aesKey, iv);
System.out.println("Cifrado con AES: " + encryptedData.length() + " caracteres");
// Paso 2: Cifrar clave AES con RSA
String encryptedKey = encryptRSA(Base64.getEncoder().encodeToString(aesKey.getEncoded()), rsaKeyPair.getPublic());
System.out.println("Clave AES cifrada con RSA");
// Paso 3: Descifrado (proceso inverso)
String decryptedKey = decryptRSA(encryptedKey, rsaKeyPair.getPrivate());
byte[] decodedKey = Base64.getDecoder().decode(decryptedKey);
SecretKey restoredAESKey = new SecretKeySpec(decodedKey, "AES");
String decryptedData = SymmetricEncryptionDemo.decryptAES(encryptedData, restoredAESKey);
System.out.println("Cifrado híbrido exitoso: " + largeData.equals(decryptedData));
} catch (Exception e) {
System.err.println("Error en cifrado híbrido: " + e.getMessage());
}
}
// Comparación de tamaños de clave RSA
public static void compareKeySizes() {
try {
System.out.println("\n=== Comparación de Tamaños de Clave RSA ===");
int[] keySizes = {1024, 2048, 4096};
String testData = "Datos de prueba para comparación de tamaño de clave";
for (int keySize : keySizes) {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(keySize);
KeyPair keyPair = keyGen.generateKeyPair();
long startTime = System.nanoTime();
String encrypted = encryptRSA(testData, keyPair.getPublic());
long encryptTime = System.nanoTime() - startTime;
startTime = System.nanoTime();
String decrypted = decryptRSA(encrypted, keyPair.getPrivate());
long decryptTime = System.nanoTime() - startTime;
System.out.printf("RSA-%d: Cifrado %dms, Descifrado %dms%n",
keySize, encryptTime / 1_000_000, decryptTime / 1_000_000);
} catch (Exception e) {
System.out.printf("RSA-%d: Error - %s%n", keySize, e.getMessage());
}
}
} catch (Exception e) {
System.err.println("Error en comparación de tamaños: " + e.getMessage());
}
}
public static void main(String[] args) {
rsaDemo();
hybridEncryptionDemo();
compareKeySizes();
}
}
3. Funciones hash y seguridad de contraseñas
import java.security.*;
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Arrays;
public class HashFunctionsDemo {
private static final String SHA_256 = "SHA-256";
private static final String SHA_3_256 = "SHA3-256";
private static final String BCRYPT = "BCrypt";
// Calcular hash SHA-256
public static String sha256(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(SHA_256);
byte[] hashBytes = digest.digest(input.getBytes());
return Base64.getEncoder().encodeToString(hashBytes);
}
// Calcular hash SHA-3
public static String sha3_256(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(SHA_3_256);
byte[] hashBytes = digest.digest(input.getBytes());
return Base64.getEncoder().encodeToString(hashBytes);
}
// Hash con salt (salt aleatorio)
public static String saltedHash(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(SHA_256);
digest.reset();
digest.update(salt);
byte[] hashBytes = digest.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hashBytes);
}
// Generar salt
public static byte[] generateSalt() {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
return salt;
}
// PBKDF2 para hash de contraseñas
public static String pbkdf2Hash(String password, byte[] salt, int iterations, int keyLength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = skf.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(hash);
}
// Verificar contraseña con PBKDF2
public static boolean verifyPassword(String password, String storedHash, byte[] salt, int iterations)
throws NoSuchAlgorithmException, InvalidKeySpecException {
String newHash = pbkdf2Hash(password, salt, iterations, storedHash.length());
return newHash.equals(storedHash);
}
// HMAC para autenticación de mensajes
public static String hmacSHA256(String data, String secretKey) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(hmacBytes);
}
// Prueba de colisión de hashes
public static void testHashCollisions() {
try {
System.out.println("=== Prueba de Colisión de Hashes ===");
String[] testStrings = {
"password123",
"password124", // Muy similar
"Password123", // Mayúsculas/minúsculas
"pass word123", // Espacio
"pa$$word123" // Carácter especial
};
System.out.println("Hashes SHA-256:");
for (String test : testStrings) {
String hash = sha256(test);
System.out.printf("%-15s: %s%n", test, hash);
}
System.out.println("\nHashes SHA-3:");
for (String test : testStrings) {
String hash = sha3_256(test);
System.out.printf("%-15s: %s%n", test, hash);
}
} catch (Exception e) {
System.err.println("Error en prueba de colisión: " + e.getMessage());
}
}
// Demo de seguridad de contraseñas
public static void passwordSecurityDemo() {
try {
System.out.println("\n=== Demo de Seguridad de Contraseñas ===");
String password = "MySecurePassword123!";
// 1. Hash simple (inseguro)
String simpleHash = sha256(password);
System.out.println("SHA-256 simple: " + simpleHash);
// 2. Hash con salt
byte[] salt = generateSalt();
String saltedHashStr = saltedHash(password, salt);
System.out.println("Hash con salt: " + saltedHashStr);
System.out.println("Salt: " + Base64.getEncoder().encodeToString(salt));
// 3. PBKDF2 (recomendado)
int iterations = 10000;
int keyLength = 256;
String pbkdf2HashStr = pbkdf2Hash(password, salt, iterations, keyLength);
System.out.println("Hash PBKDF2: " + pbkdf2HashStr);
System.out.println("Iteraciones: " + iterations);
// 4. Verificación
boolean isValid = verifyPassword(password, pbkdf2HashStr, salt, iterations);
System.out.println("Contraseña válida: " + isValid);
// 5. Protección contra timing attacks
System.out.println("\nPrueba de Protección contra Timing Attacks:");
testTimingAttackProtection();
} catch (Exception e) {
System.err.println("Error en seguridad de contraseña: " + e.getMessage());
}
}
// Demo de protección contra timing attacks
public static void testTimingAttackProtection() {
try {
String correctPassword = "correctPassword123";
String wrongPassword = "wrongPassword456";
byte[] salt = generateSalt();
String storedHash = pbkdf2Hash(correctPassword, salt, 10000, 256);
// Pruebas de tiempo
long[] correctTimes = new long[10];
long[] wrongTimes = new long[10];
for (int i = 0; i < 10; i++) {
// Contraseña correcta
long start = System.nanoTime();
verifyPassword(correctPassword, storedHash, salt, 10000);
correctTimes[i] = System.nanoTime() - start;
// Contraseña incorrecta
start = System.nanoTime();
verifyPassword(wrongPassword, storedHash, salt, 10000);
wrongTimes[i] = System.nanoTime() - start;
}
long avgCorrect = Arrays.stream(correctTimes).sum() / correctTimes.length;
long avgWrong = Arrays.stream(wrongTimes).sum() / wrongTimes.length;
System.out.printf("Contraseña correcta: %dms (promedio)%n", avgCorrect / 1_000_000);
System.out.printf("Contraseña incorrecta: %dms (promedio)%n", avgWrong / 1_000_000);
System.out.printf("Diferencia de tiempo: %.2f%%%n",
Math.abs(avgCorrect - avgWrong) * 100.0 / Math.max(avgCorrect, avgWrong));
} catch (Exception e) {
System.err.println("Error en prueba de timing attack: " + e.getMessage());
}
}
// Demo HMAC
public static void hmacDemo() {
try {
System.out.println("\n=== Demo HMAC ===");
String message = "Este es un mensaje confidencial";
String secretKey = "claveSecreta123";
// Calcular HMAC
String hmac = hmacSHA256(message, secretKey);
System.out.println("Mensaje: " + message);
System.out.println("HMAC: " + hmac);
// HMAC con clave incorrecta
String wrongKey = "claveIncorrecta456";
String wrongHmac = hmacSHA256(message, wrongKey);
System.out.println("HMAC (clave incorrecta): " + wrongHmac);
// Verificación
boolean isValid = hmac.equals(hmacSHA256(message, secretKey));
boolean isInvalid = !wrongHmac.equals(hmacSHA256(message, secretKey));
System.out.println("HMAC válido: " + isValid);
System.out.println("HMAC incorrecto detectado: " + isInvalid);
} catch (Exception e) {
System.err.println("Error en demo HMAC: " + e.getMessage());
}
}
public static void main(String[] args) {
testHashCollisions();
passwordSecurityDemo();
hmacDemo();
}
}
4. SSL/TLS y certificados
import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.*;
import java.util.Base64;
public class SSLTLSDemo {
// Crear contexto SSL
public static SSLContext createSSLContext() throws Exception {
// Trust Manager (para certificados de servidor)
TrustManager[] trustManagers = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
// En producción: validar certificado
System.out.println("Certificado de servidor validado");
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext;
}
// Solicitud HTTPS con SSL
public static void makeHTTPSRequest(String urlString) {
try {
System.out.println("=== Demo de solicitud HTTPS ===");
URL url = new URL(urlString);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
// Establecer contexto SSL
SSLContext sslContext = createSSLContext();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
// Verificador de hostname (para demo)
connection.setHostnameVerifier((hostname, session) -> {
System.out.println("Hostname: " + hostname);
return true; // En producción: verificación correcta de hostname
});
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
int responseCode = connection.getResponseCode();
System.out.println("Código de respuesta: " + responseCode);
// Información de certificados
Certificate[] certs = connection.getServerCertificates();
if (certs.length > 0 && certs[0] instanceof java.security.cert.X509Certificate) {
java.security.cert.X509Certificate cert = (java.security.cert.X509Certificate) certs[0];
System.out.println("Certificado de servidor:");
System.out.println(" Asunto: " + cert.getSubjectDN());
System.out.println(" Emisor: " + cert.getIssuerDN());
System.out.println(" Válido desde: " + cert.getNotBefore());
System.out.println(" Válido hasta: " + cert.getNotAfter());
System.out.println(" Número de serie: " + cert.getSerialNumber());
}
// Versión TLS
System.out.println("Protocolo TLS: " + connection.getSSLSession().getProtocol());
System.out.println("Suite de cifrado: " + connection.getSSLSession().getCipherSuite());
// Leer respuesta
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line).append("\n");
}
System.out.println("Respuesta (primeros 200 caracteres):");
System.out.println(response.substring(0, Math.min(200, response.length())));
}
} catch (Exception e) {
System.err.println("Error en solicitud HTTPS: " + e.getMessage());
}
}
// Generar certificado autofirmado
public static void generateSelfSignedCertificate() {
try {
System.out.println("\n=== Demo de certificado autofirmado ===");
// Generar KeyPair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
// Crear certificado (simplificado)
// En la práctica se utilizarían librerías como BouncyCastle
System.out.println("KeyPair generado para certificado autofirmado");
System.out.println("Clave pública: " + Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
} catch (Exception e) {
System.err.println("Error al crear certificado: " + e.getMessage());
}
}
// Listar suites de cifrado
public static void listCipherSuites() {
try {
System.out.println("\n=== Suites de cifrado soportadas ===");
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
String[] cipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
System.out.println("Cantidad de suites de cifrado soportadas: " + cipherSuites.length);
System.out.println("\nSuites de cifrado recomendadas:");
for (String suite : cipherSuites) {
// Mostrar solo suites de cifrado modernas y seguras
if (suite.contains("TLS_ECDHE") && suite.contains("GCM")) {
System.out.println(" " + suite);
}
}
} catch (Exception e) {
System.err.println("Error al listar suites de cifrado: " + e.getMessage());
}
}
// Analizar handshake SSL
public static void analyzeSSLHandshake() {
try {
System.out.println("\n=== Análisis de handshake SSL ===");
SSLContext sslContext = createSSLContext();
// Parámetros SSL personalizados
SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
sslParams.setProtocols(new String[]{"TLSv1.3", "TLSv1.2"});
// Suites de cifrado seguras
String[] secureSuites = {
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
};
sslParams.setCipherSuites(secureSuites);
System.out.println("Configuración SSL:");
System.out.println(" Protocolos: " + String.join(", ", sslParams.getProtocols()));
System.out.println(" Suites de cifrado: " + sslParams.getCipherSuites().length + " configuradas");
System.out.println(" Verificación de hostname: " + sslParams.getEndpointIdentificationAlgorithm());
} catch (Exception e) {
System.err.println("Error en análisis SSL: " + e.getMessage());
}
}
// Validación de cadena de certificados
public static void validateCertificateChain() {
try {
System.out.println("\n=== Validación de cadena de certificados ===");
// Ejemplo de validación de ruta de certificados
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// En la práctica se cargarían los certificados desde un archivo
// Aquí mostramos solo los conceptos
System.out.println("Conceptos de validación de ruta de certificados:");
System.out.println("1. Certificado de CA raíz");
System.out.println("2. Certificado(s) de CA intermedia");
System.out.println("3. Certificado de entidad final");
System.out.println("4. Verificación de revocación de certificados (CRL/OCSP)");
System.out.println("5. Registros de transparencia de certificados");
// Configuración de Trust Manager
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// KeyStore para certificados de confianza
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null); // TrustStore vacío
// En producción: cargar TrustStore del sistema o añadir certificados propios
// trustStore.load(new FileInputStream("truststore.jks"), "password".toCharArray());
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
System.out.println("Trust Manager configurado: " + trustManagers.length + " manager(s)");
} catch (Exception e) {
System.err.println("Error en validación de certificados: " + e.getMessage());
}
}
public static void main(String[] args) {
// Demo de solicitud HTTPS
// makeHTTPSRequest("https://www.google.com");
// Demos de certificados
generateSelfSignedCertificate();
listCipherSuites();
analyzeSSLHandshake();
validateCertificateChain();
}
}
Resumen de Algoritmos de Criptografía
Algoritmos Simétricos
| Algoritmo | Longitud de Clave | Tamaño de Bloque | Aplicación | Seguridad |
|---|---|---|---|---|
| AES | 128/192/256 | 128 Bit | Estándar | Seguro |
| DES | 56 | 64 | Obsoleto | Inseguro |
| 3DES | 168 | 64 | Legacy | Débil |
| Blowfish | 32-448 | 64 | Varios | Seguro |
Algoritmos Asimétricos
| Algoritmo | Longitud de Clave | Aplicación | Seguridad | Rendimiento |
|---|---|---|---|---|
| RSA | 1024-4096 | Firma/Cifrado | Seguro | Lento |
| ECC | 160-521 | Firma/Cifrado | Seguro | Rápido |
| DSA | 1024-3072 | Firma | Seguro | Lento |
| ElGamal | 1024-4096 | Cifrado | Seguro | Lento |
Algoritmos Hash
| Algoritmo | Longitud de Salida | Seguridad de Colisiones | Estado | Aplicación |
|---|---|---|---|---|
| SHA-256 | 256 Bit | Segura | Recomendado | Multipropósito |
| SHA-3 | 224-512 | Segura | Moderno | Multipropósito |
| MD5 | 128 Bit | Roto | Obsoleto | Sumas de Verificación |
| bcrypt | Variable | Segura | Recomendado | Contraseñas |
Versiones del Protocolo SSL/TLS
| Versión | Año | Seguridad | Cipher Suites | Recomendación |
|---|---|---|---|---|
| SSL 2.0 | 1995 | Inseguro | Obsoleto | No usar |
| SSL 3.0 | 1996 | Inseguro | Obsoleto | No usar |
| TLS 1.0 | 1999 | Débil | Limitado | No usar |
| TLS 1.1 | 2006 | Aceptable | Limitado | No usar |
| TLS 1.2 | 2008 | Seguro | Moderno | Recomendado |
| TLS 1.3 | 2018 | Muy Seguro | Moderno | Mejor opción |
Buenas Prácticas de Seguridad de Contraseñas
Métodos de Hash
// ❌ Inseguro
String hash = md5(password);
// ⚠️ Mejor
String hash = sha256(password);
// ✅ Seguro
String hash = pbkdf2(password, salt, 100000);
// ✅ Óptimo
String hash = bcrypt(password);
Salting
// Generar salt
byte[] salt = generateSalt(); // 16+ Bytes aleatorios
// Guardar salt (no es secreto)
String saltedHash = salt + ":" + hash(password, salt);
Derivación de Clave
// Parámetros PBKDF2
int iterations = 100000; // Mínimo 100.000
int keyLength = 256; // 256 Bit
int saltLength = 32; // 32 Bytes
String derivedKey = pbkdf2(password, salt, iterations, keyLength);
Proceso de Firmas Digitales
Creación de Firma
- Calcular hash:
hash = SHA256(data) - Cifrar hash:
signature = RSA_encrypt(hash, private_key) - Adjuntar firma:
data + signature
Verificación de Firma
- Extraer firma:
signature = extract(data_with_signature) - Calcular hash:
hash = SHA256(data) - Descifrar firma:
decrypted_hash = RSA_decrypt(signature, public_key) - Comparar:
hash == decrypted_hash ?
Infraestructura de Clave Pública (PKI)
Componentes
- Root CA: Autoridad de Certificación raíz confiable
- Intermediate CA: Autoridades de Certificación intermedias
- End Entity: Certificados de servidor/cliente
- CRL: Lista de Revocación de Certificados
- OCSP: Protocolo de Estado de Certificado en Línea
Validación de Certificados
// 1. Verificar cadena de certificados
// 2. Verificar fecha de expiración
// 3. Verificar revocación (CRL/OCSP)
// 4. Verificar nombre de host
// 5. Verificar firma
Mejores Prácticas de Seguridad
Implementación
// ✅ Configuración segura
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, trustManagers, null);
// ✅ Cipher Suites seguros
String[] secureSuites = {
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_AES_128_GCM_SHA256"
};
// ✅ Verificación de nombre de host
connection.setHostnameVerifier((hostname, session) -> {
return hostname.equals(session.getPeerHost());
});
Errores a Evitar
// ❌ Incorrecto: Deshabilitar TrustManager
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() { /* aceptar todos los certificados */ }
};
// ✅ Correcto: TrustManager personalizado con validación
TrustManager[] secureTrustManagers = new TrustManager[] {
new X509TrustManager() {
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// Lógica de validación personalizada
}
}
};
Ventajas y Desventajas
Ventajas de la Criptografía
- Confidencialidad: Protección contra acceso no autorizado
- Integridad: Detección de manipulación de datos
- Autenticidad: Verificación de identidad
- No Repudio: Irrefutabilidad
- Cumplimiento: Cumplimiento de estándares de seguridad
Desventajas
- Complejidad: Se requiere experiencia
- Rendimiento: Operaciones computacionalmente intensivas
- Gestión de Claves: Administración de claves compleja
- Sobrecarga: Infraestructura adicional
Preguntas Frecuentes de Examen
-
¿Cuál es la diferencia entre cifrado simétrico y asimétrico? El cifrado simétrico usa una clave para ambas direcciones, mientras que el asimétrico usa pares de clave pública/privada.
-
¿Por qué son importantes los salts en el hashing de contraseñas? Los salts previenen ataques de Rainbow Table y garantizan hashes únicos incluso para contraseñas idénticas.
-
¡Explica las firmas digitales! Las firmas digitales usan hashing y cifrado asimétrico para garantizar autenticidad e integridad.
-
¿Cuál es el propósito de SSL/TLS? SSL/TLS asegura la comunicación en internet mediante cifrado y autenticación.
Fuentes Principales
- https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html
- https://www.ietf.org/rfc/rfc5246.html (TLS 1.2)
- https://www.ietf.org/rfc/rfc8446.html (TLS 1.3)
Literatura Recomendada: Ciberseguridad
Keine Bücher für Kategorie "cybersecurity" gefunden.
Más Artículos de Ciberseguridad
La ciberseguridad y la criptografía son fundamentales para el desarrollo seguro de software. Los siguientes artículos te ayudarán a comprender todos los aspectos de la seguridad informática.
Seguridad en Linux
- Recomendaciones de Libros sobre Seguridad de Linux en Ciberseguridad - Conceptos de seguridad especializados para sistemas Linux



