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 okusun | AES şifreleme |
| Bütünlük (Integrity) | Veri değiştirilmemiş olsun | SHA-256 hash |
| Kimlik Doğrulama (Authentication) | Gönderen gerçekten o mu? | Dijital imza |
| İnkar Edilemezlik (Non-repudiation) | "Ben göndermedim" diyemesin | RSA 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
memsetile 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.volatilepointer veya C11'dekimemset_skullanmak ş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:
Preimage Resistance: Hash'ten orijinali bulmak imkansız
Second Preimage Resistance: Aynı hash'i üreten farklı veri bulmak imkansız
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 * 33neden 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:
| Anahtar | Round |
|---|---|
| AES-128 | 10 |
| AES-192 | 12 |
| AES-256 | 14 |
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ı
| Mod | Açıklama | Güvenlik |
|---|---|---|
| ECB | Her blok bağımsız şifrelenir | ❌ Patern sızdırır — asla kullanma |
| CBC | Her blok önceki şifreli blokla XOR'lanır | ✅ Dosya şifreleme |
| GCM | Counter 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ıyorsanstd::unique_ptrile 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:
İki asal seç:
p,qn = p × q,φ(n) = (p-1)(q-1)eseç:gcd(e, φ(n)) = 1(genelde 65537)dbul:d × e ≡ 1 (mod φ(n))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 Boyutu | Kırılma Süresi |
|---|---|
| 512 bit | Saatler (güvensiz) |
| 1024 bit | Yıllar (güvensiz kabul) |
| 2048 bit | Evrenin ö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üvenlik | RSA | ECC | Oran |
|---|---|---|---|
| 80-bit | 1024 bit | 160 bit | 6.4× |
| 112-bit | 2048 bit | 224 bit | 9.1× |
| 128-bit | 3072 bit | 256 bit | 12× |
| 192-bit | 7680 bit | 384 bit | 20× |
| 256-bit | 15360 bit | 521 bit | 29.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.
| Kaynak | Güvenli? | Kullanım |
|---|---|---|
rand() | ❌ | Oyunlar |
std::mt19937 | ❌ | İstatistik |
std::random_device | ✅ Genellikle | Genel 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_devicebazı 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?
Asimetrik şifreleme yavaş — her paketi RSA ile şifreleyemezsin
Simetrik şifreleme hızlı ama anahtar paylaşımı lazım
Çö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
stringskomutuna 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:
Asla düz metin parola saklama — hash'le
Salt kullan — rainbow table saldırılarını engelle
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'uncrypto_pwhashfonksiyonu Argon2 desteği sunar.
Ne Zaman Ne Kullanılır?
| İhtiyaç | Algoritma | C++ Kütüphane |
|---|---|---|
| Parola hashleme | Argon2, bcrypt | libsodium |
| Veri bütünlüğü | SHA-256, SHA-3 | OpenSSL, Crypto++ |
| Simetrik şifreleme | AES-256-GCM | OpenSSL, libsodium |
| Anahtar değişimi | ECDHE (Curve25519) | libsodium |
| Dijital imza | Ed25519, ECDSA | libsodium, OpenSSL |
| Rastgele sayı | CSPRNG | std::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_deviceveya/dev/urandomkullan —rand()vemt19937kriptografi 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.
AI Asistan
Sorularını yanıtlamaya hazır