호우동의 개발일지

Today :

article thumbnail

썸네일

 


RSA의 개념

- 공개키 암호 시스템의 하나로, 공개키와 비밀키 두 가지의 키를 사용한다.
- 전자서명 기능을 요구하는 전자 상거래 등 광범위하게 사용됨

 


RSA 암호화 방식

공개키 : 모두에게 알려져 있으며 암호화하는 데 사용
비밀키 : 단 한 사람만 가지고 있고 해독할 때 사용

-> 누구나 메시지를 암호화할 수 있지만 해독할 수 있는 사람은 개인키를 지닌 사람뿐

 


구현

import socketserver
from os.path import exists
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

HOST = ''
PORT = 3333

# 들어오는 요청을 처리하는 핸들러 클래스를 재정의한 요청처리기
class MyTcpHandler(socketserver.BaseRequestHandler):
    def handle(self):
        print('[%s]연결됨' % self.client_address[0])
        # 1024 바이트 받기
        file = self.request.recv(1024)
        file = file.decode()

        if not exists(file):
            return

        print('파일 [%s] 전송 시작..' % file)
        
        # Key를 만들고 전송함.
        rand = Random.new().read
        rsa = RSA.generate(1024, rand)
    
        prKey = rsa.exportKey()
        with open("./private.pem","wb")as f:
            try : 
                f.write(prKey)

            except Exception as e:
                print(e)
        
        pbKey = rsa.publickey().exportKey()
        self.request.send(pbKey)
        
        with open(file,'r') as file:
            try:
                line = file.readline()
                self.request.send(('\n'+line).encode('utf-8'))
            except Exception as e:
                print(e)
                
        print('전송 완료[%s]',file)
        
        rsaText = self.request.recv(1024)
        # 비밀키를 가져옴
        prKey = RSA.importKey(open("private.pem").read())
        cipher = PKCS1_v1_5.new(prKey)
        # 비밀키로 암호문을 해독
        text = cipher.decrypt(base64.b64decode(rsaText)," ")
        print(text.decode('utf-8'))
          
def runServer():
    print('파일 서버 시작')
    try:
        server = socketserver.TCPServer((HOST, PORT), MyTcpHandler)

        server.serve_forever()
    except KeyboardInterrupt:
         print('파일 서버 종료')            
runServer()

server 파일이자 파일을 보내는 send 파일이다.
소켓을 통해 네트워킹을 한다. 

랜덤함수를 통해 비밀키와 공개키를 랜덤 생성하고 이를 파일로 저장한다.

Client가 파일을 요청하면 공개키를 먼저 보내주고
파일의 내용을 읽어 이를 요청한 Client에게 보내준다.

이후 Client가 파일을 공개키로 암호화한 암호문을 서버를 통해 보내주면
그 파일을 개인키로 복호화한다.

import socket
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

HOST = 'localhost'
PORT = 3333

def getFileFromServer(file):
    data_transferred = 0

    with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as sock:
        sock.connect((HOST,PORT))
        sock.sendall(file.encode())

        # 통신을 통해 공개키를 받아옴
        public_key = sock.recv(1024)
        print('public key : %s\n' %public_key)
        

        # 데이터 파일을 받아옴
        data = sock.recv(1024)
        if not data:
            print('파일[%s]: 서버에 존재하지 않거나 오류. 발생'%file)
            return

        # 파일이름.RSA로 저장
        with open(file+'.RSA','wb') as f:
            try:
                # 공개키로 RSA 암호화
                rsaKey = RSA.importKey(public_key.decode())
                cipher = PKCS1_v1_5.new(rsaKey)
                
                while data:
                    rsaText = base64.b64encode(cipher.encrypt(data))
                    f.write(rsaText)
                    data_transferred +=len(rsaText)
                    # 
                    sock.sendall(rsaText)
                    data = sock.recv(1024)

            except Exception as e:
                print(e)

    print('파일 [%s] 전송 종료',file)
            
        
getFileFromServer('example.txt')

클라이언트 파일이자, 파일을 받는 receive 파일이다.

포트는 서버와 동일하게 3333으로 통일시켜 주고
먼저 파일을 요청하자마자 공개키를 받는다.

그리고 데이터 파일을 모두 받아온 다음 파일을 쓴다.

공개키로 데이터를 암호화 한 뒤 다시 서버로 보낸다.

평문
평문
RSA로 암호화된 텍스트
RSA로 암호화된 텍스트
파일로 저장된 비밀키
파일로 저장된 비밀키(private.pem)
결과

위는 client 실행 결과
아래는 server의 실행결과창이다.

위는 정상적으로 복호화된 모습
아래는 공개키의 내용이다.

정상으로 복호화된 모습을 볼 수 있다.