.NET Üzerinde Kriptografi
Bugünlerde bilgisayar dünyasında en çok konuşulan konulardan biri kriptografi,
yani şifreleme. İki tarafın kulak misafiri kaygısı olmaksızın haberleşmelerini
sağlayan bu teknoloji, özellikle son birkaç yılda İnternet''in yaygınlaşmasıyla
önem kazandı. .NET platformu üzerinde gelistirdiğiniz uygulamaları nasıl daha güvenli
hale getirebileceğimize bu yazımızla kısa bir başlangıç yapıyoruz.
İyi bir kriptografik sistem, bilgilerinizin İnternet ya da başka her hangi bir ortam
üzerinden iletilirken başkaları tarafından görülse bile anlaşılmamasını sağlar.
Şekil 1''de görüldügü gibi, böyle bir sistem dört bileşenden oluşur:
Şekil 1 Kriptoğrafik Sistem

1. Gönderen ve alan dışında kimsenin görmesi istenmeyen bir mesaj.
2. Sadece mesajı gönderen ve alanda bulunan, baska kimsenin bilmediği bir
anahtar.
3. Anahtarı kullanarak mesajı şifreleyecek ya da deşifreleyecek bir algoritma.
4. İçinden geçen mesajların isteyen herkes tarafından görülebileceği, güvensiz
bir ortam -mesela İnternet.
Bu modelin dayandığı tek varsayım, kulak misafirinin anahtar olmadan şifrelenmiş
mesajdan orijinali elde etmesinin çok zor oluşudur. Durum böyle olunca izinsiz dinleyicinin
yapabileceği tek şey, anahtar olabilecek bütün olasılıkları şifrelenmiş ileti üzerinde
teker teker denemek oluyor. Tabi, mesajı gönderen ve alan, tahmin edilmesi zor ve
uzun bir anahtar seçerek bu metodu rahatlıkla etkisiz hale getirebilirler.
Günümüzde kullanılan kriptografik algoritmalar iki ana gruba ayrılmakta: simetrik
ve asimetrik. Bu iki grup arasindaki fark, simetrik algoritmaların hem şifrelemek
hem de deşifrelemek icin aynı anahtarı kullanmalarına karşın, asimetrik algoritmaların
iki farklı anahtar kullanmaları. Gönderen mesajı iki anahtardan herhangi
biriyle şifreleyebilir. Ama alan, gönderen hangi anahtarları kullanırsa kullansın,
mesajı çözmek için diğer anahtarı kullanmak zorundadır.
Microsoft .NET Platformu beraberinde dört simetrik kriptografi algoritması ile geliyor.
Butun bu algoritmalar System.Security.Cryptography aduzayındaki sınıflarca sağlaniyor.
Sözkonusu algoritmaların özelliklerini Şekil 2''de özetledik. Bu yazıdaki program
örneğinde simetrik kriptografinin en eski standartlarından biri olan DES''i kullanacağız.
Ama örnek program .NET platformu ile gelen her simetrik algoritma için geçerli.
Şekil 2: .NET Platformu ile gelen simetrik algoritmalar
| Algoritma | Sağlayan .NET Sınıfı | Blok Uzunluğu (bit) | Anahtar Uzunluğu (bit) |
| DES | DESCryptoServiceProvider | 64 | 64 |
| RC2 | RC2CryptoServiceProvider | 64 | 40 - 128 (8''in katı olmalı) |
| Rijndael | RijndaelManaged | 128, 192 veya 256 | 128, 192 veya 256 |
| TripleDES | TripleDESCryptoServiceProvider | 64 | 128 veya 192 |
Hemen her simetrik algoritma mesajı blok blok şifreler. Farzedelim, gönderenin mesajı "Saat 10''da buluşalım". Bu mesaj 20 bayt''tan oluşuyor. Eger DES''i kullanırsak, 8 bayt''tan oluşan 3 bloğa ihtiyacımız olacak:
| Blok Numarası | Blok İçeriği |
| 1 | Saat 10'' |
| 2 | da buluş |
| 3 | alım |
Son blok 4 bayt''tan oluşuyor. Bu durum hakkında ne yapacağız? İki seçeneğimiz var:
· Son bloga 4 sıfır içeren bayt ekleyebiliriz (00 00 00 00).
· Son bloğa her biri eklediğimiz bayt sayısını gösteren baytlar ekleyebiliriz. (04 04 04 04).
Yukarıda belirttiğimiz yöntemlerden ilki .NET platformunda PaddingMode.Zeros, ikincisi de PaddingMode.PKCS7 değerlerine karşılık geliyor. Örnek programda PKCS7''yi tercih ettik, çünkü bu yöntem mesajı kırmak isteyenlere daha az ipucu vererek işlerini zorlaştıracak.
Anahtar olarak 8 bayt seçmek durumundayız. Bu durumda ilk akla gelen rasgele bir parola seçip bu parolanın ilk 8 karakterini bayta çevirmek. Bu yöntemle 8 baytlık anahtarı en verimli şekilde kullanmıyoruz, çünkü böyle yaparak şifreyi kırmak isteyen birinin denemek zorunda olduğu olası anahtarları kısıtlayıp işini kolaylaştırmış oluyoruz. Yine de, şimdilik bununla idare edelim.
Bir mesajı şifrelemek ya da deşifrelemek bir anlamda onu bir başka şekle dönüştürmek olduğu için ilk yapmamız gereken, sectiğimiz kriptografi sınıfından ( DESCryptoServiceProvider) bir ICryptoTransform arayüzü istemek. Mesajı şifrelemek için CreateEncryptor, çözmek için ise CreateDecryptor metodlarını kullanarak bu arayüzü elde ediyoruz.
ICryptoTransform arayüzünü elde ettikten sonra yapmamız gereken bir tek şey kalıyor, o da TransformBlock ya da TransformFinalBlock metodlarını kullanarak mesajı şifrelemek ya da çözmek. Bu arayüzün sunduğu iki yararlı özellik daha var:
· CanReuseTransform: Eğer bu özellik doğru ( true) ise, ICryptoTransform arayüzünü bir sonraki mesajlar için de kullanabiliriz. Aksi taktirde dönüştürmek istediğimiz her mesaj için yeni bir arayüz edinmek zorundayız. .NET platformu ile gelen dört simetrik algoritmanın hepsi yeniden kullanılabilir özellikte.
· CanTransformMultipleBlocks: Eger bu özellik doğru ( true) ise, mesajı TransformBlock metodunu bir döngü içinde her blok icin çağırmak yerine bütün mesajı bir defada şifreleyebilir ya da çözebiliriz.
Bu kısa baslangıçtan sonra ilk programımızı yazmaya hazırız.
System ve System.Security.Cryptography aduzaylarının yanısıra System.Text aduzayını mesaj ve anahtardaki karakterleri stringden bayta ya da bayttan stringe çevirmek için kullanıyoruz.
|
using System; using System.Text; using System.Security.Cryptography; |
WriteHex metodunu herhangi bir bayt dizinini onaltılık düzende yazmak için kullanıyoruz.
|
static void WriteHex(byte[] array) { for (int i = 0; i < array.Length; i++) Console.Write(array[i].ToString("x")); } |
.NET platformu string ve bayt dizinleri arasinda kolayca dönüşüm yapabilmek için çesitli Encoding sınıfları sunuyor. Bu örnekte UTF-8 dönüşümünü kullanıyoruz.
|
static void Main(string[] args) { try { // string <-> byte[] dönüşümu icin UTF8 kullanalim Encoding ue = new UTF8Encoding(); |
Algoritma olarak DES''i kullanıyor ve daha önce de gördüğümüz gibi son bloğu PKCS7 yöntemi ile uygun uzunluğa çeviriyoruz.
|
// Algoritma olarak DES''i kullanalım SymmetricAlgorithm ea = new DESCryptoServiceProvider(); // Kolaylık açısından ECB ve PKCS7''yi tercih edelim ea.Mode = CipherMode.ECB; ea.Padding = PaddingMode.PKCS7; |
BlockSize ve KeySize özellikleri anahtar ve blok uzunluklarını bit cinsinden belirttiği icin bayta çevirmemiz gerekiyor.
|
// Her algoritma ancak belli anahtar uzunluklarini kabul eder. // DES: 64 bit, TripleDES: 128 ya da 192 bit ... // Anahtar ve blok uzunluklarini bitten bayta cevirelim int blockSize = ea.BlockSize / 8; int keySize = ea.KeySize / 8; // Once anahtar dizinini kuralim byte[] key = new byte[keySize]; |
Örnek programda kullanıcıdan önce parolayı daha sonra da mesajı girmesini istiyor ve her iki stringi de UTF-8 dönüşümü ile bayt dizinine çeviriyoruz.
|
// Sonra kullanıcıdan parolayı alıp anahtar dizinine aktaralım Console.Write("Anahtar: "); try { ue.GetBytes(Console.ReadLine()).CopyTo(key, 0); } catch (ArgumentException) { Console.WriteLine("Sectiginiz anahtar bu algoritma icin cok uzun."); return; } // Anahtarı algoritmaya iletelim ea.Key = key; // Kullanıcı şimdi istediği mesajı girebilir Console.Write("Mesaj: "); byte[] message = ue.GetBytes(Console.ReadLine()); |
Mesajı şifrelemek için CreateEncryptor metodu ile bir dönüşüm arayüzü alıyoruz.
|
// Mesajı şifrelemeye başlayabiliriz. Önce bir dönüşüm arayüzü alalım: ICryptoTransform enc = ea.CreateEncryptor(); |
Mesajın tamamı elimizde ve DES için CanTransformMultipleBlocks doğru. Bu yüzden mesajı blok blok şifrelemek yerine TransformFinalBlock metodunu bir kez çağırarak şifreliyoruz.
|
// Sonra şifreleyelim byte[] output = enc.TransformFinalBlock(message, 0, message.Length); // Şifreyi yazalım Console.Write("Sifre: "); WriteHex(output); Console.WriteLine(); |
Şifrelemek ile şifreyi çözmek arasındaki tek fark ICryptoTransform''u CreateDecryptor metodunu kullanarak elde etmemiz.
|
// Şimdi kontrol edelim SymmetricAlgorithm da = new DESCryptoServiceProvider(); // Asağıdaki özellikler elbette ea sınıfı ile aynı olmak zorunda da.Key = key; da.Mode = CipherMode.ECB; da.Padding = PaddingMode.PKCS7; ICryptoTransform dec = da.CreateDecryptor(); byte[] check = dec.TransformFinalBlock(output, 0, output.Length); // Çözülmüş mesajı yazalım Console.Write("Mesajın aslı: "); Console.WriteLine(ue.GetString(check)); |
Her türlü kriptografik işlem CryptographicException verebilir. Bu yüzden bütün programı bu türlü hataları yakalayan bir try-catch bloğu altında çalıştırıyoruz.
|
} catch (CryptographicException e) { Console.WriteLine("Beklenmedik bir şey oldu. Ayrıntılar:"); Console.WriteLine(e.Message); return; } } |
Simetrik algoritmaların butun kolaylıklarına karşın çok kritik bir zayıflıkları var: Gönderici ile alıcı anahtar üzerinde nasıl anlaşacaklar? Farzedelim gönderici anahtarın "aliveli4950" olmasını istiyor. Bunu alıcıya şifreleyerek gönderemez, çünkü alıcı henüz anahtarın ne olduğunu bilmiyor; bu yüzden mesajı çözemeyecektir. Öte tarafta, gönderici anahtarı şifrelemeden de göndermez. Çünkü bu defa mesajı dinleyen herkes anahtarın ne olduğunu öğrenecek, ve bu anahtara dayanan sonraki mesajları kolayca çözebilecek. Sonuç olarak, iki tarafın anahtar üzerinde anlasmaları için bir başka yönteme ihtiyaçları var.
Bu problem günümüzde asimetrik kriptografi, ve onun en yaygın şekli olan açık anahtar şifrelemesi (public key cryptography) ile çözülüyor. Bu konu üzerinde de bir dahaki sefere duracağız.
