Skip to content
IRC-CodingIRC-Coding
Osnovy CybersecurityKriptografiya shifrovanieHash funkciiCifrovye podpisiRSA AESAlgoritmyAlgoritmOsnovy

Основы кибербезопасности: Cryptography и цифровые подписи

Основы Cybersecurity: Cryptography, шифрование, Hash функции и цифровые подписи. RSA, AES, SSL/TLS с примерами.

S

schutzgeist

17 min read
Основы кибербезопасности: Cryptography и цифровые подписи

Основы кибербезопасности: криптография, шифрование, хеш-функции и цифровые подписи

Это подробное введение в основы кибербезопасности, включающее криптографию, шифрование, хеш-функции и цифровые подписи с практическими примерами.

Суть вопроса

Криптография защищает данные через шифрование, хеш-функции обеспечивают целостность, а цифровые подписи гарантируют аутентичность. Современная безопасность строится на математических алгоритмах.

Краткое описание

Криптография — это наука об алгоритмах шифрования и расшифровки информации для защиты от несанкционированного доступа.

Основные направления:

Симметричное шифрование

  • Концепция: один и тот же ключ для шифрования и расшифровки
  • Алгоритмы: AES, DES, 3DES, Blowfish
  • Преимущества: быстрое, эффективное для больших объёмов данных
  • Недостатки: сложное распределение ключей

Асимметричное шифрование

  • Концепция: открытый и закрытый ключи
  • Алгоритмы: RSA, ECC, DSA, ElGamal
  • Преимущества: безопасное распределение ключей
  • Недостатки: медленнее, требует больших вычислительных ресурсов

Хеш-функции

  • Концепция: односторонняя функция для создания цифровых отпечатков
  • Алгоритмы: SHA-256, SHA-3, MD5 (устарел), bcrypt
  • Свойства: устойчивость к коллизиям, устойчивость к прообразу
  • Применение: хеширование паролей, проверка целостности данных

Цифровые подписи

  • Концепция: криптографическая подпись для подтверждения аутентичности
  • Процесс: хеширование → шифрование закрытым ключом
  • Проверка: расшифровка открытым ключом → сравнение хешей
  • Стандарты: RSA, DSA, ECDSA

S/MIME

S/MIME — стандарт, позволяющий шифровать и подписывать письма цифровой подписью. Он основан на асимметричной криптографии и использует сертификаты для аутентификации отправителя и получателя. Когда вы шифруете письмо с помощью S/MIME, содержимое становится доступным только с помощью закрытого ключа получателя. Цифровая подпись гарантирует, что сообщение действительно от вас и не было изменено. S/MIME широко используется в корпоративной среде для защиты конфиденциальности и целостности электронной почты.

Ключевые моменты для экзамена

  • Криптография: наука о безопасной коммуникации
  • Симметричное шифрование: AES, один ключ для обоих направлений
  • Асимметричное шифрование: RSA, пары открытого и закрытого ключей
  • Хеш-функции: SHA-256, односторонний хеш для целостности
  • Цифровые подписи: RSA/ECDSA, аутентичность и целостность
  • SSL/TLS: защищённое веб-соединение
  • Актуально для экзамена: основа IT-безопасности и защиты данных

Основные компоненты

  1. Шифрование: защита конфиденциальности
  2. Хеш-функции: обеспечение целостности
  3. Цифровые подписи: гарантия аутентичности
  4. Public Key Infrastructure: управление ключами
  5. SSL/TLS: защищённое сетевое соединение
  6. Криптографические протоколы: безопасная передача данных
  7. Управление ключами: генерация, хранение, распределение
  8. Best practices безопасности: реализация и применение

Практические примеры

1. Симметричное шифрование с 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;
    
    // Генерируем AES ключ
    public static SecretKey generateAESKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        keyGenerator.init(KEY_LENGTH);
        return keyGenerator.generateKey();
    }
    
    // Генерируем вектор инициализации (IV)
    public static byte[] generateIV() {
        byte[] iv = new byte[IV_LENGTH];
        new SecureRandom().nextBytes(iv);
        return iv;
    }
    
    // Шифруем данные
    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());
        
        // Объединяем IV и зашифрованные данные
        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);
    }
    
    // Расшифровываем данные
    public static String decryptAES(String ciphertext, SecretKey key) throws Exception {
        byte[] combined = Base64.getDecoder().decode(ciphertext);
        
        // Извлекаем IV
        byte[] iv = new byte[IV_LENGTH];
        System.arraycopy(combined, 0, iv, 0, iv.length);
        
        // Извлекаем зашифрованные данные
        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);
    }
    
    // Демонстрация AES-256
    public static void aesDemo() {
        try {
            System.out.println("=== Демонстрация AES-256 Шифрования ===");
            
            // Генерируем ключ
            SecretKey aesKey = generateAESKey();
            System.out.println("AES-256 ключ сгенерирован");
            System.out.println("Ключ (Base64): " + Base64.getEncoder().encodeToString(aesKey.getEncoded()));
            
            // Тестовые данные
            String plaintext = "Это секретное сообщение, которое будет зашифровано с помощью AES-256.";
            System.out.println("\nОткрытый текст: " + plaintext);
            
            // Шифруем
            byte[] iv = generateIV();
            String ciphertext = encryptAES(plaintext, aesKey, iv);
            System.out.println("\nЗашифровано: " + ciphertext);
            
            // Расшифровываем
            String decryptedText = decryptAES(ciphertext, aesKey);
            System.out.println("\nРасшифровано: " + decryptedText);
            
            // Проверяем
            System.out.println("\nШифрование успешно: " + plaintext.equals(decryptedText));
            
        } catch (Exception e) {
            System.err.println("Ошибка при AES-шифровании: " + e.getMessage());
        }
    }
    
    // Сравнение производительности различных режимов AES
    public static void compareAESModes() {
        try {
            System.out.println("\n=== Сравнение производительности режимов AES ===");
            
            String[] modes = {"AES/ECB/PKCS5Padding", "AES/CBC/PKCS5Padding", 
                              "AES/GCM/NoPadding", "AES/CFB/PKCS5Padding"};
            
            SecretKey key = generateAESKey();
            String testData = "Данные для теста производительности разных режимов 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: Ошибка - %s%n", mode, e.getMessage());
                }
            }
            
        } catch (Exception e) {
            System.err.println("Ошибка при сравнении производительности: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        aesDemo();
        compareAESModes();
    }
}

2. Асимметричная криптография с 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";
    
    // RSA Schlüsselpaar generieren
    public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
        keyGen.initialize(KEY_SIZE);
        return keyGen.generateKeyPair();
    }
    
    // Mit Public Key verschlüsseln
    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);
    }
    
    // Mit Private Key entschlüsseln
    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);
    }
    
    // Digitale Signatur erstellen
    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);
    }
    
    // Digitale Signatur verifizieren
    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);
    }
    
    // RSA Demo
    public static void rsaDemo() {
        try {
            System.out.println("=== RSA Verschlüsselung Demo ===");
            
            // Schlüsselpaar generieren
            KeyPair keyPair = generateRSAKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            
            System.out.println("RSA-2048 Schlüsselpaar generiert");
            System.out.println("Public Key: " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
            System.out.println("Private Key: " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
            
            // Testdaten
            String plaintext = "Diese Nachricht wird mit RSA-2048 verschlüsselt und digital signiert.";
            System.out.println("\nKlartext: " + plaintext);
            
            // Verschlüsseln mit Public Key
            String ciphertext = encryptRSA(plaintext, publicKey);
            System.out.println("\nVerschlüsselt (Public Key): " + ciphertext);
            
            // Entschlüsseln mit Private Key
            String decryptedText = decryptRSA(ciphertext, privateKey);
            System.out.println("Entschlüsselt (Private Key): " + decryptedText);
            
            // Digitale Signatur erstellen
            String signature = signData(plaintext, privateKey);
            System.out.println("\nDigitale Signatur: " + signature);
            
            // Signatur verifizieren
            boolean isValid = verifySignature(plaintext, signature, publicKey);
            System.out.println("Signatur gültig: " + isValid);
            
            // Manipulierte Signatur testen
            String manipulatedData = plaintext + " (manipuliert)";
            boolean isManipulatedValid = verifySignature(manipulatedData, signature, publicKey);
            System.out.println("Manipulierte Signatur gültig: " + isManipulatedValid);
            
        } catch (Exception e) {
            System.err.println("Fehler bei RSA-Verschlüsselung: " + e.getMessage());
        }
    }
    
    // Hybrid-Verschlüsselung (RSA + AES)
    public static void hybridEncryptionDemo() {
        try {
            System.out.println("\n=== Hybrid-Verschlüsselung Demo (RSA + AES) ===");
            
            // Schlüssel generieren
            KeyPair rsaKeyPair = generateRSAKeyPair();
            SecretKey aesKey = SymmetricEncryptionDemo.generateAESKey();
            
            // Große Datenmenge
            String largeData = "Dies ist eine große Datenmenge, die mit AES verschlüsselt und der AES-Schlüssel dann mit RSA verschlüsselt wird. ".repeat(50);
            System.out.println("Originaldaten Größe: " + largeData.length() + " Zeichen");
            
            // Schritt 1: Daten mit AES verschlüsseln
            byte[] iv = SymmetricEncryptionDemo.generateIV();
            String encryptedData = SymmetricEncryptionDemo.encryptAES(largeData, aesKey, iv);
            System.out.println("Mit AES verschlüsselt: " + encryptedData.length() + " Zeichen");
            
            // Schritt 2: AES-Schlüssel mit RSA verschlüsseln
            String encryptedKey = encryptRSA(Base64.getEncoder().encodeToString(aesKey.getEncoded()), rsaKeyPair.getPublic());
            System.out.println("AES-Schlüssel mit RSA verschlüsselt");
            
            // Schritt 3: Entschlüsselung (umgekehrt)
            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("Hybrid-Verschlüsselung erfolgreich: " + largeData.equals(decryptedData));
            
        } catch (Exception e) {
            System.err.println("Fehler bei Hybrid-Verschlüsselung: " + e.getMessage());
        }
    }
    
    // RSA Key-Size Vergleich
    public static void compareKeySizes() {
        try {
            System.out.println("\n=== RSA Key-Size Vergleich ===");
            
            int[] keySizes = {1024, 2048, 4096};
            String testData = "Testdaten für Key-Size Vergleich";
            
            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: Verschlüsselung %dms, Entschlüsselung %dms%n",
                                     keySize, encryptTime / 1_000_000, decryptTime / 1_000_000);
                    
                } catch (Exception e) {
                    System.out.printf("RSA-%d: Fehler - %s%n", keySize, e.getMessage());
                }
            }
            
        } catch (Exception e) {
            System.err.println("Fehler bei Key-Size Vergleich: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        rsaDemo();
        hybridEncryptionDemo();
        compareKeySizes();
    }
}

Асимметричная криптография использует пару ключей: открытый и приватный. Любой может зашифровать данные открытым ключом, но расшифровать их сможет только тот, кто располагает соответствующим приватным ключом.

Приведенный выше код демонстрирует три ключевых сценария:

Базовое шифрование. Данные шифруются открытым ключом и расшифровываются приватным ключом. Это гарантирует, что только обладатель приватного ключа сможет прочитать сообщение.

Цифровая подпись. Данные подписываются приватным ключом, а проверяются открытым ключом. Подпись доказывает, что данные исходят именно от владельца приватного ключа и не были изменены.

Гибридное шифрование. Больших объемов данных зашифровывают с помощью быстрого симметричного алгоритма (AES), а ключ AES защищают асимметричным шифрованием (RSA). Это сочетает преимущества обоих подходов: производительность AES и удобство распределения ключей RSA.

Параметры KEY_SIZE = 2048 обеспечивают надежную защиту для большинства современных сценариев. Ключи размером 4096 бит предпочтительны для особо чувствительных данных, но заметно медленнее.

3. Хеш-функции и защита паролей

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";
    
    // SHA-256 Hash berechnen
    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);
    }
    
    // SHA-3 Hash berechnen
    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);
    }
    
    // Salted Hash (mit zufälligem Salt)
    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);
    }
    
    // Salt generieren
    public static byte[] generateSalt() {
        byte[] salt = new byte[16];
        new SecureRandom().nextBytes(salt);
        return salt;
    }
    
    // PBKDF2 für Passwort-Hashing
    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);
    }
    
    // Passwort mit PBKDF2 verifizieren
    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 für Message Authentication
    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);
    }
    
    // Hash-Kollisionstest
    public static void testHashCollisions() {
        try {
            System.out.println("=== Hash-Kollisionstest ===");
            
            String[] testStrings = {
                "password123",
                "password124",  // Sehr ähnlich
                "Password123",  // Groß/Kleinschreibung
                "pass word123", // Leerzeichen
                "pa$$word123"   // Sonderzeichen
            };
            
            System.out.println("SHA-256 Hashes:");
            for (String test : testStrings) {
                String hash = sha256(test);
                System.out.printf("%-15s: %s%n", test, hash);
            }
            
            System.out.println("\nSHA-3 Hashes:");
            for (String test : testStrings) {
                String hash = sha3_256(test);
                System.out.printf("%-15s: %s%n", test, hash);
            }
            
        } catch (Exception e) {
            System.err.println("Fehler bei Kollisionstest: " + e.getMessage());
        }
    }
    
    // Passwort-Sicherheits-Demo
    public static void passwordSecurityDemo() {
        try {
            System.out.println("\n=== Passwort-Sicherheits-Demo ===");
            
            String password = "MySecurePassword123!";
            
            // 1. Einfacher Hash (unsicher)
            String simpleHash = sha256(password);
            System.out.println("Einfacher SHA-256: " + simpleHash);
            
            // 2. Salted Hash
            byte[] salt = generateSalt();
            String saltedHashStr = saltedHash(password, salt);
            System.out.println("Salted Hash: " + saltedHashStr);
            System.out.println("Salt: " + Base64.getEncoder().encodeToString(salt));
            
            // 3. PBKDF2 (empfohlen)
            int iterations = 10000;
            int keyLength = 256;
            String pbkdf2HashStr = pbkdf2Hash(password, salt, iterations, keyLength);
            System.out.println("PBKDF2 Hash: " + pbkdf2HashStr);
            System.out.println("Iterationen: " + iterations);
            
            // 4. Verifikation
            boolean isValid = verifyPassword(password, pbkdf2HashStr, salt, iterations);
            System.out.println("Passwort gültig: " + isValid);
            
            // 5. Timing Attack Protection
            System.out.println("\nTiming Attack Protection Test:");
            testTimingAttackProtection();
            
        } catch (Exception e) {
            System.err.println("Fehler bei Passwort-Sicherheit: " + e.getMessage());
        }
    }
    
    // Timing Attack Protection Demo
    public static void testTimingAttackProtection() {
        try {
            String correctPassword = "correctPassword123";
            String wrongPassword = "wrongPassword456";
            
            byte[] salt = generateSalt();
            String storedHash = pbkdf2Hash(correctPassword, salt, 10000, 256);
            
            // Timing Tests
            long[] correctTimes = new long[10];
            long[] wrongTimes = new long[10];
            
            for (int i = 0; i < 10; i++) {
                // Korrektes Passwort
                long start = System.nanoTime();
                verifyPassword(correctPassword, storedHash, salt, 10000);
                correctTimes[i] = System.nanoTime() - start;
                
                // Falsches Passwort
                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("Korrektes Passwort: %dms (Durchschnitt)%n", avgCorrect / 1_000_000);
            System.out.printf("Falsches Passwort: %dms (Durchschnitt)%n", avgWrong / 1_000_000);
            System.out.printf("Timing-Differenz: %.2f%%n", 
                             Math.abs(avgCorrect - avgWrong) * 100.0 / Math.max(avgCorrect, avgWrong));
            
        } catch (Exception e) {
            System.err.println("Fehler bei Timing Attack Test: " + e.getMessage());
        }
    }
    
    // HMAC Demo
    public static void hmacDemo() {
        try {
            System.out.println("\n=== HMAC Demo ===");
            
            String message = "Dies ist eine vertrauliche Nachricht";
            String secretKey = "geheimerSchlüssel123";
            
            // HMAC berechnen
            String hmac = hmacSHA256(message, secretKey);
            System.out.println("Nachricht: " + message);
            System.out.println("HMAC: " + hmac);
            
            // HMAC mit falschem Schlüssel
            String wrongKey = "falscherSchlüssel456";
            String wrongHmac = hmacSHA256(message, wrongKey);
            System.out.println("HMAC (falscher Key): " + wrongHmac);
            
            // Verifikation
            boolean isValid = hmac.equals(hmacSHA256(message, secretKey));
            boolean isInvalid = !wrongHmac.equals(hmacSHA256(message, secretKey));
            
            System.out.println("HMAC gültig: " + isValid);
            System.out.println("Falscher HMAC erkannt: " + isInvalid);
            
        } catch (Exception e) {
            System.err.println("Fehler bei HMAC Demo: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        testHashCollisions();
        passwordSecurityDemo();
        hmacDemo();
    }
}

Хеш-функции играют решающую роль в криптографии и защите данных. Они преобразуют входные данные произвольного размера в фиксированный размер вывода. Хорошая хеш-функция обладает свойством однонаправленности, то есть практически невозможно восстановить исходные данные по хешу.

SHA-256 и SHA-3 являются стандартными алгоритмами хеширования. SHA-256 широко используется в криптографических приложениях, а SHA-3 предлагает дополнительный уровень безопасности благодаря другой внутренней структуре.

Когда речь идет о паролях, простого хеширования недостаточно. Нужно использовать соль (salt) - случайные данные, которые добавляются к паролю перед хешированием. Это предотвращает использование предвычисленных таблиц хешей (радужных таблиц) для взлома паролей.

PBKDF2 применяет функцию хеширования многократно. Большое количество итераций (обычно 10000 и более) замедляет процесс вычисления хеша, что делает перебор паролей намного дороже с точки зрения вычислительных ресурсов.

byte[] salt = generateSalt();
String hash = pbkdf2Hash(password, salt, 10000, 256);
boolean valid = verifyPassword(password, hash, salt, 10000);

HMAC (Hash-based Message Authentication Code) используется для проверки подлинности сообщений. Он гарантирует, что сообщение не было изменено и что оно действительно отправлено тем, кто обладает секретным ключом.

Защита от timing attacks важна при сравнении хешей паролей. Если функция сравнения прерывается при первом несовпадении, время выполнения может раскрыть информацию о корректности отдельных символов пароля. Функция verifyPassword использует constant-time сравнение благодаря String.equals(), хотя для критичных операций лучше использовать специализированные методы.

4. SSL/TLS и сертификаты

import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.*;
import java.util.Base64;

public class SSLTLSDemo {
    
    // Создание SSL-контекста
    public static SSLContext createSSLContext() throws Exception {
        // Trust Manager для сервер-сертификатов
        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) {
                // В production: валидация сертификата
                    System.out.println("Сервер-сертификат валиден");
                }
            }
        };
        
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagers, new SecureRandom());
        
        return sslContext;
    }
    
    // HTTPS-запрос с SSL
    public static void makeHTTPSRequest(String urlString) {
        try {
            System.out.println("=== HTTPS Request Demo ===");
            
            URL url = new URL(urlString);
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            
            // Устанавливаем SSL-контекст
            SSLContext sslContext = createSSLContext();
            connection.setSSLSocketFactory(sslContext.getSocketFactory());
            
            // Hostname Verifier для демо
            connection.setHostnameVerifier((hostname, session) -> {
                System.out.println("Hostname: " + hostname);
                return true; // В production: правильная проверка hostname
            });
            
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);
            
            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);
            
            // Информация о сертификате
            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("Сервер-сертификат:");
                System.out.println("  Subject: " + cert.getSubjectDN());
                System.out.println("  Issuer: " + cert.getIssuerDN());
                System.out.println("  Valid from: " + cert.getNotBefore());
                System.out.println("  Valid until: " + cert.getNotAfter());
                System.out.println("  Serial Number: " + cert.getSerialNumber());
            }
            
            // TLS-версия
            System.out.println("TLS Protocol: " + connection.getSSLSession().getProtocol());
            System.out.println("Cipher Suite: " + connection.getSSLSession().getCipherSuite());
            
            // Чтение ответа
            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("Response (первые 200 символов):");
                System.out.println(response.substring(0, Math.min(200, response.length())));
            }
            
        } catch (Exception e) {
            System.err.println("Ошибка при HTTPS запросе: " + e.getMessage());
        }
    }
    
    // Генерация самоподписанного сертификата
    public static void generateSelfSignedCertificate() {
        try {
            System.out.println("\n=== Демо самоподписанного сертификата ===");
            
            // Генерируем KeyPair
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048);
            KeyPair keyPair = keyGen.generateKeyPair();
            
            // Создание сертификата (упрощённо)
            // На практике используются BouncyCastle или подобные библиотеки
            System.out.println("KeyPair сгенерирован для самоподписанного сертификата");
            System.out.println("Public Key: " + Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
            
        } catch (Exception e) {
            System.err.println("Ошибка при создании сертификата: " + e.getMessage());
        }
    }
    
    // Список поддерживаемых cipher suites
    public static void listCipherSuites() {
        try {
            System.out.println("\n=== Поддерживаемые Cipher Suites ===");
            
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, null, null);
            
            String[] cipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
            
            System.out.println("Количество поддерживаемых Cipher Suites: " + cipherSuites.length);
            System.out.println("\nРекомендуемые Cipher Suites:");
            
            for (String suite : cipherSuites) {
                // Показываем только современные, безопасные cipher suites
                if (suite.contains("TLS_ECDHE") && suite.contains("GCM")) {
                    System.out.println("  " + suite);
                }
            }
            
        } catch (Exception e) {
            System.err.println("Ошибка при получении списка Cipher Suites: " + e.getMessage());
        }
    }
    
    // Анализ SSL-handshake
    public static void analyzeSSLHandshake() {
        try {
            System.out.println("\n=== Анализ SSL Handshake ===");
            
            SSLContext sslContext = createSSLContext();
            
            // Пользовательские SSL параметры
            SSLParameters sslParams = new SSLParameters();
            sslParams.setEndpointIdentificationAlgorithm("HTTPS");
            sslParams.setProtocols(new String[]{"TLSv1.3", "TLSv1.2"});
            
            // Безопасные cipher suites
            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("Конфигурация SSL:");
            System.out.println("  Протоколы: " + String.join(", ", sslParams.getProtocols()));
            System.out.println("  Cipher Suites: " + sslParams.getCipherSuites().length + " сконфигурировано");
            System.out.println("  Hostname Verification: " + sslParams.getEndpointIdentificationAlgorithm());
            
        } catch (Exception e) {
            System.err.println("Ошибка при анализе SSL: " + e.getMessage());
        }
    }
    
    // Валидация цепочки сертификатов
    public static void validateCertificateChain() {
        try {
            System.out.println("\n=== Certificate Chain Validation ===");
            
            // Пример валидации пути сертификата
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            
            // На практике сертификаты загружаются из файла
            // Здесь показаны только концепции
            
            System.out.println("Концепции валидации цепочки сертификатов:");
            System.out.println("1. Root CA Certificate");
            System.out.println("2. Intermediate CA Certificate(s)");
            System.out.println("3. End Entity Certificate");
            System.out.println("4. Certificate Revocation Check (CRL/OCSP)");
            System.out.println("5. Certificate Transparency Logs");
            
            // Конфигурация Trust Manager
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            
            // KeyStore для доверенных сертификатов
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null); // Пустой TrustStore
            
            // В production: загружаем системный TrustStore или добавляем свои сертификаты
            // trustStore.load(new FileInputStream("truststore.jks"), "password".toCharArray());
            
            tmf.init(trustStore);
            
            TrustManager[] trustManagers = tmf.getTrustManagers();
            System.out.println("Trust Manager сконфигурирован: " + trustManagers.length + " менеджеров");
            
        } catch (Exception e) {
            System.err.println("Ошибка при валидации сертификата: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        // HTTPS Request Demo
        // makeHTTPSRequest("https://www.google.com");
        
        // Демо сертификатов
        generateSelfSignedCertificate();
        listCipherSuites();
        analyzeSSLHandshake();
        validateCertificateChain();
    }
}

Обзор криптографических алгоритмов

Симметричные алгоритмы

АлгоритмДлина ключаРазмер блокаПрименениеБезопасность
AES128/192/256128 битСтандартБезопасен
DES5664УстарелНебезопасен
3DES16864LegacyСлаб
Blowfish32-44864РазличноеБезопасен

Асимметричные алгоритмы

АлгоритмДлина ключаПрименениеБезопасностьПроизводительность
RSA1024-4096Подпись/ШифрованиеБезопасенМедленно
ECC160-521Подпись/ШифрованиеБезопасенБыстро
DSA1024-3072ПодписьБезопасенМедленно
ElGamal1024-4096ШифрованиеБезопасенМедленно

Алгоритмы хеширования

АлгоритмДлина выводаЗащита от коллизийСтатусПрименение
SHA-256256 битБезопаснаРекомендуетсяУниверсальное
SHA-3224-512БезопаснаСовременныйУниверсальное
MD5128 битНарушенаУстарелКонтрольные суммы
bcryptПеременнаяБезопаснаРекомендуетсяПароли

Версии протокола SSL/TLS

ВерсияГодБезопасностьCipher SuitesРекомендация
SSL 2.01995НебезопасенУстарелНе использовать
SSL 3.01996НебезопасенУстарелНе использовать
TLS 1.01999СлабОграниченоНе использовать
TLS 1.12006ОКОграниченоНе использовать
TLS 1.22008БезопасенСовременныеРекомендуется
TLS 1.32018Очень безопасенСовременныеЛучший выбор

Best practices безопасности паролей

Методы хеширования

// ❌ Небезопасно
String hash = md5(password);

// ⚠️ Лучше
String hash = sha256(password);

// ✅ Безопасно
String hash = pbkdf2(password, salt, 100000);

// ✅ Лучший вариант
String hash = bcrypt(password);

Салирование

// Генерируем соль
byte[] salt = generateSalt(); // 16+ байт случайных данных

// Сохраняем соль (она не секретна)
String saltedHash = salt + ":" + hash(password, salt);

Извлечение ключа

// Параметры PBKDF2
int iterations = 100000; // Минимум 100.000
int keyLength = 256;     // 256 бит
int saltLength = 32;     // 32 байта

String derivedKey = pbkdf2(password, salt, iterations, keyLength);

Процесс цифровой подписи

Создание подписи

  1. Вычислить хеш: hash = SHA256(data)
  2. Зашифровать хеш: signature = RSA_encrypt(hash, private_key)
  3. Присоединить подпись: data + signature

Проверка подписи

  1. Извлечь подпись: signature = extract(data_with_signature)
  2. Вычислить хеш: hash = SHA256(data)
  3. Расшифровать подпись: decrypted_hash = RSA_decrypt(signature, public_key)
  4. Сравнить: hash == decrypted_hash ?

Infrastructure открытых ключей (PKI)

Компоненты

  • Root CA: Доверенный корневой центр сертификации
  • Intermediate CA: Промежуточные центры сертификации
  • End Entity: Сертификаты сервера/клиента
  • CRL: Список отзыва сертификатов
  • OCSP: Протокол проверки статуса сертификата в сети

Валидация сертификата

// 1. Проверить цепь сертификатов
// 2. Проверить дату истечения
// 3. Проверить отзыв (CRL/OCSP)
// 4. Проверить имя хоста
// 5. Проверить подпись

Best practices безопасности

Реализация

// ✅ Безопасная конфигурация
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, trustManagers, null);

// ✅ Безопасные Cipher Suites
String[] secureSuites = {
    "TLS_AES_256_GCM_SHA384",
    "TLS_CHACHA20_POLY1305_SHA256",
    "TLS_AES_128_GCM_SHA256"
};

// ✅ Проверка имени хоста
connection.setHostnameVerifier((hostname, session) -> {
    return hostname.equals(session.getPeerHost());
});

Избежание ошибок

// ❌ Неправильно: отключить TrustManager
TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() { /* принимать все сертификаты */ }
};

// ✅ Правильно: собственный TrustManager с валидацией
TrustManager[] secureTrustManagers = new TrustManager[] {
    new X509TrustManager() {
        public void checkServerTrusted(X509Certificate[] chain, String authType) 
                throws CertificateException {
            // Собственная логика валидации
        }
    }
};

Преимущества и недостатки

Преимущества криптографии

  • Конфиденциальность: защита от несанкционированного доступа
  • Целостность: обнаружение манипуляций с данными
  • Аутентичность: проверка личности
  • Неотказуемость: невозможность отрицать действие
  • Соответствие: выполнение требований безопасности

Недостатки

  • Сложность: требуется экспертиза
  • Производительность: ресурсоемкие операции
  • Управление ключами: сложная организация ключей
  • Накладные расходы: дополнительная инфраструктура

Часто встречающиеся вопросы на экзаменах

  1. В чем разница между симметричным и асимметричным шифрованием? Симметричное использует один ключ для обеих сторон, асимметричное использует пары открытый/закрытый ключ.

  2. Почему соленые хеши важны для паролей? Соли предотвращают радужные таблицы и обеспечивают уникальные хеши даже для одинаковых паролей.

  3. Объясните цифровые подписи! Цифровые подписи используют хеширование и асимметричное шифрование для гарантии аутентичности и целостности.

  4. Какова цель SSL/TLS? SSL/TLS защищает интернет-коммуникацию через шифрование и аутентификацию.

Основные источники

  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)

Рекомендуемая литература: Cybersecurity

Keine Bücher für Kategorie "cybersecurity" gefunden.

Другие статьи по Cybersecurity

Cybersecurity и криптография критичны для безопасной разработки ПО. Следующие статьи помогут вам разобраться во всех аспектах информационной безопасности.

Linux безопасность

Назад к блогу
Share:

Похожие статьи