Основы кибербезопасности: криптография, шифрование, хеш-функции и цифровые подписи
Это подробное введение в основы кибербезопасности, включающее криптографию, шифрование, хеш-функции и цифровые подписи с практическими примерами.
Суть вопроса
Криптография защищает данные через шифрование, хеш-функции обеспечивают целостность, а цифровые подписи гарантируют аутентичность. Современная безопасность строится на математических алгоритмах.
Краткое описание
Криптография — это наука об алгоритмах шифрования и расшифровки информации для защиты от несанкционированного доступа.
Основные направления:
Симметричное шифрование
- Концепция: один и тот же ключ для шифрования и расшифровки
- Алгоритмы: 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-безопасности и защиты данных
Основные компоненты
- Шифрование: защита конфиденциальности
- Хеш-функции: обеспечение целостности
- Цифровые подписи: гарантия аутентичности
- Public Key Infrastructure: управление ключами
- SSL/TLS: защищённое сетевое соединение
- Криптографические протоколы: безопасная передача данных
- Управление ключами: генерация, хранение, распределение
- 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();
}
}
Обзор криптографических алгоритмов
Симметричные алгоритмы
| Алгоритм | Длина ключа | Размер блока | Применение | Безопасность |
|---|---|---|---|---|
| AES | 128/192/256 | 128 бит | Стандарт | Безопасен |
| DES | 56 | 64 | Устарел | Небезопасен |
| 3DES | 168 | 64 | Legacy | Слаб |
| Blowfish | 32-448 | 64 | Различное | Безопасен |
Асимметричные алгоритмы
| Алгоритм | Длина ключа | Применение | Безопасность | Производительность |
|---|---|---|---|---|
| RSA | 1024-4096 | Подпись/Шифрование | Безопасен | Медленно |
| ECC | 160-521 | Подпись/Шифрование | Безопасен | Быстро |
| DSA | 1024-3072 | Подпись | Безопасен | Медленно |
| ElGamal | 1024-4096 | Шифрование | Безопасен | Медленно |
Алгоритмы хеширования
| Алгоритм | Длина вывода | Защита от коллизий | Статус | Применение |
|---|---|---|---|---|
| SHA-256 | 256 бит | Безопасна | Рекомендуется | Универсальное |
| SHA-3 | 224-512 | Безопасна | Современный | Универсальное |
| MD5 | 128 бит | Нарушена | Устарел | Контрольные суммы |
| bcrypt | Переменная | Безопасна | Рекомендуется | Пароли |
Версии протокола SSL/TLS
| Версия | Год | Безопасность | Cipher Suites | Рекомендация |
|---|---|---|---|---|
| SSL 2.0 | 1995 | Небезопасен | Устарел | Не использовать |
| SSL 3.0 | 1996 | Небезопасен | Устарел | Не использовать |
| TLS 1.0 | 1999 | Слаб | Ограничено | Не использовать |
| TLS 1.1 | 2006 | ОК | Ограничено | Не использовать |
| TLS 1.2 | 2008 | Безопасен | Современные | Рекомендуется |
| TLS 1.3 | 2018 | Очень безопасен | Современные | Лучший выбор |
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);
Процесс цифровой подписи
Создание подписи
- Вычислить хеш:
hash = SHA256(data) - Зашифровать хеш:
signature = RSA_encrypt(hash, private_key) - Присоединить подпись:
data + signature
Проверка подписи
- Извлечь подпись:
signature = extract(data_with_signature) - Вычислить хеш:
hash = SHA256(data) - Расшифровать подпись:
decrypted_hash = RSA_decrypt(signature, public_key) - Сравнить:
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 {
// Собственная логика валидации
}
}
};
Преимущества и недостатки
Преимущества криптографии
- Конфиденциальность: защита от несанкционированного доступа
- Целостность: обнаружение манипуляций с данными
- Аутентичность: проверка личности
- Неотказуемость: невозможность отрицать действие
- Соответствие: выполнение требований безопасности
Недостатки
- Сложность: требуется экспертиза
- Производительность: ресурсоемкие операции
- Управление ключами: сложная организация ключей
- Накладные расходы: дополнительная инфраструктура
Часто встречающиеся вопросы на экзаменах
-
В чем разница между симметричным и асимметричным шифрованием? Симметричное использует один ключ для обеих сторон, асимметричное использует пары открытый/закрытый ключ.
-
Почему соленые хеши важны для паролей? Соли предотвращают радужные таблицы и обеспечивают уникальные хеши даже для одинаковых паролей.
-
Объясните цифровые подписи! Цифровые подписи используют хеширование и асимметричное шифрование для гарантии аутентичности и целостности.
-
Какова цель SSL/TLS? SSL/TLS защищает интернет-коммуникацию через шифрование и аутентификацию.
Основные источники
- 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)
Рекомендуемая литература: Cybersecurity
Keine Bücher für Kategorie "cybersecurity" gefunden.
Другие статьи по Cybersecurity
Cybersecurity и криптография критичны для безопасной разработки ПО. Следующие статьи помогут вам разобраться во всех аспектах информационной безопасности.
Linux безопасность
- Cybersecurity Linux безопасность рекомендации книг - Специальные концепции безопасности для Linux-систем



