رمزنگاری های کلاسیک در پایتون قسمت 2 - الگوریتم ویژنر (Vigenère) :: Mr Python | مستر پایتون

رمزنگاری های کلاسیک در پایتون قسمت 2 - الگوریتم ویژنر (Vigenère)

  • ۱۳۲۹

رمزنگاری های کلاسیک در پایتون قسمت 2 - الگوریتم ویژنر (Vigenère)

درود به همه !

در قسمت قبلی سری دنباله دار رمزنگاری های کلاسیک در پایتون ، به شرح الگوریتم رمزنگاری سزار (Caesar) پرداختیم و روش های مربوط به آن را توضیح دادیم . همچنین آن را به طور کامل در پایتون پیاده سازی کردیم . امروز میریم سراغ یه الگوریتم دیگه که در اصل بسیار به الگوریتم سزار شبیه هستش ولی کلی بهتره . این الگوریتم ویژنر نام داره . با ما همراه باشید .

پیش نیاز این پست ، قسمت قبلی مجموعه یعنی الگوریتم سزار هستش . در پست قبلی بسیار از مفاهیم پایه مورد نیاز برای رمزنگاری های کلاسیک رو توضیح دادیم . همچنین پست امروز ارتباط زیادی با الگوریتم قبلی دارد . بنابراین دونستن پست قبلی ضروری هستش . 

قسمت قبل : رمزنگاری های کلاسیک در پایتون قسمت 1 - الگوریتم سزار (Caesar)

 

فرض رو بر این میزاریم که مطالب قسمت قبل رو شما عزیزان میدونید و ادامه میدیم . 

اگه یادتون باشه الگوریتم سزار به این صورت بود که میومدیم به هر کدوم از حروف الفبا یک عدد اختصاص میدادیم . a=0 , b=1 , c= 2 , ... 

و به این صورت برای عملیات رمزگذاری میومدیم تک تک حروف متن آشکار (plain text) رو با کلید جمع پیمانه ای میکردیم و متن رمزشده (cipher text) بدست میومد .

برای رمزگشایی کردن نیز میومدیم کلید و حروف متن رمزشده رو از هم کم میکردیم . البته یه روش ساده تر گفتیم که بیایم کلید رو  به فرم زیر در بیاریم :

key = 26 - key

سپس متن رمزشده رو با کلید جدید رمزگذاری کنیم  و برسیم به متن آشکار (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 تبدیل به رمزگشا بشه باید کلید رو به فرم زیر در بیاریم :

key = 26 - key

حالا فقط کافیه همین تبدیل رو در تابع 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

نظرات: (۰) هیچ نظری هنوز ثبت نشده است
ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی