[실습] 단순 치환 암호 프로그램
----------------------------------------------------------------------------------------------
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
LETTERS : A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
mykey : L F W O A Y U I S V K M N X P B D C R J T Q E G H Z
----------------------------------------------------------------------------------------------
입력평문 : H e l l o
출력암호 : I i m m p
-----------------------------------------------------------------------------------------------
simpleSubCipher.py
import sys
import random
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def main():
mymessages = 'If a man is offered a fact which goes against his instincts, he will scrutinize it closely, and unless the evidence is overwhelming, he will refuse to believe it. If, on the other hand, he is offered something which affords a reason for acting in accordance to his instincts, he will accept it even on the slightest evidence. The origin of myths is explained in this way. -Bertrand Russell'
mykey = 'LFWOAYUISVKMNXPBDCRJTQEGHZ'
if not key_is_valid(mykey):
sys.exit('There is an error in the key or symbol set.')
myencrypted = sub_cipher_encrypt(mymessages, mykey)
mydecrypted = sub_cipher_decrypt(myencrypted, mykey)
print("My Messages :", mymessages)
print("My Encrypted:", myencrypted)
print("My Decrypted:",mydecrypted)
def sub_cipher_decrypt(msg, key):
# input : str(msg), str(key)
# output: str(decrypted)
# function:
# * msg => 'Sy l nlx sr pyyacao l ylwj eiswi upar lulsxrj isr sxrjsxwjr ...'
# * key => 'LFWOAYUISVKMNXPBDCRJTQEGHZ'
# * LETTERS => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
decrypted = ''
for char in msg:
if char.upper() in key:
char_index = key.find(char.upper())
if char.isupper():
decrypted += LETTERS[char_index].upper()
else:
decrypted += LETTERS[char_index].lower()
else:
decrypted += char
return decrypted
def sub_cipher_encrypt(msg, key):
# input : str(mode=encrypt|decrypt), str(msg), str(key)
# output: str(encrypted)
# function:
# * msg => 'If a man is offered a fact which goes against his instincts ....'
# * LETTERS => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
# * key => 'LFWOAYUISVKMNXPBDCRJTQEGHZ'
# print(mode, key, msg)
encrypted = ''
for char in msg:
if char.upper() in LETTERS:
char_index = LETTERS.find(char.upper())
if char.isupper():
encrypted += key[char_index].upper()
else:
encrypted += key[char_index].lower()
else:
encrypted += char
return encrypted
def key_is_valid(key):
# input : str(key)
# output: True|False
# function:
# * list(key).sort() == list(LETTERS).sort()
return sorted(list(key)) == sorted(list(LETTERS))
if __name__ == '__main__':
main()
[실습] 단순 치환 암호 해킹에서 사용할 makeWordPatterns.py 파일 분석
단순 치환 암호 방식
---------------------------------------------------------------
symbols : ABCDEFGHIJKLMNOPQRSTUVWXYZ
---------------------------------------------------------------
암호 키 : LFWOAYUISVKMNXPBDCRJTQEGHZ
{'A': 'L', 'B': 'F', 'C': 'W', ... }
입력 평문 : Hello (0.1.2.2.3)
출력 암호문 : Iammp (0.1.2.2.3)
---------------------------------------------------------------
makeWordPatterns.py 파일
- 실행하면 WordPatterns.py 파일을 생성한다.
- dictionary.txt 파일의 패턴을 분석해서 WordPatterns.py 파일에 dict 객체을 만든다.
dictionary.txt --> makeWordPatterns.py --> WordPatters.py ------------------ ------------------- hello { '0.1.2.2.3': ['hello', ...] apple '0.1.1.2.3': ['apple', ...] the '0.1.2' : ['the', ...] .... } |
makeWordPatterns.py
# 단어 패턴 분석/저장
#
# 특정 문자열(EX: puppy)을 취해서 그 단어의 패턴을 return
# (0) 준비 : dictionary.txt
# (1) 입력 : dictionary.txt 파일
# (2) 출력 : WordPatterns.py 파일 생성
# (3) 기능 : dictionary.txt 파일의 단어들의 패턴을 분석하여 WordPatterns.py 파일 생성
# (4) 참고
import pprint
def get_word_pattern(word):
# (EX) '0.1.2.3.4.1.2.3.5.6' ==> 'D U S T B U S T E R'
# 0 1 2 3 4 1 2 3 5 6
# input : str(word)
# output : str(''.join(word_pattern))
# function :
word = word.upper()
next_num = 0
letter_nums = {}
word_pattern = []
for letter in word:
if letter not in letter_nums:
letter_nums[letter] = str(next_num)
next_num += 1
word_pattern.append(letter_nums[letter])
return '.'.join(word_pattern)
def main():
all_patterns = {}
fd = open('dictionary')
word_list = fd.read().split('\n')
fd.close()
for word in word_list:
# word_list 에서 각 string를 위한 패턴을 얻는다.
pattern = get_word_pattern(word)
if pattern not in all_patterns:
all_patterns[pattern] = [word]
else:
all_patterns[pattern].append(word)
fd = open('wordPatterns.py', 'w')
fd.write('allPatterns = ')
fd.write(pprint.pformat(all_patterns))
fd.close()
if __name__ == '__main__':
main()
WordPatterns.py 파일의 일부 내용
allPatterns = {'0.0.1': ['EEL'],
'0.0.1.2': ['EELS', 'OOZE'],
'0.0.1.2.0': ['EERIE'],
'0.0.1.2.3': ['AARON', 'LLOYD', 'OOZED'],
'0.0.1.2.3.4': ['AARHUS', 'EERILY'],
'0.0.1.2.3.4.5.5': ['EELGRASS'],
'0.1.0': ['ADA',
'BIB',
'BOB',
'DAD',
'DID',
[실습] 단순 치환 암호 해킹 프로그램
단순 치환 암호 크래킹 프로그램 제작하기
simpleSubHacker.py
#!/usr/bin/python3 # # 2. 단순 치환 암호 해킹 - 1st version # (0) 준비 : wordPatterns.py, simpleSubCipher.py, makeWordPatterns # (1) 입력 : 암호화 되어 있는 암호문을 입력 받음 # (2) 출력 : 평문 출력 # (3) 기능 : 암호화 되어 있는 암호문을 입력 받아서 복호화하여 평문을 출력 |
import os
import re
import copy
import sys
try:
import simpleSubCipher
import wordPatterns
import makeWordPatterns
except Exception as e:
print("Error: %s" % e)
sys.exit(1)
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
non_letters_or_space_pattern = re.compile('[^A-Z\s]')
def main():
message = 'Sy l nlx sr pyyacao l ylwj eiswi upar lulsxrj isr sxrjsxwjr, ia esmm rwctjsxsza sj wmpramh, lxo txmarr jia aqsoaxwa sr pqaceiamnsxu, ia esmm caytra jp famsaqa sj. Sy, px jia pjiac ilxo, ia sr pyyacao rpnajisxu eiswi lyypcor l calrpx ypc lwjsxu sx lwwpcolxwa jp isr sxrjsxwjr, ia esmm lwwabj sj aqax px jia rmsuijarj aqsoaxwa. Jia pcsusx py nhjir sr agbmlsxao sx jisr elh. -Facjclxo Ctrramm'
# 유효한 복호화 글자 변환을 얻는다.
letter_mapping = hack_simple_sub(message)
# 사용자에게 결과를 표시한다.
print('Mapping: ')
print(letter_mapping)
print()
print('Orignal Ciphertext:')
print(message)
print()
hacked_message = decrypt_with_cipher_letter_mapping(message, letter_mapping)
print(hacked_message)
def decrypt_with_cipher_letter_mapping(ciphertext, letter_mapping):
# input :
# output:
# function :
# * 암호문을 letter_mapping으로 복호화해 return
# * 복호화하기 어려운 모든 문자는 밑줄 문자로 바꾼다.
# letter_mapping 매핑으로부터 첫 번째 단순 부분 키를 생성한다.
key = ['x'] * len(LETTERS)
for cipher_letter in LETTERS:
if len(letter_mapping[cipher_letter]) == 1:
# 글자가 하나만 있다면 그것을 키에 더한다.
key_index = LETTERS.find(letter_mapping[cipher_letter][0])
key[key_index] = cipher_letter
else:
ciphertext = ciphertext.replace(cipher_letter.lower(), '_')
ciphertext = ciphertext.replace(cipher_letter.upper(), '_')
key = ''.join(key)
# 생성된 key로 암호문을 복호화 한다.
return simpleSubCipher.sub_cipher_decrypt(ciphertext, key)
def hack_simple_sub(message):
intersected_map = get_bank_cipher_letter_mapping()
cipher_word_list = non_letters_or_space_pattern.sub('', message.upper()).split()
for cipher_word in cipher_word_list:
# 각각의 암호 단어에서 새로운 암호 글자 매핑을 얻는다.
candidate_map = get_bank_cipher_letter_mapping()
word_pattern = makeWordPatterns.get_word_pattern(cipher_word)
if word_pattern not in wordPatterns.allPatterns:
continue # 이 단어는 사전에는 존재하지 않으므로 루프를 다시 돌린다.
# 각 후보 글자를 맵에 더한다.
for candidate in wordPatterns.allPatterns[word_pattern]:
add_letters_to_mapping(candidate_map, cipher_word, candidate)
# 이미 존재하는 교집합 맵과 새 맵의 교집합을 구한다.
intersected_map = intersect_mappings(intersected_map, candidate_map)
# 다른 리스트에서 푸는 데 성공한 글자를 삭제한다.
return remove_solved_letters_from_mapping(intersected_map)
def intersect_mappings(map_a, map_b):
# input : map_a, map_b
# output : intersected_mapping
# function :
# * 두 맵의 교집합을 구하기 위한 빈 맵을 만들고 두 맵에 모두 존재하는 복호화 후보 글자만 더한다.
intersected_mapping = get_bank_cipher_letter_mapping()
for letter in LETTERS:
# 리스트가 비어 있다는 것은 어떤 문자라도 들어갈 수 있다는 것을 의미한다.
# 여기에서는 다른 맵 전체를 단순히 복사한다.
if map_a[letter] == []:
intersected_mapping[letter] = copy.deepcopy(map_b[letter])
elif map_b[letter] == []:
intersected_mapping[letter] = copy.deepcopy(map_a[letter])
else:
# map_a[letter]의 글자가 map_b[letter]에도 존재하면
# intersected_mapping[letter]에 글자를 더한다.
for mapped_letter in map_a[letter]:
if mapped_letter in map_b[letter]:
intersected_mapping[letter].append(mapped_letter)
return intersected_mapping
def remove_solved_letters_from_mapping(letter_mapping):
# 암호 글자가 단 하나의 글자에 대응할 때 "풀었다"라고 선언할 수 있고, 그 글자 외 다른 글자는 제거해야 한다.
# (ex) 'A' 맵은 후보 글자 ['M', 'N']에 대응하고 'B' 맵은 ['N']에 대응한다면, 결과적으로 'A'는 'M'에 대응한다.
# 'A'가 글자 하나에 대응한다는 것을 알았으므로 다른 모든 글자의 리스트에서 'M'을 지울 수 있다.
# 즉, 맵을 줄이기 위해 루프를 사용하고 있는 것이다.
loop_again = True
while loop_again:
# 처음에는 루프를 반복하지 않을 것으로 가정한다.
loop_again = False
# solved_letters는 대문자의 리스트가 될 것이고, letter_mapping에 있는 가능한 대응 글자가 한 개인 경우에만 글자를 추가한다.
solved_letters = []
for cipher_letter in LETTERS:
if len(letter_mapping[cipher_letter]) == 1:
solved_letters.append(letter_mapping[cipher_letter][0])
# 문자를 풀었다면 복호화 후보 글자가 다른 암호 글자에 대응되어 있으면 안된다.
# 따라서, 다른 리스트에서 그 글자를 지원야 한다.
for cipher_letter in LETTERS:
for s in solved_letters:
if len(letter_mapping[cipher_letter]) != 1 and s in letter_mapping[cipher_letter]:
letter_mapping[cipher_letter].remove(s)
if len(letter_mapping[cipher_letter]) == 1:
# 새로운 글자를 풀었다면 루프를 계속한다.
loop_again = True
return letter_mapping
def add_letters_to_mapping(letter_mapping, cipher_word, candidate):
# input :
# * letter_mapping : 딕셔너리 값을 취해서 이 함수에 의해 복사한 암호 글자 변환을 저장한다.
# * cipher_word : 암호 단어 문자열 값이다.
# * candidate : 암호 단어를 복호화한 대응할 수 있는 영어 단어이다.
# output :
# function :
# * 이 함수는 암호 글자 변환에 들어 있는 암호 글자의 복호화 후보 글자들을 candidate에 추가한다.
for i in range(len(cipher_word)):
if candidate[i] not in letter_mapping[cipher_word[i]]:
letter_mapping[cipher_word[i]].append(candidate[i])
def get_bank_cipher_letter_mapping():
# input :
# output :
# function : 복호화 글자 변환에 빈 리스트를 채운 딕셔너리 값을 리턴한다.
return {'A': [], 'B': [], 'C': [], 'D': [], 'E': [], 'F': [], 'G': [], 'H': [], 'I': [], 'J': [], 'K': [], 'L': [],
'M': [], 'N': [], 'O': [], 'P': [], 'Q': [], 'R': [], 'S': [], 'T': [], 'U': [], 'V': [], 'W': [], 'X': [],
'Y': [], 'Z': []}
if __name__ == '__main__':
main()
'현대 암호학' 카테고리의 다른 글
[현대 암호학] 04-2. 일회용 패드 - 절대 해독 불가능한 암호 (0) | 2021.06.22 |
---|---|
[현대 암호학] 04. 대칭(Symmetric) 암호 -1. 문자 암호에서 비트열 암호로 (0) | 2021.06.22 |
[현대 암호학] 03. 실습(3) (0) | 2021.06.22 |
[현대 암호학] 03. 실습(2) (0) | 2021.06.22 |
[현대 암호학] 03. 실습(1) (0) | 2021.06.21 |