Skip to content
IRC-CodingIRC-Coding
Fundamentos de ciberseguridadCriptografía encriptaciónFunciones hashFirmas digitalesRSA AESAlgoritmosFundamentos

Criptografía y Seguridad: Encriptación y Firmas Digitales

Aprende criptografía, encriptación simétrica y asimétrica, funciones hash y firmas digitales. Guía práctica con RSA, AES y SSL/TLS.

S

schutzgeist

17 min read
Criptografía y Seguridad: Encriptación y Firmas Digitales

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

  1. Cifrado: Protección de confidencialidad
  2. Funciones Hash: Aseguramiento de integridad
  3. Firmas Digitales: Garantía de autenticidad
  4. Infraestructura de Clave Pública: Gestión de claves
  5. SSL/TLS: Comunicación segura de red
  6. Protocolos Criptográficos: Transferencia segura de datos
  7. Gestión de Claves: Generación, almacenamiento, distribución
  8. 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

AlgoritmoLongitud de ClaveTamaño de BloqueAplicaciónSeguridad
AES128/192/256128 BitEstándarSeguro
DES5664ObsoletoInseguro
3DES16864LegacyDébil
Blowfish32-44864VariosSeguro

Algoritmos Asimétricos

AlgoritmoLongitud de ClaveAplicaciónSeguridadRendimiento
RSA1024-4096Firma/CifradoSeguroLento
ECC160-521Firma/CifradoSeguroRápido
DSA1024-3072FirmaSeguroLento
ElGamal1024-4096CifradoSeguroLento

Algoritmos Hash

AlgoritmoLongitud de SalidaSeguridad de ColisionesEstadoAplicación
SHA-256256 BitSeguraRecomendadoMultipropósito
SHA-3224-512SeguraModernoMultipropósito
MD5128 BitRotoObsoletoSumas de Verificación
bcryptVariableSeguraRecomendadoContraseñas

Versiones del Protocolo SSL/TLS

VersiónAñoSeguridadCipher SuitesRecomendación
SSL 2.01995InseguroObsoletoNo usar
SSL 3.01996InseguroObsoletoNo usar
TLS 1.01999DébilLimitadoNo usar
TLS 1.12006AceptableLimitadoNo usar
TLS 1.22008SeguroModernoRecomendado
TLS 1.32018Muy SeguroModernoMejor 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

  1. Calcular hash: hash = SHA256(data)
  2. Cifrar hash: signature = RSA_encrypt(hash, private_key)
  3. Adjuntar firma: data + signature

Verificación de Firma

  1. Extraer firma: signature = extract(data_with_signature)
  2. Calcular hash: hash = SHA256(data)
  3. Descifrar firma: decrypted_hash = RSA_decrypt(signature, public_key)
  4. 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

  1. ¿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.

  2. ¿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.

  3. ¡Explica las firmas digitales! Las firmas digitales usan hashing y cifrado asimétrico para garantizar autenticidad e integridad.

  4. ¿Cuál es el propósito de SSL/TLS? SSL/TLS asegura la comunicación en internet mediante cifrado y autenticación.

Fuentes Principales

  1. https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html
  2. https://www.ietf.org/rfc/rfc5246.html (TLS 1.2)
  3. 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

Volver al blog
Share:

Entradas relacionadas