Bu makalede C# ile bir veri en doğru ve güvenli yoldan nasıl şifrelenir konusunu işleyeceğiz. Şifreleme konusu çok karmaşıktır. Yazılımcıların şifreleme konusunda çok fazla seçenekleri yoktur. Asimetrik ve Simetrik şifreleme kullanırlar. Biz bu makalede Simetrik şifreleme olan AES(Advanced Encryption Standard) konusunu derinlemesine işleyeceğiz. Şifreleme mantığı tamamen matematiğe dayanır, eğer sıfırdan bir algoritma yazıyorsanız. Daha fazla uzatmadan direkt konuya geçelim.
Key Size (Anahtar Boyutu)
AES standartlarında 3 adet anahtar boyutu vardır. Bu boyutla “bit” adı verilir. Bu bouylar sırasıyla aşağıdaki gibidir.
- 128 bit
- 192 bit
- 256 bit
Peki bu boyutlar tam olarak ne işe yaradıklarını sırasıyla anlatayım.
- 128 bit: Şifreleme işlemlerini en hızlı yapabilen anahtar boyutudur. Çok hızlıdır.
- 192 bit: Güvenlik seviyesi arttırılmış bir anahtar boyutudur. Performanstan biraz ödün verilir.
- 256 bit: En yüksek güvenlik seviyesi sağlayan anahtar boyutudur. Büyük veriler şifrelenirken, performans kaybı yaşanır.
Biz işlemlerimizde 256 Bit şifreleme yapacağız.
Block Size (Blok Boyutu)
Blok boyutu AES’te sabittir. 128 bit‘tir. AES algoritmasının temel tasarım özelliğidir.
Mode of Operation (Şifrelemenin Çalışma Şekli)
AES şifrelemesinde kullanılan mod (mode of operation), verilerin nasıl şifreleneceğini belirler. Bu modlardan bazıları aşağıdaki gibidir.
- ECB: Her blok bağımsız olarak şifrelenir. Bu mod, aynı düz metin bloklarının aynı şifreli metin bloklarına dönüştürülmesi nedeniyle güvenlik açısından zayıftır ve genellikle tavsiye edilmez.
- CBC: Her blok, bir önceki şifreli blok ile XOR işlemine tabi tutulur ve ardından şifrelenir. İlk blok için bir başlangıç vektörü (IV) kullanılır. Bu mod, tekrarlanan veri bloklarının farklı şifreli metin bloklarına dönüştürülmesini sağlar ve ECB moduna göre daha güvenlidir. Bu mod, paralel şifrelemeye uygun değildir. çünkü her bloğun şifrelenmesi için önceki bloğun şifrelenmiş hali gereklidir.
- CTR: Her blok için bir sayaç değeri şifrelenir ve sonuç veriye XOR uygulanır. Sayaç değeri sürekli olarak artar ve IV olarak da kullanılabilir. Bu mod, paralel işlemeye olanak tanır ve performans açısından avantajlıdır.
Biz şifreleme işlemlerinde CBC modunu kullanacağız.
Padding Mode (Doldurma Modu)
Kullanılan padding modları, blok şifreleme algoritmasının blok boyutunu tam olarak doldurmak için verinin nasıl doldurulacağını belirler. Bir çok padding modu vardır. Ben sadece PKCS7 modunu anlatacağım. Çünkü en çok kullanılan ve standartlaşmış birçok protokollerde kullanılır.
PKCS #7 padding, eksik bayt sayısına eşit bir değerle her baytı doldurur. Örneğin, 16 baytlık bir blokta 10 baytlık veri varsa, kalan 6 baytın her biri 06
ile doldurulur.
Örnek: | 10 byte veri | 06 06 06 06 06 06 |
IV – Initialization Vector (Başlangıç Vektörü)
Başlangıç vektörü, şifreleme modlarında kullanılan ve her veri şifreleme işlemi için başlangıç değerini belirleyen rastgele bir değerdir. Özellikle CBC, CFB, OFB ve CTR gibi blok şifreleme modlarında önemlidir. Amacı ise, mesela “KORAY” metnini şifrelediğimiz zaman parola “1234” ise şifrelenen veri: “X3tRvXZw” gibi bir sonuç döner. Tekrar aynı işlemi yaptığımızda geriye dönen sonuç aynı olmaz mesela “RhY5490Xe” olur. Fakat IV aynı olursa geriye dönen sonuç hep “X3tRvXZw” olur.
Salt (Tuz)
Kriptografide özellikle şifrelerin hashlenmesi işlemlerinde kullanılan rastgele bir değerdir. Amacı ise, aynı şifrenin farklı hash sonuçları üretmesini sağlamaktır, böylece hashlenmiş şifrelerin çakışmasını önler ve sözlük saldırıları veya rainbow table saldırıları gibi saldırılara karşı ek güvenlik sağlar. Örnek olarak: Mesela parolamız “1234” diyelim. Bunun hash değeri, atıyorum “5555555555”. Bu değer her zaman “5555555555” olarak geri döner. Salt ise parolanın başına yada sonuna rastgele veri ekler. Mesela: “wX4TgV+1234” olur parolamız. Veya “1234+wX4TgV”, veya “12+wX4TgV+34” gibi. Bu değerlerin hash değerleri hep farklı olur.
Şimdi kodlamaya geçebiliriz.
Veri Şifreleme ve Çözme
Şimdi ise bir örnek yapalım.
using System;
using System.Linq;
namespace KorayOrnekler
{
internal class Program
{
static void Main(string[] args)
{
// 64 bytelık rastgele bir veri oluşturalım.
byte[] randomData = new byte[64];
Random rnd = new Random();
rnd.NextBytes(randomData);
// Veriyi şifreleyelim ve bir değişkene atayalım.
byte[] encryptedData = Encryption.Encrypt(randomData, "Çok güçlü bir parola");
// Şifreli veriyi çözelim.
byte[] decryptedData = Encryption.Decrypt(encryptedData, "Çok güçlü bir parola");
// Şimdi ise düzgün orjinal veri ile çözülen veriyi karşılaştıralım.
if (randomData.SequenceEqual(decryptedData))
{
Console.WriteLine("Orjinal veri ile çözülen veri birbirine uyuyor.");
}
else
{
Console.WriteLine("Veriler uyumsuz!");
}
}
}
}