DES의 개념
- 64bit 평문을 64bit 암호문으로 암호화하는
대표적인 비밀키 방식의 대칭 암호 알고리즘
- 암호화/복호화할 때 쓰는 비밀키가 동일함
DES 암호화 방식
평문을 64비트로 나눠 56비트의 키로
다시 64비트의 암호문을 만들어내는 알고리즘
DES는 Feistel Cipher 방식으로 암호화를 한다.
Feistel Cipher 방식이란
데이터를 좌 우로 나누어 교대로 비선형 변환을 적용하는 구조
장점으로는 누군가 키를 알아내도 원래의 함수를 알아내지 못해 보안성이 좋다.
구현
DES.py
from Crypto.Cipher import DES
from Crypto.Hash import SHA256 as SHA
class myDES():
# DES 초기화
def __init__(self, keytext, ivtext):
hash = SHA.new()
hash.update(keytext.encode('utf-8'))
# keytext를 해시화했을 때 첫 8byte를 key로 함
key = hash.digest()
self.key = key[:8]
hash.update(ivtext.encode('utf-8'))
iv=hash.digest()
# ivtext를 해시화햇을때 첫 8byte를 iv로 함
self.iv=iv[:8]
# ECB 모드로 암호화
def encrypt_ECB(self, plaintext):
# 항상 8byte 단위로 끊어서 암호화하기 때문에 평문이 8byte로 끊기지 않는다면
# padding값을 추가해 8byte로 만들어줌
while(len(plaintext) % 8 != 0):
plaintext += ' '
des=DES.new(self.key,DES.MODE_ECB)
encryptMsg = des.encrypt(plaintext.encode())
return encryptMsg
# ECB 모드로 암호화된 암호문을 복호화
def decrypt_ECB(self,ciphertext):
des = DES.new(self.key, DES.MODE_ECB)
descryptMsg = des.decrypt(ciphertext)
return descryptMsg
# CBC 모드로 암호화
def encrypt_CBC(self, plaintext):
while(len(plaintext) % 8 != 0):
plaintext += ' '
# CBC 모드에서는 iv(초기화 벡터) 값이 필요
des=DES.new(self.key,DES.MODE_CBC,self.iv)
encryptMsg = des.encrypt(plaintext.encode())
return encryptMsg
# CBC 모드로 암호화된 암호문을 복호화
def decrypt_CBC(self,ciphertext):
des = DES.new(self.key, DES.MODE_CBC,self.iv)
descryptMsg = des.decrypt(ciphertext)
return descryptMsg
DES의 초기화와 암호화, 복호화 함수를 담아둔 클래스이다.
중요하게 봐야 할 점은 CBC모드에서는 iv(초기화벡터)가 필요하다는 점
8byte 단위로 암호화를 하기 때문에 평문이 8byte로 끝나지 않는다면
억지로 padding을 추가해 8byte로 맞춰준다는 것이다.
Send.py
import socketserver
from DES import myDES
from os.path import exists
HOST = ''
PORT = 8081
# 임의의 키와 IV를 만듦
DESKEY = 'eightkey'
DESIV = '1234'
# 들어오는 요청을 처리하는 핸들러 클래스를 재정의한 요청처리기
class MyTcpHandler(socketserver.BaseRequestHandler):
def handle(self):
data_transferred = 0
print('[%s]연결됨' % self.client_address[0])
# 1024 바이트 받기
filename = self.request.recv(1024)
filename = filename.decode()
if not exists(filename):
return
print('파일 [%s] 전송 시작..' % filename)
#키와 IV 값을 클라이언트에게 보냄
self.request.send(DESKEY.encode('utf-8'))
self.request.send(DESIV.encode('utf-8')) # ECB 모드면 삭제
with open(filename,'r') as file:
try:
line = file.readline()
des = myDES(DESKEY,DESIV)
enc = des.encrypt_CBC(line)
data_transferred += self.request.send(enc)
except Exception as e:
print(e)
print('암호화된 텍스트 : %s\n',enc)
print('\n')
print('\n')
print('전송 완료[%s],전송량[%d]' % (filename, data_transferred))
def runServer():
print('파일 서버 시작')
try:
server = socketserver.TCPServer((HOST, PORT), MyTcpHandler)
# Ctrl + C 하기 전까진 계속 서버 실행
server.serve_forever()
except KeyboardInterrupt:
print('파일 서버 종료')
runServer()
서버이자 파일을 보내주는 send.py이다.
임의의 키와 iv값을 만들어둔 뒤,
클라이언트로부터 요청을 받으면 해당 파일이름이 있는지 확인한다.
이후 있으면 키와 iv를 클라이언트에게 보내준다.
보내줄 때 위에 DES Class로 키와 iv를 이용하여 평문을 암호화하여 보내준다.
Receive.py
import socket
from DES import myDES
HOST = 'localhost'
PORT = 8081
# 서버로부터 파일을 받아오는 함수(파일 이름)
def getFileFromServer(filename):
data_transferred = 0
#IP_4 Version으로 TCP방식으로 설정
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as sock:
sock.connect((HOST,PORT)) # 해당 포트와 호스트로 연결
sock.sendall(filename.encode()) # 파일이름을 인코딩(바이트화)해서 서버로 보냄
key = sock.recv(8) # Key는 8byte이므로 8byte만 받아옴
# ECB모드라면 iv는 필요없으므로 해당 코드 삭제-----------------
iv = sock.recv(8) # iv도 마찬가지
data = sock.recv(1024) # 데이터는 1024byte씩 받아오면됨(해당 테스트에선 1줄이어서 한번만)
if not data:
print('파일[%s]: 서버에 존재하지 않거나 오류 발생'%filename)
return
# 복호화가 제대로 됐는지 확인하기 위해 파일로 저장
with open(filename+'.des-CBC','wb') as f:
try:
# 해당 키와 iv로 복호화
des = myDES(key.decode(),iv.decode()) # EBC 모드라면 iv쪽을 ''로 비워둠
# 데이터를 다 읽을때까지 읽어들여 데이터를 복호화
while data:
des = des.decrypt_CBC(data)
f.write(des)
data_transferred +=len(data)
data = sock.recv(1024)
except Exception as e:
print(e)
print('파일 [%s] 전송 종료. 전송량 [%d]' %(filename, data_transferred))
getFileFromServer('example.txt')
클라이언트 파일이자 파일을 받는 receive 파일이다.
파일을 요청하고 파일을 만드는 역할을 한다.
파일을 요청한 뒤 파일이 있으면 서버로부터 키와 iv를 받는다.
이후 서버로부터 암호문 데이터를 받아 키와 데이터를 이용하여 복호화한다.
그리고 이를 파일에 적어 파일화 한다.
해당 example.txt 파일을 클라이언트가 서버에게 요청했다고 가정해 보자.
이를 두 개의 터미널을 이용해서 실행하면 이런 결과가 나온다.
연결된 아이피와 전송된 파일, 그리고 암호화된 텍스트를 확인할 수 있다.
그리고 전송량 또한 알 수 있었다.
그리고 복호화가 제대로 된 지 확인하기 위해
만들어둔 파일에 제대로 복호화가 된 것을 확인할 수 있다.