Makale Özeti

Günümüzde bilgi hırsızlığını yapan kişilerin yaş ortalamasının ne kadar düştüğünü düşünürseniz, bu konu hakkında daha çok ciddiyetle durulması gerektiğine sizlerinde benim kadar hak vereceğinizi düşünüyorum. Bilgi güvenliği için izlenebilecek yöntemler arasında en büyük rekabeti olan sanırım Cryptology(şifreleme) sistemleridir. Bir yandan yeni algoritmalar geliştiren uzmanların yanısıra, buna paralel olarak ilerleyen bu algoritmaları çözmek yada kırmak için uğraşan kişiler olduğu sürece bu savaş sanırım dünyanın sonuna kadar devam edecektir. asp.net1.1 ve 2.0 ile gelen Cryptography namespace i bizlere çeşitli algoritmalar sunmaktadır. Bu yazımda bilgilerimizi şifreleme ve deşifreleme (Encryption / Decryption) işlemlerinin asp.net 2.0da nasıl yapıdığından bahsedeceğim.

Makale

 

Yazarın Notu : Bu makaleyi yazarken oldukça çekindiğim bir nokta vardı ! Uzunluğu!

Oldukça uzun gibi görünen bu makalemi sıkılmadan okuyacağınızı ümid etmekteyim.

Konunun öneminden dolayı elimden geldiğince detaya inmek istedim.

Alpaslan Aykovan

 

Günümüzde bilgi hırsızlığını yapan kişilerin yaş ortalamasının ne kadar düştüğünü düşünürseniz, bu konu hakkında daha çok ciddiyetle durulması gerektiğine sizlerinde benim kadar hak vereceğinizi düşünüyorum.

 

Bilgi güvenliği için izlenebilecek yöntemler arasında en büyük rekabeti olan sanırım Cryptology(şifreleme) sistemleridir.

 

Bir yandan yeni algoritmalar geliştiren uzmanların yanısıra, buna paralel olarak ilerleyen bu algoritmaları çözmek yada kırmak için uğraşan kişiler  olduğu sürece bu savaş sanırım dünyanın sonuna kadar devam edecektir.

 

.net 1.1 ve 2.0 ile gelen Cryptography namespace i bizlere çeşitli algoritmalar sunarak, bilgilerimizi şifreleme ve deşifreleme  (Encryption / Decryption) imkanı sağlamaktadır.

 

Şifreleme konusu çok derin olmasından dolayı ben kolay anlaşılabilmesi adına konuyu bir kaç makaleye dökmeyi uygun gördüm.

 

Şifreleme işlemleri için temelde kullanılan iki yöntem mevcuttur.

·        Simetrik şifreleme(SymetricAlgorithm)

·        Asimetrik şifreleme(AsymetricAlgorithm)

·        Hash Sifreleme (Hash Algorithm)

 

Bu makalemde öncelikle sizlere Simetrik algoritmalar dan bahsetmek istiyorum.

 

Simetrik algoritmalar şifreleme ve deşifreleme işlemleri için tek olan bir anahtar(key) kullanırlar.

 

Anahtarın ne olduğundan biraz bahsedecek olursak, bir çok şifreleme algoritması hesaplamaları yapabilmesi için kendisine referans olarak alabileceği bir sayı dizisine ihtiyaç duyar.Bu sayı dizisi genelde ikili(binnary) sayısal sisteminden oluşur.

 

Olayı çok kaba haliyle örneklemek gerekirse

 

 

 private int topla(int key, int data)

 {

    return data + key;

 }

 

                       

Şimdi yukarıdaki basit örneği ele alalım. Diyelimki data değişkenine

10 değerini verelim, key değerimizde 5 olsun. Dönen değer farzı misal şifrelenmiş değerimiz 15 olacaktır. Biz 10 sayısını(yani asıl verimizi) tekrar elde etmek için ,key değerini 5 olarak atamamız lazım aksi takdirde 10 yani asıl değerimizi elde edemeyiz.

 

 

 private int cikart(int key, int data)

 {

     return data - key;

 }

 

  

      Tabii yukarıdaki sadece işin mantık kısmını, çok basite indirerek anlatılmış halidir, amaç key’in ne amaçla kullanıldığına dair fikir vermesidir.

 

      Şimdi Simetrik algoritmaların .net içinde nasıl kullanıldığı konusuna gelelim. Ben örnek algoritma olarak TripleDES algoritmasını kullanacağım.

 

      Şifreleme işlemleri için System.Security.Cryptography, anahtar bilgilimizi bir config dosyası oluşturup içine yazdıracağımız için System.IO namespacelerini eklememiz gerekiyor.

 

 

using System.Security.Cryptography;

using System.IO;

 

 

Şifreleme işlemlerimizi yapmamızı sağlayacak bir class oluşturalım.Bu sayede projemizin her yerinden çok rahat ulaşabiliriz.

 

 

 namespace Cryptology

 {

   public class DataCryptography

   {

   

   }

 }

 

 

Öncelikle static olarak private bir string oluşturalım ve algoritmamızın adını atayalım.Bu bize algoritmanın class içerisinde her yerde kullanmamızı sağlar.

 

 

 private static string algorithm = "TripleDES";

 

 

Şimdi bu algoritma ile anahtarımızın oluşturlması ve gerekli yerlere yazılması lazım.Bu işlemi oluşturacak bir void geliştiriyoruz.

 

 

 public static void GenerateKey(bool CreateFile, string FileName)

 {

  SymmetricAlgorithm algo = SymmetricAlgorithm.Create(Algorithma);

  algo.GenerateKey();

  byte[] key = algo.Key;

  

  if (CreateFile)

  {

    FileStream fs = new FileStream(FileName, FileMode.Create);

    fs.Write(key, 0, key.Length);

    fs.Close();

  }

   

   Configuration config = System.Web.Configuration.WebConfigurationManager.

   OpenWebConfiguration("~");

 

 AppSettingsSection appSettings = config.GetSection("appSettings") as   AppSettingsSection;

 

 appSettings.Settings.Add("secureKey", Convert.ToBase64String(key));

 

 config.Save();

 }

 

 

SymmetricAlgorithm classı bize, bu tür için geliştirilen algoritmaları    kullanmamızı sağlar.

 

 Satır 1-2-3 : Öncelikle algoritma create ediliyor, sonrasında bu algoritmadan bir key üretiliyor ve üretilen key byte[] tipinde bir arraya atılıyor.

        

 Satır 4-5-6—7 : Referans olarak istenilen CreateFile'a eğer true değeri atanmış ise FileStream ile bir config dosyası oluşturuluyor ve key değerinin orjinali buraya yazılıyor ve Stream objemiz kapatılıyor. İleride keyin tekrar lazım olması durumlarına karşın bu dosyayı saklamalısınız.

        

Kullanacağımız şifreleme ve deşifreleme işlemleri için  key değerini web.configden alacağız.

 

Satır 8-9-10-11-12-13 : Config adında bir Configuration objesini inherit ediyoruz ve rootdaki web.config dosyasına ulaşacağımızı belirtiyoruz.

 

Daha sonra web.config içerisinde yer alan AppSettingse ulaşmak için AppSettingsSection classını inherit ediyoruz.

 

AppSetiings içerisine secureKey adlı bir key(AppSettings içerisindeki key) oluşturuyoruz ve value olarak algo adlı SymmetricAlgorithm ile türettiğimiz key[] değerini yazdırıyoruz.

 

Burada dikkat edilmesi gereken konu web.config valid bir xml dosyası olduğundan ikili(binary) sistemde data tutamaz(bkz. yukarıda key'in yapısına)bu yüzden key[] değerimizi base64String'e çeviriyoruz.Base64String bu türdeki verilerimizi web üzerinde string olarak tutmamızı sağlar.

         

Daha sonra web.config den key değerimizi geri çağırdığımızda FromBase64 ile tekrardan byte[] a çevireceğiz, bu konu ileride anlatılmıştır.

         

Son olarak config dosyamızı save ediyoruz. Keyimizin orjinal hali şu şekilde olacaktır,

 

 

ToBase64Stringe çevrilerek web.config dosyasındaki halide aşağıdadır.

        

Buraya kadar bir key oluşturduk ve oluşturduğumuz bu key değerini önce bir config dosyasına orjinal halini yazdırdık daha sonrada web.config dosyasına base64string olarak dönüştürüp appSettings içerisinde secureKey adlı bir keye value olarak yazdırdık.

 

Şimdi bu key’i ileride kullanabilmek için byte[] tipinde statatic bir

değişken tanımlayalım ve bunu farklı bir class içinde kullanalım, ileride çok işimize yarayacaktır.

 

 

public class SecureKey

{

    public static byte[] Key = Convert.FromBase64String(ConfigurationManager.AppSettings["secureKey"]

.ToString());

 

 // Hatırlarsanız, web.config içine key değerimizi ToBase64String()e çevirip yazdırmıştık.

  // Burada da FromBase64 ile tekrardan Byte[] tipine çeviriyoruz.

 

}

 

 

Bir datayı şifrelediğimiz zaman bize byte[] tipinde numeric bir data dizisi döndürücektir. Burada önemli olan bir nokta var! Eğer dönen değeri string tipinde bir yere yazdıracaksak mutlaka bir sperator(ayraç) kullanmamız gerekmektedir. Şifrelenen datayı deşifre etmek istediğimizde tekrardan byte[] tipine döndürebilmemiz için bu speratorlerden faydalanacağız.

 

 

 public static string Encrypt(byte[] key, string data)

 {

    string returnData = null;

    SymmetricAlgorithm algo = SymmetricAlgorithm.Create(algorithm);

    algo.Key = key;

     

    byte[] encodingData = System.Text.Encoding.UTF8.GetBytes(data);

     

    algo.GenerateIV();

    MemoryStream ms = new MemoryStream();

    ms.Write(algo.IV, 0, algo.IV.Length);

   

    CryptoStream cs = new CryptoStream(ms, algo.CreateEncryptor()

    ,CryptoStreamMode.Write);

   

    cs.Write(encodingData, 0, encodingData.Length);

    cs.FlushFinalBlock();

    cs.Close();

 

    for (int i = 0; i < ms.ToArray().Length; i++)

    {

   returnData += ms.ToArray()[i].ToString() + " ";

    }

    return returnData;

 }

 

 

 

Satır 1 : Şifrelenmiş olarak geri dönecek değerimizi taşıyacak olan değişkenimizi tanımlıyoruz.

 

Satır 2-3 : Algoritmamızı(bkz. GenerateKey methodu) oluşturuyoruz ve bu algoritmanın kullanacağı key değerini tanımlıyoruz.

 

Satır 4 : Çoğunlukla şifreleme algoritmalarının byte[] tipi ile çalıştığından bahsetmiştik, bu sebeple şifrelemek istediğiniz veriyi de byte tipine çevirmelisiniz ben yukarıda UTF8 formatında çevirdim.

 

Satır 5-6-7 : GenerateIV(Generate Initialization Vector)ile bir vektör oluşturuyoruz. Vektör konusu oldukça önemlidir, bir örnek ile bu konuyu daha anlaşılır kılalım.

 

Bir kredi kartı numarasını şifrelediğinizi varsaylım burada 4 er guruptan oluşan 16 basamaklı bir sayı dizisi söz konusudur. Bu numarayı eğer bir vektör atamadan şifrelerseniz her seferinde size aynı şifrelenmiş değeri döndürecektir. Örneğin 1. şifrelemede 145 555 666 999 888 222 555 değerini döndürdüyse ikincisindede aynı değeri döndürecektir.

 

      Biraz sabır ile bu yapıyı çözen bir kişi sizin şifreli verilerinizi kolaylıkla okuyabilir.Farzı misal 145 değeri 4 e denk geliyor ise bütün bilgilerinizde bunu kolaylıkla okuyabilir , şifrelerinizde vektör oluşturduğunuzda her şifreleme işlemi için farklı bir veri gurubu size döndürecektir.

 

      Bir MemoryStream classını inherit edip veriyi şifreleme işlemine sokmadan önce oluşturduğumuz vektörü bu MemoryStream ile yazıyoruz.

 

      Satır 8-9-10-11 : Burada verimizi şifreleyecek olan CryptoStream nesnemizi inherit ediyoruz, ilk parametre olarak kullanacağı stream nesnesini(ms)belirliyoruz, sonrasında şifreleme işlemi için algoritmamızın CreateEncryptor() methodunu tanımlıyoruz ve son olarak bu bilgiyi yazdıracağımız için CryptoStreamMode un write methodunu kullanıyoruz.

 

      CryptoStream in Write methodu ile şifrelenen veriyi memorye yazıyoruz ve FlushFinalBlock() ile memoryi güncelleyip bu stream nesnemizi kapatıyoruz.

 

      Satır 12-16 : Şifrelenen veri byte[] tipinde olduğundan ve biz geriye string tipinde data döndüreceğimiz için bir for döngüsü ile şifrelenen veriyi returnData değişkenimize atıyoruz, burada dikkat edilmesi gereken konu “returnData += encodingData[i].ToString() + " ";” satırının sonundaki " " bölümdür. Bu boşluk veriyi deşifre etmek istediğimizde tekrardan byte[] tipine döndüreceğimiz için sperator olarak kullanacağımız referans noktasıdır.

 

Sırada şifrelenen bu datayı deşifreleme(Decrypt) etmeye geldi.Bunun için aşağıdaki method işimize yarayacaktır.

 

 public static string Decrypt(byte[] Key, string data)

 {

    string[] sperator = new string[] { " " };

    string[] stringData = data.Split(sperator,StringSplitOptions.

                          RemoveEmptyEntries);

    byte[] convertData = new byte[stringData.Length];

 

    for (int i = 0; i < stringData.Length; i++)

    {

       convertData[i] = Convert.ToByte(stringData[i]);

    }

 

    SymmetricAlgorithm algo = SymmetricAlgorithm.Create(Algoritma);

    algo.Key = Key;

 

   byte[] IV = new byte[algo.IV.Length];

   int count = 0;

   Array.Copy(convertData, IV, algo.IV.Length);

   algo.IV = IV;

   count += IV.Length;

 

   MemoryStream ms = new MemoryStream();

 

   CryptoStream cs = new      CryptoStream(ms,algo.CreateDecryptor(),CryptoStreamMode.Write);

   cs.Write(convertData,count,convertData.Length - count);

   cs.FlushFinalBlock();

   cs.Close();

           

   return System.Text.Encoding.UTF8.GetString(ms.ToArray());

 

}

 

 

Yukarıdaki kodu adım adım incelediğimizde;

 

Verimizi şifrelediğimizde bize byte tipinde bir array döndürmüştü bizde bu bilgiyi string olarak birleştirmiş fakat bu işlem yapılırken iki değer arasına " " şeklinde sperator kullanmıştık.

Şimdi bu sperator sayesinde bütün halindeki verimizi tekrardan ayırıp byte array tipine çevirerek işlemimize başlayalım.

 

Satır 1-2-3-4 : sperator adında bir array tanımlıyoruz ve değerini " " şeklinde atıyoruz. Şifrelenen verimizi ayırmak için, String türünün split methodunu kullanarak stringData[] adındaki arrayın içerisine atıyoruz burada dikkat etmeniz gereken nokta split methodunun aldığı ikinci parametre. Eğer StringSplitOptions.None yaparsananız, Şifrelenen veri içindeki null değerlerini de alacağından işlemimiz TypeMistMatch hatasına düşecektir bu yüzden RemoveEmptyEntires ile null alanları ortadan kaldırmalıyız.

 

Şu anda şifrelenen veriyi tekrardan ayırdık fakat hala string tipinde bulunmaktadır bunu byte[] tipinde bir arraya atmak için convertData[] adında bir array tanımlayıp eleman(index) sayısını stringData.Length ile belirliyoruz ve  bir for döngüsü ile stringData içerisindeki veriyi Convert.ToByte ile convertData[] içerisine atıyoruz.

 

 

 

 

Satır 5-6: Şifreleme işlemindede anlatıldığı gibi burada algoritmamızı oluşturuyoruz.

 

Satır 7-8-9-10-11 : Veriyi şifrelerken IV(Initialization Vector)den bahsetmiştik. Burada şifrelenen veri içerisindeki IV değerini alıp byte[] array tipindeki bir değişkene atamamız gerekmektedir. Bunun için IV adında bir değişken tanımlıyoruz ve eleman(index) sayısını oluşturduğumuz algoritmanın(algo) normalde oluşturdu IV eleman sayısı olarak atıyor ve count adında inteager tipinde bir değişken tanımlıyoruz. Bu değişken ileride kolaylık sağlayacaktır.

 

Array ın Copy methodundan faydalınarak şifrelenen verimizin içerisindeki vektor(IV) değerini IV adındaki değişkenimize atıyor ve bu değeri algoritmamıza(algo) ya tanımlıyoruz son olarakda tanımladığımız count değişkenine IV.Length değerini atarak ileride kullanmamızı sağlıyoruz.

 

Satır 12-13-14-15-16-17 : Yine şifreleme işleminde olduğu gibi MemoryStreamimizi ve CryptoStreamimizi inherit ediyoruz fakat burada iki farklı nokta var.

 

Verimizi şifrelerken MemoryStream e IV değerini yazdırmıştık burada böyle bir işleme gerek olmadığından sadece stream özelliğinden faydalnacağız diğer bir konu da CryptoStreamin aldığı ikinci parametre.

Burada veriyi deşifre edeceğimiz için algoritmanın(algo) CreateDecryptor() özelliğinden faydalanıyoruz.

 

Verimizi deşifre edilmiş olarak stream ile yazdırırken cs.write methodunda öncelikle şifrelenen veri,kaçıncı eleman(index) dan başlayacağı ve uzunluğu tanımlanıyor.

 

İşlemimizi FlushFinalBlock() ve Close methodları ile tamamlıyoruz. Memory e yazılmış olan verimizi tekrardan string tipinde geri döndürmek için Encoding in UTF8.GetString() mthodundan faydalanıyoruz.

 

Hazırladığımız bu classı form içerisinde kullanımı şu şekildedir.

Anahtar oluşturma

protected void btnGenerateKey_Click(object sender, EventArgs e)

{

  root.DataCryptography.GenerateKey(true,"SecureKey");

}

 

Şifreleme

protected void btnEncrypt_Click(object sender, EventArgs e)

{

 lblEncryptValue.Text = root.DataCryptography.Encrypt(root.SecureKey.Key, txtData.Text);

}

 

Deşifreleme

protected void btnDecrypt_Click(object sender, EventArgs e)

{

  lblDecryptValue.Text = root.DataCryptography.Decrypt(root.SecureKey.Key, lblEncryptValue.Text);

}

 

Sonuç

 

Yazımı bitirmeden önce önemli birkaç noktaya değinmek istiyorum.

Web.Config her nekadar yayınlanmasada buluduğu noktadaki kişi(ler) tarafından okuna bilir. Bu yüzden web.config dosyasını da şifrelemeniz gerekmektedir.

Diğer bir konu işlem yapacağınız verinin şifrelenmesi sırasında doğacak bir problem deşifre edilmesi sırasında karşınıza hata olarak çıkacaktır.

Örneğin veri uzunluğu yada veri türü sorunları gibi...

Bu tür konularda gerekli önlemleri ve kontrolleri almak yada geliştirmek sizin hayal gücünüzle paraleldir.

 

Bir sonraki makalem bu serinin devamı olan asimetrik şifreleme algoritmaları üzerine olacaktır.

 

 

Kaynak : Volkan uzun'un Asp.net ve simetrik sifreleme adlı makalesi

 

 

Sabrınız ve okuduğunuz için teşekkür ederim.

 

Alpaslan Aykovan

Senior Solution Developer