The book of Magnus

IT-заметки и знания

Основы пентеста. Методичка по криптографии с нуля

Tags = [ Crypto, Pentest_base ]

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

Основные задачи

  1. Конфиденциальность — защита от несанкционированного доступа
  2. Целостность — обеспечение неизменности данных
  3. Аутентификация — подтверждение подлинности отправителя
  4. Неотказуемость — невозможность отрицания авторства

История

  • 1900 до н.э. — египетские иероглифы
  • 100-44 до н.э. — Шифр Цезаря
  • 1467 — Шифр Альберти
  • 1976 — Публикация Диффи-Хеллмана
  • 1977 — RSA
  • 2001 — AES становится стандартом

Основные понятия

Терминология

  • Открытый текст (Plaintext) — исходное сообщение
  • Шифртекст (Ciphertext) — зашифрованное сообщение
  • Ключ (Key) — секретная информация
  • Шифрование (Encryption) — преобразование открытого текста
  • Дешифрование (Decryption) — обратное преобразование

Принцип Керкгоффса

Безопасность должна основываться на секретности ключа, а не алгоритма

Виды шифрования

Криптография
├── Симметричная (один ключ)
│   ├── Потоковые шифры
│   └── Блочные шифры
├── Асимметричная (пара ключей)
│   ├── RSA
│   ├── ECC
│   └── ElGamal
└── Гибридная
    └── TLS, PGP, SSH

Классическая криптография

1. Шифр Цезаря

Простейший шифр подстановки со сдвигом.

Принцип:

HELLO → (сдвиг 3) → KHOOR

Математика:

Шифрование: C = (P + k) mod 26
Дешифрование: P = (C - k) mod 26

Код на Python:

def caesar_encrypt(text, shift):
    result = ""
    for char in text:
        if char.isalpha():
            base = ord('A') if char.isupper() else ord('a')
            shifted = (ord(char) - base + shift) % 26
            result += chr(base + shifted)
        else:
            result += char
    return result

def caesar_decrypt(text, shift):
    return caesar_encrypt(text, -shift)

# Пример
plaintext = "HELLO WORLD"
encrypted = caesar_encrypt(plaintext, 3)
print(f"Зашифровано: {encrypted}")  # KHOOR ZRUOG
print(f"Расшифровано: {caesar_decrypt(encrypted, 3)}")

Взлом (brute force):

def caesar_crack(ciphertext):
    for shift in range(26):
        print(f"Ключ {shift:2d}: {caesar_decrypt(ciphertext, shift)}")

caesar_crack("KHOOR ZRUOG")

2. Шифр Виженера

Многоалфавитный шифр.

Принцип:

Текст: HELLO WORLD
Ключ:  KEYKE YKEYK
Результ: RIJVS UYVJN

Реализация:

def vigenere_encrypt(plaintext, key):
    result = ""
    key_index = 0
    key = key.upper()
    
    for char in plaintext.upper():
        if char.isalpha():
            shift = ord(key[key_index % len(key)]) - ord('A')
            encrypted = chr((ord(char) - ord('A') + shift) % 26 + ord('A'))
            result += encrypted
            key_index += 1
        else:
            result += char
    return result

def vigenere_decrypt(ciphertext, key):
    result = ""
    key_index = 0
    key = key.upper()
    
    for char in ciphertext.upper():
        if char.isalpha():
            shift = ord(key[key_index % len(key)]) - ord('A')
            decrypted = chr((ord(char) - ord('A') - shift) % 26 + ord('A'))
            result += decrypted
            key_index += 1
        else:
            result += char
    return result

# Пример
text = "HELLO WORLD"
key = "KEY"
encrypted = vigenere_encrypt(text, key)
print(f"Зашифровано: {encrypted}")
print(f"Расшифровано: {vigenere_decrypt(encrypted, key)}")

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

Принцип

Один ключ для шифрования и дешифрования.

AES (Advanced Encryption Standard)

Характеристики:

  • Размер блока: 128 бит
  • Длины ключей: 128, 192, 256 бит
  • Текущий стандарт

Режимы работы:

  1. ECB — НЕ ИСПОЛЬЗОВАТЬ (небезопасен)
  2. CBC — Cipher Block Chaining
  3. CTR — Counter mode
  4. GCM — с аутентификацией (рекомендуется)

Пример AES-GCM:

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64

def aes_encrypt(plaintext, key=None):
    """Шифрование AES-GCM"""
    if key is None:
        key = get_random_bytes(32)  # AES-256
    
    cipher = AES.new(key, AES.MODE_GCM)
    ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode())
    
    return {
        'key': base64.b64encode(key).decode(),
        'nonce': base64.b64encode(cipher.nonce).decode(),
        'tag': base64.b64encode(tag).decode(),
        'ciphertext': base64.b64encode(ciphertext).decode()
    }

def aes_decrypt(enc_dict):
    """Дешифрование AES-GCM"""
    key = base64.b64decode(enc_dict['key'])
    nonce = base64.b64decode(enc_dict['nonce'])
    tag = base64.b64decode(enc_dict['tag'])
    ciphertext = base64.b64decode(enc_dict['ciphertext'])
    
    cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
    plaintext = cipher.decrypt_and_verify(ciphertext, tag)
    
    return plaintext.decode()

# Пример
message = "Секретное сообщение"
encrypted = aes_encrypt(message)
decrypted = aes_decrypt(encrypted)
print(f"Результат: {decrypted}")

ChaCha20-Poly1305

Современная альтернатива AES.

from Crypto.Cipher import ChaCha20_Poly1305

def chacha20_encrypt(plaintext):
    key = get_random_bytes(32)
    cipher = ChaCha20_Poly1305.new(key=key)
    ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode())
    
    return {
        'key': key,
        'nonce': cipher.nonce,
        'tag': tag,
        'ciphertext': ciphertext
    }

def chacha20_decrypt(enc_dict):
    cipher = ChaCha20_Poly1305.new(
        key=enc_dict['key'],
        nonce=enc_dict['nonce']
    )
    plaintext = cipher.decrypt_and_verify(
        enc_dict['ciphertext'],
        enc_dict['tag']
    )
    return plaintext.decode()

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

Принцип

Два ключа: публичный (для шифрования) и приватный (для дешифрования).

RSA

Математическая основа: Факторизация больших чисел.

Простой пример:

def simple_rsa():
    # Маленькие числа для демонстрации
    p, q = 61, 53
    n = p * q  # 3233
    phi = (p-1) * (q-1)  # 3120
    e = 17  # Публичная экспонента
    d = pow(e, -1, phi)  # 2753 - приватная экспонента
    
    print(f"Публичный ключ: (n={n}, e={e})")
    print(f"Приватный ключ: (n={n}, d={d})")
    
    # Шифрование
    message = 123
    ciphertext = pow(message, e, n)
    print(f"Зашифровано: {ciphertext}")
    
    # Дешифрование
    decrypted = pow(ciphertext, d, n)
    print(f"Расшифровано: {decrypted}")

simple_rsa()

Практическое использование:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

# Генерация ключей
key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()

# Шифрование
def rsa_encrypt(message, public_key_pem):
    public_key = RSA.import_key(public_key_pem)
    cipher = PKCS1_OAEP.new(public_key)
    return cipher.encrypt(message)

# Дешифрование
def rsa_decrypt(ciphertext, private_key_pem):
    private_key = RSA.import_key(private_key_pem)
    cipher = PKCS1_OAEP.new(private_key)
    return cipher.decrypt(ciphertext)

# Пример
message = b"Secret message"
encrypted = rsa_encrypt(message, public_key)
decrypted = rsa_decrypt(encrypted, private_key)
print(f"Расшифровано: {decrypted.decode()}")

Эллиптические кривые (ECC)

Более эффективная альтернатива RSA.

Сравнение безопасности:

RSAECCСоотношение
1024 бит160 бит6.4:1
2048 бит224 бит9.1:1
3072 бит256 бит12:1

ECDH (обмен ключами):

from Crypto.PublicKey import ECC

# Алиса
alice_key = ECC.generate(curve='P-256')
alice_public = alice_key.public_key()

# Боб
bob_key = ECC.generate(curve='P-256')
bob_public = bob_key.public_key()

# Алиса вычисляет общий секрет
alice_shared = alice_key.d * bob_public.pointQ

# Боб вычисляет общий секрет
bob_shared = bob_key.d * alice_public.pointQ

# Секреты одинаковы!
print(alice_shared.x == bob_shared.x)  # True

Хеш-функции

Определение

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

Свойства

  1. Детерминированность — один вход = один хеш
  2. Быстрое вычисление
  3. Эффект лавины — малое изменение → большое изменение хеша
  4. Необратимость
  5. Стойкость к коллизиям

Примеры

import hashlib

message = b"Hello World"

# MD5 (устарел, НЕ ИСПОЛЬЗОВАТЬ для безопасности)
md5 = hashlib.md5(message).hexdigest()
print(f"MD5: {md5}")

# SHA-256 (рекомендуется)
sha256 = hashlib.sha256(message).hexdigest()
print(f"SHA-256: {sha256}")

# SHA-512
sha512 = hashlib.sha512(message).hexdigest()
print(f"SHA-512: {sha512}")

# SHA-3
sha3 = hashlib.sha3_256(message).hexdigest()
print(f"SHA3-256: {sha3}")

# BLAKE2
blake2 = hashlib.blake2b(message).hexdigest()
print(f"BLAKE2b: {blake2}")

Проверка целостности файла

def file_hash(filename, algorithm='sha256'):
    h = hashlib.new(algorithm)
    with open(filename, 'rb') as f:
        while chunk := f.read(8192):
            h.update(chunk)
    return h.hexdigest()

# Использование
# hash_value = file_hash('document.pdf')
# print(f"SHA-256: {hash_value}")

HMAC (аутентификация сообщений)

import hmac

key = b"secret_key"
message = b"Important message"

# Создание HMAC
h = hmac.new(key, message, hashlib.sha256)
mac = h.hexdigest()

# Проверка
h_verify = hmac.new(key, message, hashlib.sha256)
if hmac.compare_digest(h_verify.hexdigest(), mac):
    print("✓ HMAC верен")

Password Hashing

НИКОГДА не используйте обычные хеш-функции для паролей!

Правильно — Argon2:

from argon2 import PasswordHasher

ph = PasswordHasher()

# Хеширование
password = "my_secure_password"
hashed = ph.hash(password)
print(f"Хеш: {hashed}")

# Проверка
try:
    ph.verify(hashed, password)
    print("✓ Пароль верен")
except:
    print("✗ Пароль неверен")

Также хорошо — bcrypt:

import bcrypt

password = b"my_password"

# Хеширование
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))

# Проверка
if bcrypt.checkpw(password, hashed):
    print("✓ Пароль верен")

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

Принцип

Создание:
Message → [Hash] → [Sign with Private Key] → Signature

Проверка:
Message → [Hash] → Digest₁
Signature → [Verify with Public Key] → Digest₂
Digest₁ == Digest₂ ?

RSA подпись

from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256

# Генерация ключей
key = RSA.generate(2048)
public_key = key.publickey()

# Создание подписи
message = b"Contract: Pay $1000"
h = SHA256.new(message)
signature = pkcs1_15.new(key).sign(h)

# Проверка
h = SHA256.new(message)
try:
    pkcs1_15.new(public_key).verify(h, signature)
    print("✓ Подпись верна")
except:
    print("✗ Подпись неверна")

Ed25519 (современная)

from Crypto.PublicKey import ECC
from Crypto.Signature import eddsa

# Генерация ключей
key = ECC.generate(curve='Ed25519')
public_key = key.public_key()

# Подпись
message = b"Message to sign"
signer = eddsa.new(key, 'rfc8032')
signature = signer.sign(message)

# Проверка
verifier = eddsa.new(public_key, 'rfc8032')
try:
    verifier.verify(message, signature)
    print("✓ Подпись верна")
except:
    print("✗ Подпись неверна")

Сертификаты и PKI

X.509 сертификат

Стандартный формат цифрового сертификата.

Создание самоподписанного сертификата

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from datetime import datetime, timedelta

# Генерация ключа
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)

# Информация о субъекте
subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Example Inc"),
    x509.NameAttribute(NameOID.COMMON_NAME, "example.com"),
])

# Создание сертификата
cert = x509.CertificateBuilder().subject_name(
    subject
).issuer_name(
    issuer
).public_key(
    private_key.public_key()
).serial_number(
    x509.random_serial_number()
).not_valid_before(
    datetime.utcnow()
).not_valid_after(
    datetime.utcnow() + timedelta(days=365)
).add_extension(
    x509.SubjectAlternativeName([
        x509.DNSName("example.com"),
        x509.DNSName("www.example.com"),
    ]),
    critical=False
).sign(private_key, hashes.SHA256())

# Сохранение
with open("cert.pem", "wb") as f:
    f.write(cert.public_bytes(serialization.Encoding.PEM))

with open("key.pem", "wb") as f:
    f.write(private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption()
    ))

Практическое применение

1. Шифрование файлов

from Crypto.Protocol.KDF import scrypt
import os

class FileEncryptor:
    def __init__(self, password):
        self.salt = os.urandom(32)
        self.key = scrypt(password, self.salt, 32, N=2**14, r=8, p=1)
    
    def encrypt_file(self, input_file, output_file):
        # Чтение файла
        with open(input_file, 'rb') as f:
            plaintext = f.read()
        
        # Шифрование
        cipher = AES.new(self.key, AES.MODE_GCM)
        ciphertext, tag = cipher.encrypt_and_digest(plaintext)
        
        # Сохранение
        with open(output_file, 'wb') as f:
            f.write(self.salt)
            f.write(cipher.nonce)
            f.write(tag)
            f.write(ciphertext)
    
    def decrypt_file(self, input_file, output_file, password):
        with open(input_file, 'rb') as f:
            salt = f.read(32)
            nonce = f.read(16)
            tag = f.read(16)
            ciphertext = f.read()
        
        key = scrypt(password, salt, 32, N=2**14, r=8, p=1)
        cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
        
        try:
            plaintext = cipher.decrypt_and_verify(ciphertext, tag)
            with open(output_file, 'wb') as f:
                f.write(plaintext)
            print("✓ Файл расшифрован")
        except:
            print("✗ Неверный пароль")

2. Генератор паролей

import secrets
import string

def generate_password(length=16, use_symbols=True):
    charset = string.ascii_letters + string.digits
    if use_symbols:
        charset += string.punctuation
    
    return ''.join(secrets.choice(charset) for _ in range(length))

def generate_passphrase(words=5):
    wordlist = ['correct', 'horse', 'battery', 'staple', 'mountain']
    return '-'.join(secrets.choice(wordlist) for _ in range(words))

# Примеры
print(f"Пароль: {generate_password(20)}")
print(f"Фраза: {generate_passphrase(5)}")

3. Двухфакторная аутентификация (TOTP)

import pyotp

# Генерация секрета
secret = pyotp.random_base32()
print(f"Секрет: {secret}")

# Создание TOTP
totp = pyotp.TOTP(secret)

# Получение текущего кода
current_code = totp.now()
print(f"Текущий код: {current_code}")

# Проверка кода
if totp.verify(current_code):
    print("✓ Код верен")

Криптоанализ

Типы атак

1. Brute Force

Перебор всех возможных ключей.

# Время взлома
def time_to_crack(key_bits, ops_per_sec=1e12):
    combinations = 2 ** key_bits
    seconds = combinations / ops_per_sec
    years = seconds / (365.25 * 24 * 3600)
    
    print(f"Ключ {key_bits} бит: {years:.2e} лет")

time_to_crack(56)   # DES - ~20 часов
time_to_crack(128)  # AES-128 - миллиарды лет
time_to_crack(256)  # AES-256 - практически невозможно

2. Частотный анализ

Для взлома подстановочных шифров.

from collections import Counter

def frequency_analysis(text):
    clean = ''.join(c for c in text.upper() if c.isalpha())
    freq = Counter(clean)
    
    for letter, count in freq.most_common(10):
        print(f"{letter}: {count}")

3. Timing Attack

import hmac

# ❌ УЯЗВИМО
def bad_compare(a, b):
    if len(a) != len(b):
        return False
    for x, y in zip(a, b):
        if x != y:
            return False  # Выход при первом несовпадении
    return True

# ✅ БЕЗОПАСНО (constant-time)
def safe_compare(a, b):
    return hmac.compare_digest(a, b)

Best Practices

✅ Делайте

  1. Используйте проверенные библиотеки

    • PyCryptodome, cryptography
    • НЕ пишите свои алгоритмы
  2. Современные алгоритмы

    • AES-256, ChaCha20
    • SHA-256, SHA-3, BLAKE2
    • Ed25519, RSA-2048+
  3. AEAD режимы

    • AES-GCM вместо AES-CBC
    • ChaCha20-Poly1305
  4. Криптографическая случайность

    import secrets
    key = secrets.token_bytes(32)  # ✅
    
    import random
    key = random.randbytes(32)  # ❌
    
  5. Argon2/bcrypt для паролей

    from argon2 import PasswordHasher
    ph = PasswordHasher()
    hashed = ph.hash(password)  # ✅
    
    import hashlib
    hashed = hashlib.sha256(password).hexdigest()  # ❌
    

❌ Не делайте

  1. НЕ используйте MD5, SHA-1, DES, 3DES
  2. НЕ используйте ECB режим
  3. НЕ храните ключи в коде
  4. НЕ используйте обычные хеши для паролей
  5. НЕ изобретайте свою криптографию

Полезные ресурсы

Библиотеки

  • PyCryptodome — https://pycryptodome.readthedocs.io/
  • cryptography — https://cryptography.io/
  • PyNaCl — https://pynacl.readthedocs.io/
  • argon2-cffi — для паролей

Обучение

  • Cryptopals — https://cryptopals.com/
  • Crypto101 — https://www.crypto101.io/
  • Coursera Cryptography — онлайн курс
  • Khan Academy — основы

Стандарты

  • NIST — https://csrc.nist.gov/
  • OWASP — криптографические рекомендации
  • RFC — протоколы

Инструменты

  • OpenSSL — Swiss Army knife
  • GnuPG — PGP реализация
  • Hashcat — password recovery
  • John the Ripper — password cracker

Квантовая криптография

Угроза квантовых компьютеров

Алгоритм Шора может взломать:

  • ❌ RSA
  • ❌ ECC
  • ❌ Диффи-Хеллман

Относительно безопасны:

  • ✅ AES (удвоить длину ключа)
  • ✅ SHA-2/SHA-3

Post-Quantum алгоритмы (NIST 2024)

  1. CRYSTALS-Kyber — обмен ключами
  2. CRYSTALS-Dilithium — подписи
  3. FALCON — подписи
  4. SPHINCS+ — подписи

QKD (Квантовое распределение ключей)

BB84 протокол — физически безопасный обмен ключами.

Принцип:

  • Использует квантовые свойства фотонов
  • Любой перехват обнаруживается
  • Теоретически абсолютно безопасен

Заключение

Ключевые выводы

  1. ✅ Используйте проверенные алгоритмы и библиотеки
  2. ✅ Следуйте best practices
  3. ✅ Обновляйте знания
  4. ✅ Тестируйте безопасность
  5. ✅ Готовьтесь к квантовой эре

Помните

"Шифрование работает. Правильно реализованные криптосистемы — это одна из немногих вещей, на которые вы можете положиться."
— Эдвард Сноуден