-
چهارشنبه, ۱۲ خرداد ۱۴۰۰، ۰۳:۴۵ ب.ظ
-
۱۵۴۲
رمزنگاری های کلاسیک در پایتون قسمت 2 - الگوریتم ویژنر (Vigenère)
درود به همه !
در قسمت قبلی سری دنباله دار رمزنگاری های کلاسیک در پایتون ، به شرح الگوریتم رمزنگاری سزار (Caesar) پرداختیم و روش های مربوط به آن را توضیح دادیم . همچنین آن را به طور کامل در پایتون پیاده سازی کردیم . امروز میریم سراغ یه الگوریتم دیگه که در اصل بسیار به الگوریتم سزار شبیه هستش ولی کلی بهتره . این الگوریتم ویژنر نام داره . با ما همراه باشید .
پیش نیاز این پست ، قسمت قبلی مجموعه یعنی الگوریتم سزار هستش . در پست قبلی بسیار از مفاهیم پایه مورد نیاز برای رمزنگاری های کلاسیک رو توضیح دادیم . همچنین پست امروز ارتباط زیادی با الگوریتم قبلی دارد . بنابراین دونستن پست قبلی ضروری هستش .
قسمت قبل : رمزنگاری های کلاسیک در پایتون قسمت 1 - الگوریتم سزار (Caesar)
فرض رو بر این میزاریم که مطالب قسمت قبل رو شما عزیزان میدونید و ادامه میدیم .
اگه یادتون باشه الگوریتم سزار به این صورت بود که میومدیم به هر کدوم از حروف الفبا یک عدد اختصاص میدادیم . a=0 , b=1 , c= 2 , ...
و به این صورت برای عملیات رمزگذاری میومدیم تک تک حروف متن آشکار (plain text) رو با کلید جمع پیمانه ای میکردیم و متن رمزشده (cipher text) بدست میومد .
برای رمزگشایی کردن نیز میومدیم کلید و حروف متن رمزشده رو از هم کم میکردیم . البته یه روش ساده تر گفتیم که بیایم کلید رو به فرم زیر در بیاریم :
سپس متن رمزشده رو با کلید جدید رمزگذاری کنیم و برسیم به متن آشکار (plain text) .
اما خب مشکل الگوریتم سزار اینه که به راحتی کلیدش شکسته میشه . چرا ؟ چون ما کلا 26 حالت برای کلید داریم . بنابراین با یک حمله ی آزمون جامع (Brute Force) به راحتی شکسته میشه . همچنین با روش های دیگری مثل روش فراوانی حروف که توضیحش دادیم میتونه به راحتی شکسته بشه .
الگوریتم ویژنر ، همون الگوریتم سزار هستش با این تفاوت که کلید آن ، یک حرف نیست بلکه کلید آن میتواند یک کلمه باشد .
به این صورت که طبق الگوریتم قبلی ، به هر حرف حروف الفبا یک عدد اختصاص میدیم (a=0 , b=1 , c=2 , ...) ، سپس یک کلمه را به عنوان کلید انتخاب میکنیم . مثلا mrpython . همچنین متن آشکاری را در اختیار داریم که میخواهیم به متن رمز شده تبدیل کنیم مثلا کلمه ی : we are the champions
ببینیم چطوری میشه متن آشکارمون رو با استفاده از کلیدی که مشخص کردیم ، از طریق الگوریتم ویژنر رمزگذاری کنیم :
در ابتدا کاراکتر های خالی متن اشکار رو حذف میکنیم : wearethechampions
سپس متن آشکار و کلید رو حرف به حرف زیر هم مینویسیم و هر حرف از متن آشکار رو با حرف مربوطه در کلید به روش جمع پیمانه ای که در روش سزار گفته شد جمع میکنیم . توجه کنید وقتی میخوایم کلمه کلید رو زیر متن آشکار بنویسیم ، کلمه کلید رو به طور پیوسته پشت سر هم تکرار میکنیم طبق تصویر زیر :
به همین راحتی تونستیم جمله ی we are the champions رو با کلید mrpython و از طریق الگوریتم ویژنر رمزگذاری کنیم .
نحوه ی رمزگشایی نیز بسیار راحت است . کافی است این بار متن رمز شده (cipher text) و کلمه ی کلید رو حرف به حرف زیر هم بنویسیم . سپس هر حرف متن رمز شده رو با حرف مرتبط در کلید با روش رمزگشایی سزار، رمزگشایی کنیم تا به متن آشکار برسیم :
استفاده از تابلو برای رمزگذاری و رمزگشایی به روش ویژنر :
برای اینکه هر دفعه نخوایم متن آشکار و کلید رو زیر هم بنویسیم و دستی عملیات رمزگذاری یا رمزگشایی رو انجام بدیم ، جدولی برای الگوریتم ویژنر هستش که میتونیم با استفاده از اون جدول خیلی راحت عملیات رمزگذاری و رمزگشایی رو انجام بدیم :
همینطور که نوشته شده ، ستون جدول نشان دهنده ی کلید (KEY) و سطر جدول نشان دهنده ی متن آشکار (PLAINTEXT) است. برای مثال میخواهیم ببینیم معادل رمزگذاری شده ی حرف B با استفاده از کلید C چه حرفی میشود . ابتدا در ستون اول جدول حرف C را پیدا میکنیم . سپس در سطر اول جدول نیز حرف B را پیدا میکنیم و بررسی میکنیم محل تلاقی این دو حرف کدام خانه و کدام حرف است. این همان حرف رمزگذاری شده است.
تا اینجا نحوه ی رمزگذاری و رمزگشایی کردن اطلاعات با این الگوریتم رو بلدیم . بریم سراغ پیاده سازی در پایتون :)
برای اینکار از همون تابعی که برای رمزگذاری و رمزگشایی کردن سزار نوشتیم استفاده میکنیم .
سورس رمزگذاری :
# vignere cipher
def encrypt(text , key): ciphertext = "" for plain_char in text: plain_number = ord(plain_char) - 65 key_number = ord(key) - 65 cipher_number = plain_number + key_number if cipher_number > 25: # if cipher_number > 25 -> start from 0 cipher_number = cipher_number - 26 cipher_char = chr(cipher_number+65) ciphertext += cipher_char return ciphertext
text = input("text : ").replace(" ","").upper() key = input("key : ").upper()
#############################################while len(key) < len(text): key += key key_list = [] for i in range(len(text)): key_list.append(key[i])
cipher_text = "" for char,key in zip(text,key_list): # encrypting char with key cipher_char = encrypt(char , key) cipher_text += cipher_char
print("Cipher text : {}".format(cipher_text))
توضیح : خب تابع encrypt رو که در بخش سزار به طور کامل توضیحش دادیم که میاد یک کلمه یا حرف رو با یک کلید جمع پیمانه ای میکنه .
تا اونجایی که یک خط کامنت قرمز رنگ کشیدم رو بلدیم . اما از اونجا به بعد رو توضیح میدیم .
در ابتدا یک حلقه while گذاشتیم که تا وقتی که طول کلید کمتر از طول متن آشکار هستش ، کلید هی به خودش اضافه بشه(چند برابر خودش بشه) . اینکار برای اینه که بتونیم بعدا کلید رو دقیقا حرف به حرف زیر متن آشکار بنویسیم .
سپس یک لیست تعریف کردیم به نام key_list که در اصل این لیست هر عضوش قراره یکی از حروف کلید بشه که زیر یکی از حرفای متن آشکار قرار گرفته . بنابراین با استفاده از یک for اومدیم کاراکتر ها رو از کلید (key) به لیست key_list اضافه کردیم (append) .
تا اینجا ما هم متن آشکار رو داریم و هم کلیدی که به صورت پیوسته حروفش داخل لیست key_list نوشته شدن . حالا یک حلقه ی for روی هر دوی اینها یعنی متن آشکار و لیست کلید ، با استفاده از عبارت zip اجرا کردیم ، سپس هر کدوم از حرف های متن آشکار رو با استفاده از حرف کلید مربوطه رمزگذاری کردیم (با استفاده از تابع encrypt) . و تمام این حروف رمزگذاری شده رو به متغییر cipher_text اضافه کردیم تا نهایتا این متغییر حاوی متن رمزگذاری شده باشه . پس از ساختن این متغییر اون رو برای کاربر چاپ کردیم .
کارکرد اسکریپت رو ببینیم :
و اما بریم برای سورس رمزگشایی . اینم بسیار بسیار سادس . دقیقا همون سورس قبلی . فقط یه تغییر خیلی کوچولو باید در تابع encypt بوجود بیاریم تا به جای اینکه رمزگذاری کنه ، رمزگشایی کنه . اگه یادتون باشه گفتم برای اینکه تابع encrypt تبدیل به رمزگشا بشه باید کلید رو به فرم زیر در بیاریم :
حالا فقط کافیه همین تبدیل رو در تابع encrypt انجام بدیم . اونجاس که تابع encrypt تبدیل به تابع رمزگشایی و در نتیجه سورس ، تبدیل به سورس رمزگشایی میشه :
# vignere cipher
def decrypt(text , key): ciphertext = "" for plain_char in text: plain_number = ord(plain_char) - 65 key_number = ord(key) - 65 ############################## key_number = 26 - key_number # ############################## cipher_number = plain_number + key_number if cipher_number > 25: # if cipher_number > 25 -> start from 0 cipher_number = cipher_number - 26 cipher_char = chr(cipher_number+65) ciphertext += cipher_char return ciphertext
text = input("text : ").replace(" ","").upper() key = input("key : ").upper()
while len(key) < len(text): key += key key_list = [] for i in range(len(text)): key_list.append(key[i])
plain_text = "" for char,key in zip(text,key_list): # encrypting char with key plain_char = decrypt(char , key) plain_text += plain_char
print("Plain text : {}".format(plain_text))
این دقیقا همون سورس قبلیه فقط یکم اسامی متغییر ها و تابع تغییر داده شده . اگه دقت کنید تنها تغییر اساسی که داده شده داخل تابع decrpyt (همون encrypt فقط اسمش عوض شده) هستش . دور خط کد اضافه شده با علامت # یک جعبه قرمز کشیده شده . این همون تغییری بود که گفتیم یعنی کلید رو به فرم مورد نظر برای رمزگشایی درآوردیم .
حالا کارکرد رمزگشا رو ببینیم :
روش های شکستن رمز ویژنر (Vigenère):
آزمون جامع از نوع دیکشنری (Brute Force using Password List) : طبق معمول ، روش آزمون جامع ، روشی برای شکستن کلید اینگونه الگوریتم ها هست . به این صورت که با توجه به محتوا و فرستندگان پیام رمز شده ، تعدادی حدس از کلید متن را داخل فایلی (password list) مینویسیم . سپس اسکریپتی مینویسیم که با تمام این کلید ها ، عملیات رمزگشایی رو انجام بده .رمزگشایی با هرکدوم از کلید ها که منجر به متنی با معنی شد ، معلوم میشود کلید درست همان بوده است .
امیدوارم لذت برده باشید . سوالی بود مطرح کنید .
یا حق !
Telegram Channel : @mrpythonblog