Study Record

[암호 프로그래밍] 하이브리드 암호 프로그램 본문

암호/프로그래밍

[암호 프로그래밍] 하이브리드 암호 프로그램

초코초코초코 2021. 12. 10. 11:40
728x90

1. 작업 절차

1-1. 사전준비

Server 는 공개키 암호(RSA_OAEP)에서 사용할 privateKey 와 publicKey 를 새로 생성하거나 저장된 파일에서 가져온다.

 

1-2. 작업 과정

 

① Server 는 Client 에게 public key 를 전송한다.

② Client 는 메시지 암/복호화에 사용할 대칭키를 만든다.

③ Client 는 난수 값(none) 과 대칭키를 이용하여 메시지(m)를 암호화(AES_CTR_E)하여 암호문(Msg_C)을 생성한다.

④ Client 는 Server 에게 보낼 대칭키를 Server 에게 받은 public key 를 이용하여 암호화(RSA_OAEP_E)하여 암호화한 대칭 키(Key_C)를 생성한다.

⑤ Client 는 난수 값(none), 암호문(Msg_C), 암호화한 대칭 키(Key_C) , Server 에 저장할 파일 이름(fileName) 을 전송한다.

⑥ Server 는 암호화한 대칭 키(Key_C)를 private key 를 이용하여 복호화(RSA_OAEP_D)하여 대칭키를 얻는다.

⑦ Server 는 ⑥ 에서 얻은 대칭키와 Client 에서 받은 난수 값(none)를 이용하여 암호문(Msg_C)을 복호화(AES_CTR_D)하여 원래의 메시지(m)를 얻는다.

⑧ Server 는 Client 에서 받은 저장할 파일이름(fileName)을 파일이름으로 원래의 메시지(m)를 저장한다.

 

2. 개발 코드 - 파이썬

2-1. Server

# 작업 절차
# 0) prikey, pubkey 생성한다.
# 1) 소켓을 생성한다.
# 2) 클라이언트가 접속이 되면, 미리 만들어진 prikey, pubkey 중 pubkey 전송한다.
# 3) 클라이언트가 "msgKey || nonce || encMsg || filename" 전송하면 복호화 동작
#   * 분리 : msgKey || nonce || encMsg || filename
#   * 세션키 복호화 (K1 = D(K2(pri), encMsg))
#   * 메시지 복호화(P = D(K1, C1))
#   * 파일(filename)에 평문 저장(plaintext file)
import json
import socket
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import sys
import os
from base64 import b64decode, b64encode
from Crypto.Cipher import AES


def createKey(priPemFile, pubPemFile):
    # priKey.pem , pubKey.pem
    if not (os.path.exists(priPemFile)) or not (os.path.exists(pubPemFile)):
        prikey = RSA.generate(1024)
        pubkey = prikey.publickey()
        with open(priPemFile, 'wb') as priFile:
            priFile.write(prikey.export_key('PEM'))

        with open(pubPemFile, 'wb') as pubFile:
            pubFile.write(pubkey.export_key('PEM'))

    return readPem(priPemFile), readPem(pubPemFile)


def readPem(f):
    readFile = open(f, 'rb')
    key = readFile.read()
    readFile.close()
    return key


def rsa_dec(encMsg, private_key):
    return PKCS1_OAEP.new(private_key).decrypt(encMsg)


def aes_ctr_dec(encMsg, nonce, key):
    return AES.new(key, AES.MODE_CTR, nonce=nonce).decrypt(encMsg)


def fileSave(filename, msg):
    with open(filename, 'wb') as f:
        f.write(msg)


def main():
    host = '127.0.0.1'
    port = 4444
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        server.bind((host, port))
        server.listen(1)
        client, address = server.accept()
    except Exception as e:
        error = "Error : " + str(e)
        sys.exit(error)
    else:
        print("Connected by", address)

        # publicKey, privateKey create
        priPemFile = 'prikey.pem'
        pubPemFile = 'pubkey.pem'

        priKey, pubKey = createKey(priPemFile, pubPemFile)

        # send pubKey
        sendData = pubKey + b' end'
        client.sendall(sendData)

        # receive message
        recv_msg = b''
        while True:
            data = client.recv(1024)
            if data.find(b' end') != -1:
                recv_msg += data
                break
            recv_msg += data

        # 메시지 분리
        message = recv_msg[:-4]
        dicMessage = json.loads(message.decode())
        encKey = b64decode(dicMessage['key'])
        nonce = b64decode(dicMessage['nonce'])
        encMsg = b64decode(dicMessage['encMsg'])
        filename = b64decode(dicMessage['filename']).decode()

        # 세션키 복호화
        deckey = rsa_dec(encKey, RSA.import_key(priKey))

        # 메시지 복호화
        decMsg = aes_ctr_dec(encMsg, nonce, deckey)

        # 파일에 plaintext 저장하기
        fileSave(filename, decMsg)

        print(filename + "에 복호화된 메시지가 저장되었습니다.")
        print("복호화된 메시지 : ")
        print(decMsg.decode())
    finally:
        server.close()


if __name__ == '__main__':
    main()

 

2-2. Client

import socket
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import sys
import os
from base64 import b64decode, b64encode
from Crypto.Cipher import AES
import json
from Crypto.Random import get_random_bytes


def aes_ctr_enc(msg):
    key = get_random_bytes(16)
    cipher = AES.new(key, AES.MODE_CTR)
    nonce = cipher.nonce
    endMsg = cipher.encrypt(msg)
    return key, nonce, endMsg


def rsa_enc(msg, pubkey):
    return PKCS1_OAEP.new(pubkey).encrypt(msg)


def rsa_dec(encMsg, private_key):
    return PKCS1_OAEP.new(private_key).decrypt(encMsg)


def getFileContent(filename):
    if os.path.exists(filename):
        fd = open(filename, 'rb')
        data = fd.read()
        fd.close()
    else:
        error = "Error : " + filename + " not found"
        sys.exit(error)
    return data


def main():
    host = '127.0.0.1'
    port = 4444

    myfileName = b'/test/plaintext.txt'
    sendFilename = b"/test/plaintext2.txt"

    # 전송할 메시지 가져오기
    msg = getFileContent(myfileName)

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        client.connect((host, port))
    except Exception as e:
        error = "Error : " + str(e)
        sys.exit(error)
    else:
        # pubKey 서버에서 받아오기
        recv_msg = b''
        while True:
            data = client.recv(1024)
            if data.find(b' end') != -1:
                recv_msg += data
                break
            recv_msg += data

        pubkey = RSA.import_key(recv_msg[:-4])

        # 메시지 암호화
        msgKey, nonce, encMsg = aes_ctr_enc(msg)

        # 서버에 전달할 데이터 만들기
        #  * session key encrypt
        sendData = {'key': b64encode(rsa_enc(msgKey, pubkey)).decode(), 'nonce': b64encode(nonce).decode(),
                    'encMsg': b64encode(encMsg).decode(), 'filename': b64encode(sendFilename).decode()}
        sendEncData = json.dumps(sendData)

        # 서버에 데이터 전달하기
        client.sendall(sendEncData.encode() + b' end')

        print("성공적으로 내용을 전달하였습니다.")
    finally:
        client.close()


if __name__ == '__main__':
    main()
728x90