التشفير (Encryption)
تشفير البيانات وفكها باستخدام النوع Aes
تَستعرِض الشيفرة التالية مثال طرفية توضيحي من شبكة مطوري مايكروسوفت (MSDN). يَشرَح هذا المثال طريقة تشفير سِلسِلة نصية ثُم فَكّ التشفير باِستخدَام الخوارزمية القياسية "معيار التشفير المُتقدِم Advanced Encryption Standard"، وتُسمَى اختصارًا AES
.
يُوفِّر إطار عمل .NET
النوع Aes
، والذي يُنْفِّذ خوارزمية معيار التشفير المُتقدِم AES
. تَتكون كُلًا من شيفرتي التشفير وفَكُه مِن عدة خطوات مُشتَركة.
نحتاج عامةً لإِنشاء مَجْرى (stream) بيانات ستمُرّ عَبره البيانات المَطلوب تَشفيِرها أو فَكّ تَشفيِرها.
using (MemoryStream msEncrypt = new MemoryStream())
كذلك سنحتاج إلى تنشئة عملية تَحوِيل مَجْرى (stream transform) -إِمّا مُشفِر أو مُفكِّك للتشفير- من النوع ICryptoTransform
بحيث تُطبَّق على البيانات أثناء مرُّورَها بالمَجْرى. يُستخدَم التابعين CreateEncryptor
وCreateDecryptor
لإنشاء المُشفِر ومُفكِّك التشفير على الترتيب.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
أخيرًا، نُنشِئ مَجْرى تَشفير من النوع CryptoStream
يُوصِل عملية تَحوِيل المَجْرى (stream transform) بمَجْرى البيانات مع تحديد وَضْع التَوصِيل إِمّا للقراءة أو للكتابة.
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
مثلًا، في حالة التشفير، سنَكتُب البيانات المطلوب تشفيرها على مَجْرى التشفير CryptoStream
. يتم تَّنْفيذ عملية تَحوِيل المَجْرى (stream transform) -مُشفِر في هذه الحالة- على البيانات، وتُكتَب النتيجة المُشفَّرة على المَجْرى المُمرَّر لمَجْرى التشفير بوَضْع الكتابة.
using (MemoryStream msEncrypt = new MemoryStream()) using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) swEncrypt.Write(plainText); encrypted = msEncrypt.ToArray(); }
في حالة فَكّ التشفير، سيَقرأ مَجْرى التشفير CryptoStream
البيانات المطلوب فَكّ تشفيرها من المَجْرى المُمرَّر له بوَضْع القراءة. يتم تَّنْفيذ عملية تَحوِيل المَجْرى (stream transform) -مُفكِّك شَفرة في هذه الحالة- على البيانات. أخيرًا نقرأ البيانات بعد فَكّ التشفير من خلال مَجْرى التشفير.
using (MemoryStream msDecrypt = new MemoryStream(cipherText)) using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) using (StreamReader srDecrypt = new StreamReader(csDecrypt)) plaintext = srDecrypt.ReadToEnd();
الشيفرة بالكامل:
using System; using System.IO; using System.Security.Cryptography; namespace Aes_Example { class AesExample { public static void Main() { try { string original = "Here is some data to encrypt!"; // أنشئ كائن من النوع Aes المُستخدَم لتوليد كلا من المفتاح ومتجه التهيئة using (Aes myAes = Aes.Create()) { // قم بتشفير سلسلة نصية إلى مصفوفة بايتات byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV); // قم بفك تشفير مصفوفة بايتات إلى سلسلة نصية string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV); Console.WriteLine("Original: {0}", original); Console.WriteLine("Round Trip: {0}", roundtrip); } } catch (Exception e) { Console.WriteLine("Error: {0}", e.Message); } } } }
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV) { // Check arguments. if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("IV"); byte[] encrypted; // أنشئ كائن من النوع Aes باستخدام المفتاح ومتجه التهيئة المحددين using (Aes aesAlg = Aes.Create()) { aesAlg.Key = Key; aesAlg.IV = IV; // أنشئ مُشفر والذي سيستخدم كمحول للمجرى ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); // انشئ المجاري المستخدمة خلال عملية التشفير using (MemoryStream msEncrypt = new MemoryStream()) using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) swEncrypt.Write(plainText); encrypted = msEncrypt.ToArray(); } } // أعد مصفوفة البايتات المشفرة المُنشأة من مجرى الذاكرة return encrypted; }
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV) { // تحقق من الوسائط if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("IV"); string plaintext = null; // أنشئ كائن من النوع Aes باستخدام المفتاح ومتجه التهيئة المحددين using (Aes aesAlg = Aes.Create()) { aesAlg.Key = Key; aesAlg.IV = IV; // أنشئ مفكك الشفرة الذي سيستخدم كمحول للمجرى ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); // انشئ المجاري المستخدمة خلال عملية فك التشفير using (MemoryStream msDecrypt = new MemoryStream(cipherText)) using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) using (StreamReader srDecrypt = new StreamReader(csDecrypt)) // قم بقراءة البايتات من مجرى فك التشفير وأسْندها إلى متغير من النوع string plaintext = srDecrypt.ReadToEnd(); } return plaintext; }
تُحدِّد الخوارزمية AES
طريقة لتشفير البيانات الالكترونية. أُسِست عام 2001 بواسطة المعهد الوطني للمعايير والتقنية NIST
بالولايات المتحدة الامريكية، ومازالت تُعدّ الخوارزمية القياسية للتشفير التَماثلي (symmetric encryption).
ملاحظات:
-
تَتوفَّر عِدة أوضاع تَشفير
cipher mode
ضِمْن خوارزميةAES
. تَستطيع تَحديد وَضْع التشفير بإِسْناد إِحدى قيم التعدادCipherMode
إلى الخاصيةMode
. لا تَستخدِم أبدًا وَضْع التشفيرElectronic codebook - ECB
- مما يَعنِي عدم إِختيارCipherMode.ECB
-؛ لأنه يُنتِج عملية تَحوِيل مَجْرى ضعيفة. -
يجب أن تَستخدِم مُولِّد تَشفير عشوائي -أو اِستخدِم الشيفرة بالأسفل (إنشاء مفتاح من كلمة سرّ / سلسلة نصية إضافية (Salt) عشوائية)- لتنشئة مفتاح (Key) جيد غيْر ضعيف. يُفضَّل أيضًا أن يَكُون حجم المفتاح 256 بت. تستطيع تخصيص حَجم المفتاح مِن خلال الخاصية
KeySize
كما تُوفِّر الخاصيةLegalKeySizes
قائمة بالأحجام المُدعَّمة. - يُمكِنك اِستخدَام سِلسِلة نصية إضافية (salt) -كالمثال بالأسفل (إنشاء مفتاح من كلمة سرّ / سلسلة نصية إضافية (Salt) عشوائية)- لتهيئة مُتّجَه التهيئة (initialization vector - IV).
مثال آخر باستخدام خوارزمية AES
شيفرة التشفير:
public static string Encrypt(string cipherText) { if (cipherText == null) return null; byte[] clearBytes = Encoding.Unicode.GetBytes(cipherText); using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(CryptKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.Close(); } cipherText = Convert.ToBase64String(ms.ToArray()); } } return cipherText; }
شيفرة فَكّ التشفير:
public static string Decrypt(string cipherText) { if (cipherText == null) return null; byte[] cipherBytes = Convert.FromBase64String(cipherText); using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(CryptKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); encryptor.Key = pdb.GetBytes(32); encryptor.IV = pdb.GetBytes(16); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(cipherBytes, 0, cipherBytes.Length); cs.Close(); } cipherText = Encoding.Unicode.GetString(ms.ToArray()); } } return cipherText; }
تُستخدَم كالتالي:
var textToEncrypt = "TestEncrypt"; var encrypted = Encrypt(textToEncrypt); var decrypted = Decrypt(encrypted);
تشفير البيانات وفكها باستخدام النوع RijndaelManaged
يَتطلَّب فضاء الاسم System.Security.Cryptography
private class Encryption { private const string SecretKey = "topSecretKeyusedforEncryptions"; private const string SecretIv = "secretVectorHere"; public string Encrypt(string data) { return string.IsNullOrEmpty(data) ? data : Convert.ToBase64String( this.EncryptStringToBytesAes(data, this.GetCryptographyKey(), this.GetCryptographyIv()) ); } public string Decrypt(string data) { return string.IsNullOrEmpty(data) ? data : this.DecryptStringFromBytesAes(Convert.FromBase64String(data), this.GetCryptographyKey(), this.GetCryptographyIv()); } private byte[] GetCryptographyKey() { return Encoding.ASCII.GetBytes(SecretKey.Replace('e', '!')); } private byte[] GetCryptographyIv() { return Encoding.ASCII.GetBytes(SecretIv.Replace('r', '!')); } }
private byte[] EncryptStringToBytesAes(string plainText, byte[] key, byte[] iv) { MemoryStream encrypt; RijndaelManaged aesAlg = null; try { aesAlg = new RijndaelManaged { Key = key, IV = iv }; var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); encrypt = new MemoryStream(); using (var csEncrypt = new CryptoStream(encrypt, encryptor, CryptoStreamMode.Write)) using (var swEncrypt = new StreamWriter(csEncrypt)) swEncrypt.Write(plainText); } finally { aesAlg?.Clear(); } return encrypt.ToArray(); }
private string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv) { RijndaelManaged aesAlg = null; string plaintext; try { aesAlg = new RijndaelManaged { Key = key, IV = iv }; var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (var msDecrypt = new MemoryStream(cipherText)) using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) using (var srDecrypt = new StreamReader(csDecrypt)) plaintext = srDecrypt.ReadToEnd(); } finally { aesAlg?.Clear(); } return plaintext; }
يُستخدَم كالتالي:
var textToEncrypt = "hello World"; //-> zBmW+FUxOvdbpOGm9Ss/vQ== var encrypted = new Encryption().Encrypt(textToEncrypt); //-> hello World var decrypted = new Encryption().Decrypt(encrypted);
تنبيه: يُنفِّذ النوع Rijndael
النسخة الأقدم من خوارزمية AES
، ولذلك ينبغي استعمال النوع Aes
الذي يُنفِّذ النسخة الحديثة.
إنشاء مفتاح من كلمة سر / سلسلة نصية إضافية (Salt) عشوائية
تَستعرِض الشيفرة التالية مثال طرفية توضيحي من شبكة مطوري مايكروسوفت (MSDN). يَشرح هذا المثال كيفية تنشئة مفتاح (key) آمِن اعتمادًا على كلمة سرّ مُحدَّدة من قِبَل المُستخدِم، بالإضافة إلى طريقة تنشئة سِلسِلة نصية إضافية (salt أو يدعى غفل باللغة العربية، انظر كتاب «علم التعمية واستخراج المعمى عند العرب») عشوائية باِستخدَام مُولِّد تشفير عشوائي.
using System; using System.Security.Cryptography; using System.Text; public class PasswordDerivedBytesExample { public static void Main(String[] args) { // اجلب كلمة السر من المستخدم Console.WriteLine("Enter a password to produce a key:"); byte[] pwd = Encoding.Unicode.GetBytes(Console.ReadLine()); byte[] salt = CreateRandomSalt(7); // TripleDESCryptoServiceProvider أنشئ كائنًا من النوع TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider(); try { Console.WriteLine("Creating a key with PasswordDeriveBytes..."); // أنشئ كائنًا من النوع PasswordDeriveBytes ثم أنشئ مفتاح لخوارزمية TripleDES // من كلمة سر وسلسلة نصية إضافية PasswordDeriveBytes pdb = new PasswordDeriveBytes(pwd, salt); // أنشئ المفتاح وأسنده إلى الخاصية Key الموجودة بكائن موفر خدمة التشفير tdes.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, tdes.IV); Console.WriteLine("Operation complete."); } catch (Exception e) { Console.WriteLine(e.Message); } finally { ClearBytes(pwd); ClearBytes(salt); tdes.Clear(); } Console.ReadLine(); } }
/// توليد غفل (سلسلة نصية إضافية) بالطول المحدد public static byte[] CreateRandomSalt(int length) { byte[] randBytes; if (length >= 1) randBytes = new byte[length]; else randBytes = new byte[1]; // RNGCryptoServiceProvider إنشاء كائن من النوع RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider(); // املأ المخزن بالبايتات العشوائية rand.GetBytes(randBytes); return randBytes; }
/// امسح البايتات من المخزن لكي لا تُقرَأ مستقبلًا من الذاكرة public static void ClearBytes(byte[] buffer) { if (buffer == null) throw new ArgumentNullException("buffer"); // اضبط قيمة كل بايت إلى القيمة 0 for (int x = 0; x < buffer.Length; x++) { buffer[x] = 0; } }
ملاحظات:
-
تَستقبِل الدالة
PasswordDeriveBytes
المَبنية مُسبَقًا (built-in) كلمة سرّ، وتَستخدِم خوارزميةPBKDF1
القياسية لتولِّيد المفتاح. تَقوم هذه الدالة بشكل افتراضي بمائة تكرار أثناء تولِّيد المفتاح؛ وذلك لتُبطئ من فاعلية هَجمَات القوى الغاشمة (brute force attacks). بالإضافة إلى ذلك، يُعزز اِستخدَام السِلسِلة النصية الإضافية (salt) المُولَّدة عشوائيًا من قوة المفتاح. -
تَستخدِم الدالة
CryptDeriveKey
خوارزمية التَقطيع (hashing) المُمرَّرة إليها -تم اختيارSHA1
بالمثال- لتَحوِيل المفتاح المُولَّد من الدالةPasswordDeriveBytes
إلى مفتاح مُتوافِق مع خوارزمية التشفير المُمرَّرة إليها -تم اختيارTripleDES
بالمثال-، يمكنك أيضًا تخصيص كلًا من حجم المفتاح والقيمة المبدئية لمُتٍّجَه التهيئة (initialization vector - IV) بتمريرهما كمُعامِلين للدالة. في المثال بالأعلى، مُرِّرت القيمة 192 بايت كما اُستخدِم مُوفِّر خدمة التشفير من النوعTripleDESCryptoServiceProvider
لتهيئة مُتّجَه التهيئة. - عندما تحتاج إلى مفتاح قوي مُولَّد عشوائيًا لاِستخدَامُه لتشفير كمية ضخمة من البيانات، اِستخدِم هذه الطريقة لتولِّيده مِن مجرد كلمة سرّ. يُمكِن أيضًا اِستخدَام هذه الطريقة لتوليد كلمات سر لعِدّة مُستخدِمين للولوج إلى نفس البيانات.
-
للأسف، لا تُدعِّم الدالة
CryptDeriveKey
خوارزميةAES
حاليًا (تَحقَّق هنا). يُمكِن التحايل على ذلك بالاعتماد على حَاوِي خوارزميةTripleDES
، مع أنه سيُؤدِي إلى تَبَعيّة لهذه الخوارزمية وسيُقصِرك على مُستَوَى حمايتها، فمثلًا لن تستطيع تَوليد مفاتيح بأحجام أكبر من تلك المُدعَّمة بخوارزميةTripleDES
على الرغم من دَعََّم تلك الأحجام بخوارزميةAES
.
دوال التعمية (Hashing)
توليد تدقيق المجموع (checksum) لملف باستخدام خوارزمية SHA1
تَتطلَّب فضاء الاسم System.Security.Cryptography
public string GetSha1Hash(string filePath) { using (FileStream fs = File.OpenRead(filePath)) { SHA1 sha = new SHA1Managed(); return BitConverter.ToString(sha.ComputeHash(fs)); } }
توليد القيمة المعماة (hash أو المقطعة) لسِلسِلة نصية
public static string TextToHash(string text) { var sh = SHA1.Create(); var hash = new StringBuilder(); byte[] bytes = Encoding.UTF8.GetBytes(text); byte[] b = sh.ComputeHash(bytes); foreach (byte a in b) { var h = a.ToString("x2"); hash.Append(h); } return hash.ToString(); }
ملحوظة: الشيفرة بالكامل موجودة بـمستودع mahdiabasi/SHA1Tool في GitHub.
ترجمة -وبتصرف- للفصلين Encryption / Cryptography و Work with SHA1 in C# من كتاب .NET Framework Notes for Professionals
تم التعديل في بواسطة جميل بيلوني
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.