By rls1004 | December 3, 2016
암알못의 암호 핥기 - 전치암호
이번 편에서는 고전 암호 중 전치 암호에 대해 알아보려고 합니다!
1. 전치 암호 : 단순 전치 암호
전치 암호는 평문의 알파벳 순서를 재배치하여 암호화 합니다.
암호화 키가 14352라고 하면 평문인 “ABCDEFG”는 “ADCEBFG”로 암호화됩니다.
이때 복호화 키는 무엇일까요?
암호화 키가 14352 일 때 복호화 키 구하기
[1의 인덱스: 1][2의 인덱스: 5][3의 인덱스: 3][4의 인덱스: 2][5의 인덱스: 4]
복호화 키는 15324
복호화 키인 15324에 의해 암호문인 “ADCEBFG”를 복호화하면 “ABCDEFG”로 복호화됩니다.
역시 문제를 풀어볼까요?
Training: Crypto - Transposition I
oWdnreuf.lY uoc nar ae dht eemssga eaw yebttrew eh nht eelttre sra enic roertco drre . Ihtni koy uowlu dilekt oes eoyrup sawsro don:wm ibirlgeacb.m
(주의! 암호문과 정답은 계속 달라집니다)
어렵지 않은 전치암호인데요, 마침표가 등장하는 첫 번째 문장을 봅시다.
oWdnreuf.lY
문자의 구성들을 보면 단어를 유추할 수 있는데요, Wonderful 이라는 단어가 떠오르네요.
Wonderful이라는 단어를 만들기 위한 복호화키를 생각해보면, 두 글자씩 서로 자리를 바꿔야된다는 것을 금방 알 수 있습니다.
그렇다면 암호화 키와 복호화 키는 둘 다 21이 되겠네요!
'''
WeChall
Training: Crypto - Transposition I
'''
def solve(chip, key):
chip = list(chip)
key = list(key)
mes = ''
for i in range(len(chip)+len(key)):
try:
mes += chip[int(key[i%len(key)])+(i/len(key))*len(key)-1]
except :
pass
return mes
if __name__ == '__main__':
print "===Transposition==="
print "> Input Chiper message"
chip = raw_input('> ')
print "> Input decrypt key"
key = raw_input('> ')
print "[*] "+solve(chip,key)
짧은 키 길이 덕분에 암호화키와 복호화키를 금방 알 수 있었습니다!
2. 전치 암호 : 스키탈리
이전 편에서 스키탈리 암호가 잠깐 등장했는데요, 스키탈리(스키테일, 스키탈레 등 등..) 암호 역시 전치 암호에 속합니다.
# 출처 : 위키백과 ‘스키테일’
스키탈리 암호는 스파르타에서 전쟁터에 나가있는 아군과 소통할 때 사용한 암호입니다.
위 그림에 보이는 원통형의 나무 막대를 스키탈리라고 하고, 이 막대에 양피지를 감아 메세지를 적고 펼치면 암호문이 만들어집니다.
펼쳐진 양피지를 다시 같은 굵기의 스키탈리에 감으면 평문을 읽을 수 있게 됩니다.
스키탈리 암호에서 암호키와 복호키는 스키탈리의 굵기가 되겠네요~
재밌어보이는데 직접 만들어 볼까요??
손으로 만들긴 귀찮으니 컴퓨터에게 시킵시다!
스키탈리 암호를 해독해주는 코드입니다~
'''
Transposition Chiper
Scytale Decrypt Service
'''
#-*- coding: utf-8 -*-
def solve(pigi, key, w):
key = int(key)
pigi = list(pigi)
mes = ''
for i in range((len(pigi)+key-1)/key):
tmp = i*key+w%key
if tmp < len(pigi):
mes += pigi[tmp]
return mes
def scytale(mes):
line1 = " ┌─┐"
line2 = "┌─┤ ├"
line3 = "│ │"
line4 = "└─"
line5 = ""
for i in range(len(mes)):
if i < len(mes)-1:
line2 += "─┬"
line4 += "┴─"
line3 += mes[i:i+1]
line3 += "│"
line5 += " "
line2 += "─┐"
line3 += " │"
line4 += "┤ ├─┘"
line5 += "└─┘"
print line1
print line2
print line3
print line4
print line5
if __name__ == "__main__":
print "===Scytale Service==="
print "[*] Input Crypto MSG"
pigi = raw_input('> ')
print "[*] Input Key"
key = raw_input('> ')
select = ''
w = 0
while True:
if len(pigi) < int(key):
print "Invalid key"
break
mes = solve(pigi, key, w)
scytale(mes)
print "1) wheel up [w]"
print "2) quit [q]"
select = raw_input('> ')
if select == 'w':
w += 1
elif select == 'q':
break
else:
print "Wrong Select"
-- coding: utf-8 --
파이썬의 기본 인코딩은 ascii 인데요,
미관을 위해 특수문자를 출력할 것이기 때문에 인코딩이 utf-8이라고 알려주는 코드를 꼭 추가해주어야 합니다.
핵심이 되는 코드는 solve 함수의 for문 입니다.
코드에 대한 설명을 드리자면,
전체 문장을 키 값만큼의 길이를 갖도록 블록으로 나누고,
각 블록에서 0번째 값들을 차례로 모두 이어붙이고, 이어서 1번째 값들을 모두 이어붙이는 형태입니다.
[Slc i] [cerSc] [y yee] [tDpr ] [aetv]
0 번째 값들 : Scyta
1 번째 값들 : le De
2 번째 값들 : crypt
3 번째 값들 : Serv
4 번째 값들 : ice
이것을 합치면 평문!
이제 확인해볼까요? :)
이 프로그램에서의 키 값은 스키탈리의 한 지름에 들어갈 수 있는 문자 수를 의미하도록 했습니다.
암호문을 입력하고 키를 입력하면 스키탈리에 암호문을 감아 평문을 볼 수 있습니다.
키를 모를 땐 어떻게 할까요??
키 값을 증가시켜가며 알아볼 수 있는 평문이 나올 때까지 반복하면 될 것 같습니다.
그럼, 다음의 스키탈리 암호문을 해독 해 볼까요? :)
스키탈리 암호 해독 해보기
hmiien jmieyso nnmga
3. 전치 암호 : Rail Fence
Rail Fence 암호는 깊이에 따라 지그재그 모양으로 문자를 배치하여 암호화합니다.
여기서 암호화 키와 복호화 키는 깊이가 됩니다.
- 깊이가 2인 Rail Fence 암호
- 깊이가 3인 Rail Fence 암호
암호화 방법은 위처럼 그림을 그리면 쉽게 할 수 있는데요, 복호화 방법은 어떻게 될까요?
깊이가 3인 Rail Fence 암호
RFep | alecCyt | inro
깊이가 3이면 3개의 블록으로 나눌 수 있습니다.
앞에서 부터 첫 번째, 두 번째, 세 번째 블록이라고 하면
1 > 2 > 3 > 2 의 순서를 반복하여 알파벳을 하나씩 가져오면 됩니다.
여기서 주의해야 할 것은 각 블록의 크기입니다.
막상 코드를 짜려고 보니 각 블록의 크기를 계산해야 했습니다.
저는 수학 공식을 짜보려고 노력했지만.. 결국 암호화 순서를 따라가며 각 블록의 크기를 카운트했습니다.
아래는 그것을 적용한 복호화 코드입니다!
'''
Transposition Chiper
Rail Fence Decrypt Service
'''
def solve(crypt, key):
key = int(key)
crypt = list(crypt)
mes = ''
index = []
block_size = []
idx = 0
flag = 0
# block size
for i in range(len(crypt)):
if len(block_size) <= idx:
block_size.append(1)
else:
block_size[idx] += 1
if idx == key-1:
flag = 1
elif idx == 0:
flag = 0
if flag == 1:
idx -= 1
elif flag == 0:
idx += 1
# set index
for i in range(len(block_size)):
index.append(0)
idx = 0
flag = 0
# get char
for i in range(len(crypt)):
tmp = 0
for j in range(idx):
tmp += block_size[j]
tmp += index[idx]
index[idx] += 1
mes += crypt[tmp]
if idx == key-1:
flag = 1
elif idx == 0:
flag = 0
if flag == 1:
idx -= 1
elif flag == 0:
idx += 1
return mes
if __name__ == "__main__":
print "===Rail Fence Service==="
print "[*] Input Crypto MSG"
crypt = raw_input('> ')
print "[*] Input Key"
key = raw_input('> ')
print "[*] "+solve(crypt,key)
다시 두 번째 블록으로 돌아가야 하는데, 이것을 나타내는 값이 flag 변수의 값입니다.
증가해야할 타이밍인지 감소해야할 타이밍인지를 알려주는 값입니다.
잘 작동 하네요~
키 값인 깊이를 모른다면 어떻게 할까요??
스키탈리 암호의 경우처럼 깊이를 점차 늘려가며 알아볼 수 있는 평문이 나올 때까지 반복하면 될 것 같습니다.
마무리
단순 전치 암호
문자의 순서를 재배치
암호문과 평문이 같은 문자 조합을 갖는다.
예시 : 스키탈리
스키탈리라는 원통을 이용한 암호화 및 복호화
스키탈리의 두께를 알면 복호화가 가능하다.
* 예시 : Rail Fence
깊이 만큼 지그재그로 문자를 배치하여 암호화
깊이를 알면 복호화가 가능하다.
지금까지 고전 암호 중 전치 암호에 대해 알아봤습니다.
과거의 치환/전치 암호들은 해독이 쉬웠지만 점차 암호화 방식이 복잡해지며 해독을 어렵게하는 방식들이 나타났습니다.
한 가지 방법을 여러번 시도하여 암호화 하거나 여러가지 방법을 섞어 암호화하는 방식들도 있는데요,
암호에 대해 좀 더 내공이 쌓인다면 또 둘러보도록 합시다:D