← Kursa Dön
📄 Text · 22 min

C++ ile Kriptoloji: Hash, AES, RSA ve Dijital İmza

Hash, Simetrik (AES), Asimetrik (RSA, EC), Dijital İmza

Kriptoloji — şifre bilimi — günümüz dijital dünyasının temel taşı. HTTPS, WhatsApp mesajları, Bitcoin, parola doğrulaması... hepsi kriptografik algoritmalar sayesinde çalışıyor.

Neden C++? Çünkü kriptografi performans-kritik bir alan. OpenSSL, libsodium, BoringSSL gibi dünyanın en çok kullanılan kripto kütüphaneleri C/C++ ile yazılmış. Donanıma yakınlık, bellek kontrolü ve AES-NI gibi CPU instruction'larına erişim — C++'ı kripto dünyasının vazgeçilmezi yapıyor.


1. Kriptoloji Nedir?

Kriptoloji iki daldan oluşur: kriptografi (şifreleme) ve kriptanaliz (şifre kırma). Biz kriptografiye odaklanacağız.

Kale analojisi: Kriptolojiyi bir kale savunmasına benzetebilirsin. Kapılara kilit takıyorsun (şifreleme), ziyaretçilerin kimliğini doğruluyorsun (authentication), mesajların yolda değiştirilmediğinden emin oluyorsun (integrity).

AmaçAçıklamaÖrnek
Gizlilik (Confidentiality)Sadece yetkili kişiler okusunAES şifreleme
Bütünlük (Integrity)Veri değiştirilmemiş olsunSHA-256 hash
Kimlik Doğrulama (Authentication)Gönderen gerçekten o mu?Dijital imza
İnkar Edilemezlik (Non-repudiation)"Ben göndermedim" diyemesinRSA imza
#include <iostream>
#include <array>

// Güvenli bellek temizleme — optimizer bunu silemez
void secure_zero(void* ptr, size_t size) {
    volatile unsigned char* p = static_cast<volatile unsigned char*>(ptr);
    while (size--) *p++ = 0;
}

int main() {
    std::array<char, 64> secret_key{};
    // ... anahtar kullan ...
    secure_zero(secret_key.data(), secret_key.size());
    std::cout << "Anahtar bellekten guvenle silindi.\n";
    return 0;
}

⚠️ Dikkat: Normal memset ile hassas verileri temizlemeye çalışırsan, derleyici optimizasyonu bu çağrıyı kaldırabilir! Derleyici "bu bellek zaten kullanılmayacak" der ve silme işlemini atlar. volatile pointer veya C11'deki memset_s kullanmak şart.


2. Hash Fonksiyonları — Dijital Parmak İzi

Hash fonksiyonu, herhangi boyuttaki veriyi sabit uzunlukta bir "özet"e (digest) dönüştürür. Bu dönüşüm tek yönlüdür — hash'ten orijinal veriye dönemezsin.

Parmak izi analojisi: Her insanın parmak izi benzersizdir. Parmak izine bakarak kişiyi tanırsın ama parmak izinden insanı yeniden oluşturamazsın. Hash fonksiyonu da verinin "dijital parmak izi"dir.

İyi bir kriptografik hash'in üç özelliği:

  1. Preimage Resistance: Hash'ten orijinali bulmak imkansız

  2. Second Preimage Resistance: Aynı hash'i üreten farklı veri bulmak imkansız

  3. Collision Resistance: Aynı hash'i üreten herhangi iki veri bulmak imkansız

#include <iostream>
#include <string>
#include <functional>

int main() {
    std::string msg1 = "Merhaba Dunya";
    std::string msg2 = "Merhaba Dunya!";  // sadece bir '!' farkı

    std::hash<std::string> hasher;
    std::cout << "Hash1: " << hasher(msg1) << "\n";
    std::cout << "Hash2: " << hasher(msg2) << "\n";
    std::cout << "Tek karakter fark, hash tamamen degisti!\n";
    return 0;
}

3. Basit Hash İmplementasyonları

Eğitim amaçlı hash fonksiyonları yazalım. Gerçek güvenlik için kullanılmamalı, ama mantığı anlamak için harika.

XOR Hash

#include <iostream>
#include <string>
#include <cstdint>
#include <iomanip>

uint32_t xor_hash(const std::string& data) {
    uint32_t hash = 0;
    for (size_t i = 0; i < data.size(); ++i)
        hash ^= static_cast<uint32_t>(data[i]) << ((i % 4) * 8);
    return hash;
}

int main() {
    std::cout << std::hex << std::setfill('0');
    std::cout << "xor_hash(\"hello\"): 0x" << std::setw(8) << xor_hash("hello") << "\n";
    std::cout << "xor_hash(\"world\"): 0x" << std::setw(8) << xor_hash("world") << "\n";
    return 0;
}

DJB2 Hash

Daniel J. Bernstein'ın ünlü hash fonksiyonu. Basit ama şaşırtıcı derecede iyi dağılım sağlar:

#include <iostream>
#include <string>
#include <cstdint>

uint64_t djb2_hash(const std::string& data) {
    uint64_t hash = 5381;
    for (unsigned char c : data)
        hash = ((hash << 5) + hash) + c;  // hash * 33 + c
    return hash;
}

int main() {
    std::cout << std::hex;
    std::cout << "djb2(\"hello\"): 0x" << djb2_hash("hello") << "\n";
    std::cout << "djb2(\"Hello\"): 0x" << djb2_hash("Hello") << "\n";
    std::cout << "djb2(\"hellp\"): 0x" << djb2_hash("hellp") << "\n";
    return 0;
}

💡 İpucu: hash * 33 neden iyi? 33 = 2⁵ + 1, yani bit shift + toplama ile hızlıca hesaplanır. Bernstein bunu deneysel olarak keşfetmiş — bazen en iyi çözümler pratikten gelir.


4. SHA-256 Nasıl Çalışır?

SHA-256, 256-bit hash üreten kriptografik hash fonksiyonu. Bitcoin, TLS, git commit hash'leri — hepsinde kullanılır.

Çalışma Adımları

1. Padding: Mesaj 512-bit bloklara bölünür. Son bloğa 1 biti, sıfırlar ve mesaj uzunluğu eklenir.

2. İlk Değerler: 8 adet 32-bit başlangıç değeri — ilk 8 asal sayının kareköklerinden türetilir.

3. 64 Round Compression: Her blok için karmaşık bit manipülasyonları:

  a b c d e f g h   ← 8 adet 32-bit değişken
  │ │ │ │ │ │ │ │
  ┌─────────────┐
  │  Σ, Ch, Maj │ ← Bit rotation + logic ops
  │  + Kt + Wt  │ ← Round constant + mesaj word
  └─────────────┘
  a'b'c'd'e'f'g'h' ← Yeni değerler (× 64 round)

SHA-256'daki temel bit operasyonları:

#include <iostream>
#include <cstdint>
#include <iomanip>

uint32_t right_rotate(uint32_t value, unsigned int count) {
    return (value >> count) | (value << (32 - count));
}

uint32_t ch(uint32_t e, uint32_t f, uint32_t g) {
    return (e & f) ^ (~e & g);  // e=1 ise f, e=0 ise g
}

uint32_t maj(uint32_t a, uint32_t b, uint32_t c) {
    return (a & b) ^ (a & c) ^ (b & c);  // çoğunluk
}

int main() {
    uint32_t e = 0x510e527f, f = 0x9b05688c, g = 0x1f83d9ab;
    uint32_t a = 0x6a09e667, b = 0xbb67ae85, c = 0x3c6ef372;

    std::cout << std::hex << std::setfill('0');
    std::cout << "Ch(e,f,g)  = 0x" << std::setw(8) << ch(e, f, g) << "\n";
    std::cout << "Maj(a,b,c) = 0x" << std::setw(8) << maj(a, b, c) << "\n";
    return 0;
}

5. Simetrik Şifreleme (AES)

Simetrik şifreleme: aynı anahtar hem şifreleme hem çözme için kullanılır.

Kilit analojisi: Paylaşılan bir kilit gibi düşün. Sen ve arkadaşın aynı anahtarın birer kopyasına sahipsiniz. Mesajı kutuya koyup kilitliyorsun, arkadaşın kendi kopyasıyla açıyor. Sorun: anahtarı güvenle nasıl paylaşacaksın?

AES Nasıl Çalışır?

AES, 128-bit blokları şifreler. Anahtar boyutuna göre round sayısı değişir:

AnahtarRound
AES-12810
AES-19212
AES-25614

Her round'da 4 işlem:

  • SubBytes: Her byte S-Box tablosundan değiştirilir (non-linear karıştırma)

  • ShiftRows: Satırlar sola kaydırılır (1, 2, 3 pozisyon)

  • MixColumns: Sütunlar Galois Field matris çarpımıyla karıştırılır

  • AddRoundKey: Blok, round anahtarıyla XOR'lanır

XOR Cipher

AES'i anlamadan önce temel XOR cipher'la başlayalım. A ⊕ K ⊕ K = A prensibiyle çalışır:

#include <iostream>
#include <string>
#include <vector>
#include <iomanip>

std::vector<uint8_t> xor_cipher(const std::string& text, const std::string& key) {
    std::vector<uint8_t> result(text.size());
    for (size_t i = 0; i < text.size(); ++i)
        result[i] = text[i] ^ key[i % key.size()];
    return result;
}

int main() {
    std::string plaintext = "Merhaba Kriptografi!";
    std::string key = "GizliAnahtar";

    auto encrypted = xor_cipher(plaintext, key);
    std::cout << "Sifreli (hex): ";
    for (uint8_t b : encrypted)
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";

    // Çözme: aynı anahtarla tekrar XOR
    std::string enc_str(encrypted.begin(), encrypted.end());
    auto decrypted = xor_cipher(enc_str, key);
    std::cout << "\nCozulmus: " << std::string(decrypted.begin(), decrypted.end()) << "\n";
    return 0;
}

Caesar Cipher

Tarihin en eski şifreleme yöntemi. Julius Caesar harfleri kaydırarak mesaj göndermiş:

#include <iostream>
#include <string>

std::string caesar_encrypt(const std::string& text, int shift) {
    std::string result;
    for (char c : text) {
        if (c >= 'a' && c <= 'z')
            result += 'a' + (c - 'a' + shift) % 26;
        else if (c >= 'A' && c <= 'Z')
            result += 'A' + (c - 'A' + shift) % 26;
        else result += c;
    }
    return result;
}

int main() {
    std::string msg = "Hello World";
    std::string enc = caesar_encrypt(msg, 3);
    std::string dec = caesar_encrypt(enc, 23);  // 26-3 = 23

    std::cout << "Orijinal: " << msg << "\n";
    std::cout << "Sifreli:  " << enc << "\n";
    std::cout << "Cozulmus: " << dec << "\n";
    return 0;
}

⚠️ Dikkat: XOR cipher ve Caesar cipher kriptografik olarak güvenli değildir! XOR'da anahtar tekrar ederse frekans analiziyle kırılır. Caesar'da sadece 25 olasılık var — saniyede çözülür. Bunları sadece kavram için kullanıyoruz.

AES Çalışma Modları

ModAçıklamaGüvenlik
ECBHer blok bağımsız şifrelenir❌ Patern sızdırır — asla kullanma
CBCHer blok önceki şifreli blokla XOR'lanır✅ Dosya şifreleme
GCMCounter mod + authentication tag✅✅ TLS, API — modern standart

ECB neden tehlikeli? Aynı plaintext bloğu her zaman aynı ciphertext üretir. Bir resmi ECB ile şifrelersen ana hatları görünür — ünlü "ECB Penguin" örneği.

OpenSSL ile AES-GCM (Referans)

Bu kod OpenSSL gerektirir, Wandbox'ta çalışmaz ama gerçek projelerde böyle kullanılır:

// Derleme: g++ -std=c++20 -o aes_demo aes_demo.cpp -lssl -lcrypto

#include <openssl/evp.h>
#include <openssl/rand.h>
#include <vector>
#include <cstdint>

bool aes_256_gcm_encrypt(
    const std::vector<uint8_t>& plaintext,
    const std::vector<uint8_t>& key,      // 32 byte
    const std::vector<uint8_t>& iv,       // 12 byte
    std::vector<uint8_t>& ciphertext,
    std::vector<uint8_t>& tag)            // 16 byte
{
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr);
    EVP_EncryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data());

    ciphertext.resize(plaintext.size());
    int len = 0;
    EVP_EncryptUpdate(ctx, ciphertext.data(), &len,
                      plaintext.data(), plaintext.size());
    EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len);

    tag.resize(16);
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag.data());
    EVP_CIPHER_CTX_free(ctx);
    return true;
}

💡 İpucu: OpenSSL ile çalışırken EVP_CIPHER_CTX_free() çağırmayı unutma. Modern C++ yazıyorsan std::unique_ptr ile RAII sağla: auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);


6. Asimetrik Şifreleme (RSA)

Simetrik şifrelemede büyük sorun: anahtarı güvenle nasıl ileteceksin? Asimetrik şifreleme bunu çözüyor — iki farklı anahtar: public (açık) ve private (gizli).

Posta kutusu analojisi: Herkes kutunun ağzından mektup atabilir (public key ile şifreleme), ama sadece anahtarı olan kişi kutuyu açabilir (private key ile çözme).

RSA Matematiği

RSA, büyük asal çarpanlarına ayırmanın zorluğuna dayanır:

  1. İki asal seç: p, q

  2. n = p × q, φ(n) = (p-1)(q-1)

  3. e seç: gcd(e, φ(n)) = 1 (genelde 65537)

  4. d bul: d × e ≡ 1 (mod φ(n))

  5. Public key: (n, e) — Private key: (n, d)

Küçük sayılarla örnek:

p=61, q=53 → n=3233, φ(n)=3120
e=17, d=2753  (17×2753 = 46801 = 15×3120+1 ✓)

Şifreleme: 65^17  mod 3233 = 2790
Çözme:     2790^2753 mod 3233 = 65 ✓

C++ ile Basit RSA

#include <iostream>
#include <cstdint>
#include <string>

uint64_t mod_pow(uint64_t base, uint64_t exp, uint64_t mod) {
    uint64_t result = 1;
    base %= mod;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (__uint128_t)result * base % mod;
        exp /= 2;
        base = (__uint128_t)base * base % mod;
    }
    return result;
}

int64_t mod_inverse(int64_t e, int64_t phi) {
    int64_t t = 0, new_t = 1, r = phi, new_r = e;
    while (new_r != 0) {
        int64_t q = r / new_r;
        t -= q * new_t; std::swap(t, new_t);
        r -= q * new_r; std::swap(r, new_r);
    }
    return t < 0 ? t + phi : t;
}

int main() {
    uint64_t p = 61, q = 53;
    uint64_t n = p * q;                    // 3233
    uint64_t phi = (p - 1) * (q - 1);     // 3120
    uint64_t e = 17;
    uint64_t d = mod_inverse(e, phi);      // 2753

    std::cout << "Public:  (n=" << n << ", e=" << e << ")\n";
    std::cout << "Private: (n=" << n << ", d=" << d << ")\n\n";

    std::string message = "HI";
    for (char c : message) {
        uint64_t m = static_cast<uint64_t>(c);
        uint64_t cipher = mod_pow(m, e, n);
        uint64_t plain  = mod_pow(cipher, d, n);
        std::cout << "'" << c << "' (" << m << ") -> sifreli: "
                  << cipher << " -> cozulmus: " << plain << "\n";
    }
    return 0;
}

Neden Büyük Asal Sayılar?

n BoyutuKırılma Süresi
512 bitSaatler (güvensiz)
1024 bitYıllar (güvensiz kabul)
2048 bitEvrenin ömründen uzun
4096 bit...

2048-bit RSA'yı çarpanlarına ayırmak bilinen algoritmalarla astronomik zaman alır. Kuantum bilgisayarlar Shor's algoritmasıyla bunu kırabilir — bu yüzden post-quantum cryptography araştırmaları devam ediyor.


7. Elliptic Curve Cryptography (ECC)

RSA güvenli ama büyük anahtarlar gerektirir. ECC, çok daha küçük anahtarlarla aynı güvenliği sağlar — mobil, IoT ve TLS için büyük avantaj.

ECC, eliptik eğrilerde "noktasal çarpma" tersinin hesaplanmasının zorluğuna dayanır. Bir noktayı n kez kendine eklemek kolay, sonuçtan n'i bulmak imkansıza yakın.

Anahtar Boyutu Karşılaştırması

GüvenlikRSAECCOran
80-bit1024 bit160 bit6.4×
112-bit2048 bit224 bit9.1×
128-bit3072 bit256 bit12×
192-bit7680 bit384 bit20×
256-bit15360 bit521 bit29.5×

256-bit güvenlik için RSA 15360 bit isterken ECC'ye 521 bit yeterli — 30 kat fark!

Yaygın ECC eğrileri:

  • P-256 (secp256r1): NIST standardı, TLS'de yaygın

  • Curve25519: Modern, hızlı (Daniel J. Bernstein)

  • secp256k1: Bitcoin'in eğrisi

#include <iostream>
#include <string>

int main() {
    struct Row { std::string sec; int rsa; int ecc; };
    Row table[] = {
        {"80-bit",  1024, 160}, {"128-bit", 3072, 256},
        {"192-bit", 7680, 384}, {"256-bit", 15360, 521},
    };
    std::cout << "Guvenlik  | RSA       | ECC     | Oran\n";
    std::cout << "----------|-----------|---------|------\n";
    for (auto& r : table)
        std::cout << r.sec << "  | " << r.rsa << " bit | "
                  << r.ecc << " bit | " << (double)r.rsa / r.ecc << "x\n";
    return 0;
}

8. Dijital İmza

Dijital imza = hash + private key. Kimlik doğrulama ve inkar edilemezlik sağlar.

Noter analojisi: Belgenin özetini (hash) alırsın, kendi mühürünle (private key) damgalarsın. Herkes kamuya açık mühür kalıbınla (public key) doğrulayabilir, ama kimse mühürü taklit edemez.

İmzalama:  Belge → [SHA-256] → Hash → [Private Key] → İmza
Doğrulama: Belge → [SHA-256] → Hash₁
           İmza  → [Public Key] → Hash₂
           Hash₁ == Hash₂ ? ✅ Geçerli : ❌ Sahte
#include <iostream>
#include <string>
#include <cstdint>

uint64_t simple_hash(const std::string& data) {
    uint64_t hash = 5381;
    for (char c : data) hash = ((hash << 5) + hash) + c;
    return hash;
}

uint64_t mod_pow(uint64_t base, uint64_t exp, uint64_t mod) {
    uint64_t result = 1; base %= mod;
    while (exp > 0) {
        if (exp % 2 == 1) result = (__uint128_t)result * base % mod;
        exp /= 2; base = (__uint128_t)base * base % mod;
    }
    return result;
}

int main() {
    uint64_t n = 3233, e = 17, d = 2753;
    std::string msg = "Hello";

    // İmzala: hash'in private key ile şifrelenmesi
    uint64_t hash = simple_hash(msg) % n;
    uint64_t signature = mod_pow(hash, d, n);

    // Doğrula: imzanın public key ile açılması
    uint64_t recovered = mod_pow(signature, e, n);
    bool valid = (recovered == simple_hash(msg) % n);

    std::cout << "Mesaj: " << msg << " | Imza: " << signature << "\n";
    std::cout << "Dogrulama: " << (valid ? "GECERLI" : "SAHTE") << "\n";

    // Değiştirilmiş mesaj
    bool tampered = (recovered == simple_hash("Hellp") % n);
    std::cout << "Degistirilmis: " << (tampered ? "GECERLI" : "SAHTE") << "\n";
    return 0;
}

9. Kriptografik Güvenli Rastgele Sayılar

Kriptografide rastgelelik hayati. Anahtar üretimi, nonce, salt — hepsi kriptografik güvenli rastgele sayı ister.

rand() veya std::mt19937 kriptografi için güvensizdir — deterministiktir, seed'i bilirsen çıktıyı tahmin edersin.

KaynakGüvenli?Kullanım
rand()Oyunlar
std::mt19937İstatistik
std::random_device✅ GenellikleGenel güvenli rastgele
/dev/urandom✅ (Linux)Anahtar üretimi
RAND_bytes() (OpenSSL)Kripto kütüphaneleri
#include <iostream>
#include <random>
#include <array>
#include <iomanip>
#include <fstream>

int main() {
    // Yöntem 1: std::random_device
    std::random_device rd;
    std::cout << "random_device: ";
    for (int i = 0; i < 4; ++i) std::cout << rd() << " ";
    std::cout << "\nEntropi: " << rd.entropy() << " bit\n";

    // Yöntem 2: /dev/urandom (Linux)
    std::ifstream urandom("/dev/urandom", std::ios::binary);
    if (urandom) {
        std::array<uint8_t, 32> key{};
        urandom.read(reinterpret_cast<char*>(key.data()), key.size());
        std::cout << "256-bit anahtar: ";
        for (uint8_t b : key)
            std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b;
        std::cout << "\n";
    }
    return 0;
}

⚠️ Dikkat: std::random_device bazı platformlarda (eski MinGW) deterministik çalışabilir! Kritik uygulamalarda platforma özgü API kullan.


10. Base64 Encode/Decode

Base64, binary veriyi ASCII karakterlerle temsil eder. Şifreleme değil, kodlama. Ama kriptoda şifreli veri, anahtar ve sertifikalar metin ortamlarında (JSON, e-posta) taşındığı için çok kullanılır.

Her 3 byte → 4 karakter. Alfabe: A-Z, a-z, 0-9, +, /. Eksik byte'lar = ile padding yapılır.

#include <iostream>
#include <string>
#include <cstdint>

const std::string B64 =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

std::string base64_encode(const std::string& input) {
    std::string result;
    int val = 0, bits = 0;
    for (uint8_t c : input) {
        val = (val << 8) + c; bits += 8;
        while (bits >= 6) { bits -= 6; result += B64[(val >> bits) & 0x3F]; }
    }
    if (bits > 0) result += B64[(val << (6 - bits)) & 0x3F];
    while (result.size() % 4) result += '=';
    return result;
}

std::string base64_decode(const std::string& input) {
    std::string result;
    int val = 0, bits = 0;
    for (char c : input) {
        if (c == '=') break;
        auto pos = B64.find(c);
        if (pos == std::string::npos) continue;
        val = (val << 6) + static_cast<int>(pos); bits += 6;
        if (bits >= 8) { bits -= 8; result += static_cast<char>((val >> bits) & 0xFF); }
    }
    return result;
}

int main() {
    std::string original = "Merhaba Dunya!";
    std::string encoded = base64_encode(original);
    std::string decoded = base64_decode(encoded);
    std::cout << "Orijinal: " << original << "\n";
    std::cout << "Base64:   " << encoded << "\n";
    std::cout << "Decoded:  " << decoded << "\n";
    return 0;
}

11. SSL/TLS ve HTTPS Handshake

TLS, simetrik + asimetrik şifrelemenin birlikte kullanıldığı hibrit sistem. Tarayıcıdaki 🔒 simgesi TLS aktif demek.

TLS 1.3 Handshake

Client (Browser)                    Server (Web)
     │                                   │
     │── 1. ClientHello ────────────────▶│  (cipher listesi, EC params)
     │                                   │
     │◀── 2. ServerHello ───────────────│  (seçilen cipher, sertifika)
     │                                   │
     │── 3. Key Exchange (ECDHE) ──────▶│  (sertifika doğrulama)
     │                                   │
     │◀══ 4. AES-256-GCM şifreli ═════▶│  (session key türetildi)

Neden bu kadar karmaşık?

  1. Asimetrik şifreleme yavaş — her paketi RSA ile şifreleyemezsin

  2. Simetrik şifreleme hızlı ama anahtar paylaşımı lazım

  3. Çözüm: asimetriki sadece anahtar değişimi için kullan, sonra simetriğe geç

#include <iostream>
#include <string>

struct TLSSimulation {
    void client_hello() {
        std::cout << "[Client -> Server] ClientHello\n"
                  << "  Cipher: TLS_AES_256_GCM_SHA384\n\n";
    }
    void server_hello() {
        std::cout << "[Server -> Client] ServerHello\n"
                  << "  Sertifika: CN=example.com (EC P-256)\n\n";
    }
    void key_exchange() {
        std::cout << "[ECDHE] Ortak session key turetildi\n\n";
    }
    void encrypted() {
        std::cout << "[Sifreli] GET /index.html -> AES-256-GCM\n"
                  << "[Sifreli] 200 OK <- AES-256-GCM\n";
    }
};

int main() {
    TLSSimulation tls;
    tls.client_hello();
    tls.server_hello();
    tls.key_exchange();
    tls.encrypted();
    return 0;
}

12. Modern C++ ve Kripto

C++20 ile kriptografik hesaplamalar derleme zamanında bile yapılabilir hale geliyor.

Constexpr Hash

String karşılaştırmalarını switch-case'e çevirebilirsin:

#include <iostream>
#include <string_view>
#include <cstdint>

constexpr uint64_t ct_hash(std::string_view str) {
    uint64_t hash = 5381;
    for (char c : str) hash = ((hash << 5) + hash) + static_cast<uint64_t>(c);
    return hash;
}

void process(std::string_view cmd) {
    switch (ct_hash(cmd)) {
        case ct_hash("encrypt"): std::cout << "Sifreleme modu\n"; break;
        case ct_hash("decrypt"): std::cout << "Cozme modu\n"; break;
        case ct_hash("hash"):    std::cout << "Hash modu\n"; break;
        default: std::cout << "Bilinmeyen komut\n"; break;
    }
}

int main() {
    constexpr auto h = ct_hash("hello");
    static_assert(h == ct_hash("hello"));  // derleme zamanı garanti
    std::cout << "ct_hash(\"hello\"): " << h << "\n";
    process("encrypt");
    process("hash");
    return 0;
}

Compile-Time String Obfuscation

String literal'ler binary'de açık görünür (strings komutuyla okunabilir). Compile-time XOR ile gizlenebilir:

#include <iostream>
#include <array>
#include <cstdint>
#include <string>

template<size_t N>
struct ObfuscatedString {
    std::array<char, N> data{};
    static constexpr uint8_t KEY = 0xAB;

    constexpr ObfuscatedString(const char (&str)[N]) {
        for (size_t i = 0; i < N; ++i) data[i] = str[i] ^ KEY;
    }

    std::string decode() const {
        std::string result(N - 1, '\0');
        for (size_t i = 0; i < N - 1; ++i) result[i] = data[i] ^ KEY;
        return result;
    }
};

int main() {
    constexpr ObfuscatedString secret("SecretAPIKey123");
    std::cout << "Cozulmus: " << secret.decode() << "\n";
    return 0;
}

💡 İpucu: Compile-time obfuscation gerçek güvenlik sağlamaz — tersine mühendislik ile kırılır. Ama strings komutuna karşı korur. API key'leri kodda tutmak yerine environment variable veya vault servisleri kullan.


13. Pratik: Password Hasher + Verifier

Güvenli parola saklama üç prensibe dayanır:

  1. Asla düz metin parola saklama — hash'le

  2. Salt kullan — rainbow table saldırılarını engelle

  3. Yavaş hash kullan — brute force'u zorlaştır

Salt: Her kullanıcıya özel rastgele değer. Aynı parola farklı salt'larla farklı hash üretir — saldırgan önceden hesaplanmış tablolar kullanamaz.

#include <iostream>
#include <string>
#include <random>
#include <sstream>
#include <iomanip>
#include <cstdint>

uint64_t fnv_djb2_hash(const std::string& input) {
    uint64_t h1 = 0xcbf29ce484222325ULL;
    uint64_t h2 = 5381;
    for (char c : input) {
        h1 ^= static_cast<uint64_t>(c); h1 *= 0x100000001b3ULL;
        h2 = ((h2 << 5) + h2) + static_cast<uint64_t>(c);
    }
    return h1 ^ (h2 << 32) ^ (h2 >> 32);
}

std::string generate_salt(size_t len = 16) {
    std::random_device rd;
    std::mt19937_64 gen(rd());
    std::uniform_int_distribution<int> dist(33, 126);
    std::string salt;
    for (size_t i = 0; i < len; ++i) salt += static_cast<char>(dist(gen));
    return salt;
}

std::string to_hex(uint64_t v) {
    std::ostringstream oss;
    oss << std::hex << std::setfill('0') << std::setw(16) << v;
    return oss.str();
}

struct StoredPassword { std::string salt, hash_hex; };

StoredPassword hash_password(const std::string& password) {
    std::string salt = generate_salt();
    uint64_t hash = fnv_djb2_hash(salt + password + salt);
    // Key stretching: 10000 round
    for (int i = 0; i < 10000; ++i)
        hash = fnv_djb2_hash(to_hex(hash) + salt);
    return {salt, to_hex(hash)};
}

bool verify_password(const std::string& password, const StoredPassword& stored) {
    uint64_t hash = fnv_djb2_hash(stored.salt + password + stored.salt);
    for (int i = 0; i < 10000; ++i)
        hash = fnv_djb2_hash(to_hex(hash) + stored.salt);
    return to_hex(hash) == stored.hash_hex;
}

int main() {
    std::string password = "MySecureP@ss123";
    auto stored = hash_password(password);
    std::cout << "Salt: " << stored.salt << "\nHash: " << stored.hash_hex << "\n\n";

    std::cout << "Dogru parola: "
              << (verify_password("MySecureP@ss123", stored) ? "BASARILI" : "BASARISIZ") << "\n";
    std::cout << "Yanlis parola: "
              << (verify_password("WrongPass", stored) ? "BASARILI" : "BASARISIZ") << "\n";

    auto stored2 = hash_password(password);
    std::cout << "\nAyni parola, farkli salt:\n"
              << "Hash1: " << stored.hash_hex << "\n"
              << "Hash2: " << stored2.hash_hex << "\n"
              << "Farkli mi? " << (stored.hash_hex != stored2.hash_hex ? "EVET" : "HAYIR") << "\n";
    return 0;
}

⚠️ Dikkat: Bu hash fonksiyonu eğitim amaçlıdır! Gerçek uygulamalarda bcrypt, scrypt veya Argon2 kullan. Bu algoritmalar kasıtlı olarak yavaş ve bellek-yoğundur. C++'ta libsodium'un crypto_pwhash fonksiyonu Argon2 desteği sunar.


Ne Zaman Ne Kullanılır?

İhtiyaçAlgoritmaC++ Kütüphane
Parola hashlemeArgon2, bcryptlibsodium
Veri bütünlüğüSHA-256, SHA-3OpenSSL, Crypto++
Simetrik şifrelemeAES-256-GCMOpenSSL, libsodium
Anahtar değişimiECDHE (Curve25519)libsodium
Dijital imzaEd25519, ECDSAlibsodium, OpenSSL
Rastgele sayıCSPRNGstd::random_device

Özet

  • Hash fonksiyonları verinin dijital parmak izini oluşturur — tek yönlüdür, SHA-256 en yaygınıdır. Küçücük bir değişiklik hash'i tamamen değiştirir (avalanche effect).

  • Simetrik şifreleme (AES) aynı anahtarla şifreler/çözer — hızlıdır ama anahtar paylaşım sorunu vardır. Modern standart: AES-256-GCM (hem şifreleme hem bütünlük kontrolü).

  • Asimetrik şifreleme (RSA, ECC) public/private key çifti kullanır. ECC, RSA'ya göre 10-30× daha küçük anahtarlarla aynı güvenliği sağlar — TLS 1.3 ve Bitcoin'de tercih edilir.

  • Dijital imza = hash + private key. Kimlik doğrulama ve inkar edilemezlik sağlar. Sertifikalardan git commit'lere kadar her yerde.

  • Kriptografik rastgele sayılar için std::random_device veya /dev/urandom kullan — rand() ve mt19937 kriptografi için kesinlikle güvensiz.

  • TLS/HTTPS hibrit yaklaşım kullanır: asimetrik ile anahtar değişimi, sonra simetrik (AES-GCM) ile hızlı iletişim. Parola saklarken salt + key stretching + yavaş hash (Argon2) kullanmak şart.