PDA

View Full Version : آموزش: رمز نگاری متقارن در دات نت به کمک C#



returnx
چهارشنبه 11 بهمن 1391, 20:44 عصر
از اونجایی که منابع فارسی زیادی در مورد رمز نگاری متقارن در دات نت پیدا نکردم(دقیقا بر عکس انگلیسی که به وفور یافت میشد) و خودم با این موضوع سرو کار داشتم ، تصمیم گرفتم یک آموزش کوچیکی در این مورد در این تاپیک بزارم...
سعی می کنیم از تعریف مفاهیم خیلی سریع عبور کنیم تا از خواندن این مطلب خسته نشید اما چند تا تعریف کوچک برای مقدمه فکر کنم بد نباشه :
رمز نگاری چیست!؟

رمزنگاری عبارت است از بهم ریختگی اطلاعات به طوری که برای کسی قابل فهم نباشد. فن آوری رمزنگاری امکان مشاهده، مطالعه و تفسیر پیام‌های ارسالی توسط افراد غیر مجاز را سلب می‌نماید. از رمزنگاری به منظور حفاظت داده‌ها در شبکه‌های عمومی نظیر اینترنت استفاده می‌گردد. در این رابطه از الگوریتم‌های پیشرفته ریاضی به منظور رمزنمودن پیام‌ها و ضمائم مربوطه، استفاده می‌شود.
رمز نگاری متقارن(کلید خصوصی) چیست!؟

منداولترین نوع رمزنگاری مبتنی بر کلید، رمزنگاری "کلید خصوصی" است. به این نوع رمزنگاری، متقارن، سنتی، رمز مشترک، کلید رمز نیز گفته می‌شود. در این نوع رمزنگاری، فرستنده و گیرنده از کلید استفاده شده به منظور رمزنگاری اطلاعات آگاهی دارند. رمزنگاری کلیدخصوصی، گزینه‌ای مناسب به منظور مبادله اطلاعات بر روی اینترنت و یا ذخیره سازی اطلاعات حساس در یک بانک اطلاعاتی و یا یک فایل می‌باشد.
قابل به ذکر که در این روش هم سمت فرستنده (رمز کننده داده) و هم دریافت کننده (رمز گشا) از یک کلید رمز استفاده می کنند.
رمز نگاری نامتقارن (کلید عمومی) چیست!؟

رمزنگاری کلید عمومی که از آن با نام رمزنگاری نامتقارن نیز یاد می‌گردد، از دو کلید متفاوت برای رمزنگاری استفاده می‌نماید : یک کلید برای رمزنگاری و کلیدی دیگر برای رمزگشائی. در رمزنگاری کلید عمومی، با استفاده از یک روش کاملا" ایمن یک کلید برای ارسال کننده اطلاعات ایجاد و وی با استفاده از کلید فوق، اقدام به رمزنگاری و ارسال پیام رمز شده برای گیرنده می‌نماید. امکان رمزگشائی پیام رمز شده صرفا" توسط دریافت کننده، امکان پذیر خواهد بود. در رمزنگاری کلید عمومی، سیستم یک زوج کلید خصوصی و عمومی ایجاد می‌نماید. کلید عمومی برای شخصی که از آن به منظور رمزنگاری یک پیام استفاده می‌نماید، ارسال می‌گردد. وی پس از رمزنگاری پیام با استفاده از کلید عمومی، پیام رمز شده را ارسال می‌نماید. دریافت کننده با استفاده از کلید خصوصی، اقدام به رمزگشائی پیام می‌نماید.(
ما در این آموزش فقط بر روی رمز نگاری متقارن کار می کنیم...
در رمز نگاری متقارن ما دو نوع الگوریتم معروف داریم که به الگوریتم DES (http://fa.wikipedia.org/wiki/%D8%A7%D8%B3%D8%AA%D8%A7%D9%86%D8%AF%D8%A7%D8%B1%D 8%AF_%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB %8C_%D8%AF%D8%A7%D8%AF%D9%87%E2%80%8C%D9%87%D8%A7) و Rijndeal (AES) (http://fa.wikipedia.org/wiki/%D8%A7%D8%B3%D8%AA%D8%A7%D9%86%D8%AF%D8%A7%D8%B1%D 8%AF_%D8%B1%D9%85%D8%B2%D9%86%DA%AF%D8%A7%D8%B1%DB %8C_%D9%BE%DB%8C%D8%B4%D8%B1%D9%81%D8%AA%D9%87) مشهورند...
AES و Rijndeal در واقع یک الگوریتمند ، که تفاوت بسیار جزئی دارند....
ما در مورد اینکه پشت این الگوریتم ها چه می گذرد ، چیزی نمی گیم ، چون اگر بخوام بگم باید یک کتاب در این مورد بنویسم و من هم اونقدر سواد ریاضی بالایی ندارم ، پیشنهاد می کنم لینک ها یی که داده شده را مطالعه کنید ؛ البته اگر علاقه دارید ، یا یک کتاب تخصصی در این مورد بخوانید یا اگر اونقدر حوصله ندارید و کتاب مهندسی اینترنت دکتر احسان ملکیان را در اختیار دارید ، فصل 11 این کتاب رو مطالعه کنید...
در ضمن ممکنه این نوشته ، جشنواره ای از اشتباهات املایی ، نگارشی ، تایپی و فنی باشه ، پیشاپیش عذر میخوام...
و در آخر هم این نکته رو هم بگم ، برای یکپارچه ماندن آموزش ، اگر سوالی ، انتقادی ، اشتباهی و... را خواستید به من اطلاع بدید لطفا ار پیام خصوصی استفاده کنید...

returnx
پنج شنبه 12 بهمن 1391, 10:12 صبح
ابتدا بريم سراغ الگوريتم DES در دات نت:
بايد بگيم که براي اينکه از کلاس هاي آماده شده رمزنگاري در دات نت استفاده کنيد بايد فضاي نام System.Security.Cryptography را به پروژتون اضافه کنيد...
خوب حالا يک کلاس جديد بسيازيد و فضاي نام بالا رو به کلاستون اضافه کنيد ، حالا يک متد جديد ايجاد کنيد ، اين متد قرار آدرس يک فايل ورودي را به همراه آدرس يک فايل خروجي و يک کليد گرفته و فايل وردي را رمز کرده و يک فايل خروجي رمز شده جديد توليد کرده...
در نتيجه ، امضاي متد شما بايد به اين شکل باشه :

public void encrypt_file(string input_file_address,string output_file_address,string key)

نکته مهمي که در اينجا وجود دارد اين هست که ، کليد رمز در اين الگوريتم حتما بايد 64 بيتي باشه!!(اين نکته به برجسته ترين ضعف اين الگوريتم بدل شد!!)
براي استفاده از الگوريتم DES بايد از کلاس DESCryptoServiceProvider استفاده کنيم اما قبل از آن بايد دو تا FileStream بسازيم ، يکي براي فايل ورودي و ديگري براي فايل خروجي و يک Binary Reader براي خواندن از فايل ورودي و يک Binary Writer براي نوشتن در فايل خروجي پس در نتيجه کد هايي مشابه کد زير به متدتون اضافه کنيد:

FileStream input_f_stream = new FileStream(input_file_address, FileMode.Open, FileAccess.Read);

FileStream output_f_stream = new FileStream(output_file_address, FileMode.Create, FileAccess.Write);

BinaryReader i_reader = new BinaryReader(input_f_stream);

BinaryWriter i_writer = new BinaryWriter(output_f_stream);

خوب حالا نوبت به اين ميرسه که از کلاس DESCryptoServiceProvider استفاده کنيم براي استفاده از الگوريتم DES و Set کردن کليد رمز از کد زير استفاده مي کنيم :

DESCryptoServiceProvider i_des = new DESCryptoServiceProvider();

i_des.Key = Encoding.ASCII.GetBytes(key);

i_des.IV = Encoding.ASCII.GetBytes(key);

باید به این نکته توجه کنیم که اگر ما Key و IV را مقدار دهی نکنیم ، بصورت تصادفی مقدار دهی میشن ، که این کار خیلی جالب نیست ، به این دلیل که ما کلید رمز را برای رمز گشایی نخواهیم داشت!!
در مورد IV یا initialization vector در مستندات MSDN چیز زیادی بجز این چند خط گفته نشده (یا شایدم من ندیدم):

Note that you must also provide the initialization vector (IV). This value is used as part of the encryption. Like the key, the IV is randomly generated if you do not provide the value. Because the values must be the same for the encryption and the decryption, you must not permit random generation of these values.
حالا زمان این رسیده که ما با استفاده از رابط(اینترفیس) ، ICryptoTransform مشخص کنیم که از این الگوریتم می خواهیم در رمز نگاری استفاده کنیم یا رمز گشایی ، برای اینکار از کد زیر استفاده می کنیم:


ICryptoTransformi_cryptotransform=i_des.CreateEncr yptor();

و حالا نوبت به خواندن و نوشتن در فایل میرسه ، برای اینکه بتونیم فایل ورودی را رمز یا رمزگشایی کنیم از کلاس CryptoStream استفاده می کنیم تا یک جریان رمز نگاری ایجاد کرده باشیم، این کلاس در زمان رمزنگاری ، رمز کردن و نوشتن اطلاعات خوانده شده را در فایل خروجی داره که به این صورت انجام میدیم:

CryptoStreamencrypt_stream=newCryptoStream(i_write r.BaseStream, i_cryptotransform, CryptoStreamMode.Write);

در زمان رمز گشایی ، دقیقا این کلاس عکس این عمل را انجام میده و پارامتر آخر باید در حالت Read تنظیم شود...
و حالا باید از فایل ورودی بخوانیم و از کلاس CryptoStream بخوایم که برای ما در فایل خروجی بنویسه:

byte[] i_buffer=newbyte[2048];//Buffering 2Mb of file

while (i_reader.BaseStream.Position<i_reader.BaseStream.Length)




{



i_reader.Read(i_buffer, 0, i_buffer.Length);

encrypt_stream.Write(i_buffer, 0, i_buffer.Length);




}




قابل بذکر که این کار رو میشه به دو صورت انجام داد یا فایل رو یکجا بخونیم و یکجا بنویسیم ، یا اینکه فایل را Buffer کنیم بعد بنویسیم ، که این روش پیشنهاد میشه به این دلیل که اگر فضای Ram کمی داشته باشیم ، نمی توانیم فایل های بزرگ را بخوانیم....( در کد نهایی متد هر دو روش موجود هست ، اما روش اول بصورت Comment در آورده شده)
و مرحله ی آخر بستن تمام جریان ها :


encrypt_stream.Close();

input_f_stream.Close();

i_writer.Close();

i_reader.Close();

output_f_stream.Close();

به دلیل ، محدودیت 300000 کارکتری در هر پست ، کد کامل این متد در پست بعدی گذاشته می شود...

returnx
پنج شنبه 12 بهمن 1391, 10:14 صبح
تمام کد متد بالا بصورت یکپارچه:
public void encrypt_file(string input_file_address,string output_file_address,string key)//Encrypt File
{
//File Streams:
FileStream input_f_stream = new FileStream(input_file_address, FileMode.Open, FileAccess.Read);
FileStream output_f_stream = new FileStream(output_file_address, FileMode.Create, FileAccess.Write);
BinaryReader i_reader = new BinaryReader(input_f_stream);
BinaryWriter i_writer = new BinaryWriter(output_f_stream);
//Create Algorithms Provider:
DESCryptoServiceProvider i_des = new DESCryptoServiceProvider();
i_des.Key = Encoding.ASCII.GetBytes(key);
i_des.IV = Encoding.ASCII.GetBytes(key);
ICryptoTransform i_cryptotransform = i_des.CreateEncryptor();
//Read Orginal File And Encrypting Then Save To Secondery file:
CryptoStream encrypt_stream = new CryptoStream(i_writer.BaseStream, i_cryptotransform, CryptoStreamMode.Write);
/*
byte[] i_buffer = new byte[input_f_stream.Length];
i_reader.Read(i_buffer, 0, i_buffer.Length);
encrypt_stream.Write(i_buffer, 0, i_buffer.Length);
*/
byte[] i_buffer = new byte[2048];//Buffering 2Mb of file
while (i_reader.BaseStream.Position < i_reader.BaseStream.Length)
{
i_reader.Read(i_buffer, 0, i_buffer.Length);
encrypt_stream.Write(i_buffer, 0, i_buffer.Length);
}

encrypt_stream.Close();
input_f_stream.Close();
i_writer.Close();
i_reader.Close();
output_f_stream.Close();
}
در پست بعد روش رمزگشایی را مرور خواهیم کرد...

daneshjo90
پنج شنبه 12 بهمن 1391, 15:12 عصر
با عرض سلام
ممنون از اینکه این مطلب آموزنده رو در اختیار سایر دوستان قرار میدید

بنده منتظر کدگشایی این روش هستم

خواهشا شما هم مثل یعضی از افراد که چندتا پست میزنن ولی هیچوقت دگ ادامه نمیدنش نکنید و تا آخر آموزش رو بزارید

بازم ممنون

موفق باشی

returnx
پنج شنبه 12 بهمن 1391, 17:01 عصر
حالا نوبت ميرسه به رمزگشايي (Decryption) ، البته استفاده از لغت رمز شکني فکر کنم درست تر باشه!!!
مراحلي که در رمز گشايي فايل داريم ، کاملا شبيه قبل است با اين تفاوت که کار CryptoStream تغيير مي کند و عکس عمل رمز کردن را انجام مي دهد...
امضاي اين متد بايد به شکل زير باشد تا آدرس يک فايل رمز شده را همراه با يک آدرس براي فايل رمز گشايي شده و يک کليد رمز دريافت کند:
public void decrypt_file(string input_file_address,string output_file_address,string key)
حالا يک File Stream و يک Binary Reader براي خواندن از فايل ورودي به شکل زير ايجاد مي کنيم:
FileStream input_f_stream = new FileStream(input_file_address, FileMode.Open, FileAccess.Read);
BinaryReader i_reader = new BinaryReader(input_f_stream);
و حالا بايد کلاس الگوريتم DES رو ايجاد کنيم که مانند متد رمز کننده از کد زير استفاده مي کنيم :
DESCryptoServiceProvider i_des = new DESCryptoServiceProvider();
i_des.Key = Encoding.ASCII.GetBytes(key);
i_des.IV = Encoding.ASCII.GetBytes(key);
حالا بايد با استفاده از رابط ICryptoTransform مشخص کنيم که قرار چه عملي را انجام بده!؟ رمز نگاريي يا رمز گشايي !؟ جواب مشخصه :
ICryptoTransform i_cryptotransform = i_des.CreateDecryptor();
خوب ، حالا بايد با استفاده از جريان رمز فايل رمز شده را بخوانيم و رمز گشايي کنيم ، براي اينکار از کلاس CryptoStream ، به شکل زير استفاده ميکنيم :
CryptoStream deccrypt_stream = new CryptoStream(i_reader.BaseStream, i_cryptotransform, CryptoStreamMode.Read);
در پارامتر اول ، جريان ورودي را مي فرستيم ، در پارامتر دوم ICryptoTransform ميفرستيم تا مشخص کند وظيفه CryptoStream چيست و در پارامتر سوم مشخص کرديم که بايد چه عملي را انجام دهد...
و حالا بايد فايل رمز شده اي که خوانديم و رمز گشايي کرديم را در يک فايل جديد بنويسيم:
byte[] i_buffer = new byte[input_f_stream.Length];
StreamWriter i_stream_writer = new StreamWriter(output_file_address);
BinaryWriter i_writer = new BinaryWriter(i_stream_writer.BaseStream);
deccrypt_stream.Read(i_buffer, 0, i_buffer.Length);
i_writer.Write(i_buffer);
اما مشکلي که ما در اين قسمت باهاش روبرو هستييم اينکه کلاس CryptoStream قابلیت Seek نداره و ما نمی توانیم فایل های بزرگ را باهاش رمز گشایی کنیم ، متاسفانه هنوز برای بر طرف کردن این مشکل راهی پیدا نکردم!!!
و اما مرحله ی آخر ، بستن تمام جریان ها:
deccrypt_stream.Close();
input_f_stream.Close();
i_stream_writer.Flush();
i_stream_writer.Close();
i_reader.Close();
i_writer.Close();
کد کامل این متد در پست بعدی قرار خواهد گرفت....

returnx
پنج شنبه 12 بهمن 1391, 17:04 عصر
public void decrypt_file(string input_file_address,string output_file_address,string key)
{
//File Streams:
FileStream input_f_stream = new FileStream(input_file_address, FileMode.Open, FileAccess.Read);
BinaryReader i_reader = new BinaryReader(input_f_stream);
//Create Algorithms Provider:
DESCryptoServiceProvider i_des = new DESCryptoServiceProvider();
i_des.Key = Encoding.ASCII.GetBytes(key);
i_des.IV = Encoding.ASCII.GetBytes(key);
ICryptoTransform i_cryptotransform = i_des.CreateDecryptor();

CryptoStream deccrypt_stream = new CryptoStream(i_reader.BaseStream, i_cryptotransform, CryptoStreamMode.Read);

//Read Encrypted File And Write To Decrypted File:

byte[] i_buffer = new byte[input_f_stream.Length];
StreamWriter i_stream_writer = new StreamWriter(output_file_address);
BinaryWriter i_writer = new BinaryWriter(i_stream_writer.BaseStream);
deccrypt_stream.Read(i_buffer, 0, i_buffer.Length);
i_writer.Write(i_buffer);

deccrypt_stream.Close();
input_f_stream.Close();
i_stream_writer.Flush();
i_stream_writer.Close();
i_reader.Close();
i_writer.Close();
}//End function