현대 암호학

[현대 암호학] 06. 실습(1)

Uggjjini 2021. 6. 28. 17:30

[실습] RSA 공개키/개인키 생성하기

RSA 공개키 암호 구현하기

공개키/개인키가 생성되는 과정을 구현해 보자.

 

 

PyCryptodome에서 제공되는 대표적인 공개키 암호 알고리즘 : DSA, ElGamal, RSA, ECDSA

  • DSA, ECDSA : 공개키 서명
  • Elgamal, RSA : 공개키 암호 + 공개키 서명

 

 

PyCryptodome 키 객체 생성 방법 - 4가지

  • 비대칭 키는 python 객체로 표시된다.
  • 각 객체는 개인키 또는 공개키가 될수 있다.

 

(1st) generate() : Crypto.PublicKey.RSA.generate()

키가 무작위로 생성된다.(ex: RSA.generate(2048))

(2nd) import_key() : Crypto.PublicKey.RSA.import_key()

키를 메모리에 load 한다.

(3rd) construct() : Crypto.PublicKey.RSA.construct()

키를 일련의 하위 구성 요소로 구성된다.

(4th) publickey() : Crypto.PublicKey.RSA.RsaKey.publickey()

키는 주어진 객체와 일치하는 공개 키이다.

 

 

RSA Key 생성 형식 예제

private key를 생성하고, 생성된 private key를 이용하여 public key 생성하기

from Crypto.PublicKey import RSA

prikey = RSA.generate(2048)
print(prikey)

pubkey = prikey.publickey()
print(pubkey)
Private RSA key at 0x7F3DF4D03A90
Public RSA key at 0x7F3DF50A0880

 

 

 

RSA Key File(Private Key) 생성 형식 예제

 

키 웨핑 포멧(key wrapping format)

  • PEM(default) : Text encoding(RFC 1421, RFC 1423)
  • DER : Binary encoding
  • OpenSSH : Tetual encoding(OpenSSH에서만 사용, public key만(private key 아님))
from Crypto.PublicKey import RSA

prikey = RSA.generate(2048)
print(prikey)

content = prikey.export_key('PEM')
fd1 = open('mykey.pem', 'wb')
fd1.write(content)
fd1.close()

fd2 = open('mykey.pem')
content2 = fd2.read()
prikey2 = RSA.import_key(content2)
print(prikey2)

 

Private RSA key at 0x7F6C5AB5BCA0
Private RSA key at 0x7F6C5AB630A0

 

  • import_key() : RSA 키를 import 한다. (ex: PEM format -> key)
  • importKey() : RSA 키를 import 한다. (ex: PEM format -> key)
  • export_key() : RSA 키를 export 한다. (ex: key -> PEM format)
  • exportKey() : RSA 키를 export 한다. (ex: key -> PEM format)
  • 따라서, import_key() == importKey, export_key() == exportKey

 

 

RSA Key File(Public Key) 생성 형식 예제

from Crypto.PublicKey import RSA

prikey = RSA.generate(2048)
print(prikey)

pubkey = prikey.publickey()
fd1 = open('mykey.pem', 'wb')
fd1.write(pubkey.exportKey('PEM'))
fd1.close()

fd2 = open('mykey.pem')
pubkey = RSA.import_key(fd2.read())
print(pubkey)

 

Private RSA key at 0x7FCDF70DBDF0
Public RSA key at 0x7FCDF70E51F0

 

 

RSA(PKCS1_OAEP)

 

PKCS1_OAEP(RSA_OAEP)

  • PKCS(Public Key Crytography Standard)는 공개키 암호 표준으로 RSA Security라는 회사에서 정한 공개키 암호의 사용방식에 대한 표준 프로토콜이다.
  • PKCS는 그 기능이나 특성에 따라서 PKCS#1 ~ PKCS#15까지로 구분한다. 일반적으로 공개키 암호 표준이라고 하면 PKCS#1을 의미한다.
  • OAEP(Optimal Asymmetric Encryption Padding)는 RSA와 함께 사용되며, 암호화를 하기 전 메시지에 패딩(Padding)이라 부르는 랜덤값을 추가하여 RSA 암호화를 수행한다.

 

RSATest.py

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

def main():
    mymsg = b'samsjang loves python'
    myprikey = RSA.generate(1024)
    mypubkey = myprikey.publickey()
    print(myprikey,mypubkey)


    myencmsg = rsa_enc(mymsg,mypubkey)
    mydecrypted = rsa_dec(myencmsg, myprikey)

    print("Plaintext : ", mymsg.decode())
    print("Encrypted : ", myencmsg)
    print("Decrypted : ", mydecrypted.decode())

def rsa_dec(msg, prikey):
    # input : b'msg'(encrypted msg), int(prikey)
    # output: decrypted msg(decmsg)
    cipher = PKCS1_OAEP.new(prikey)
    decmsg = cipher.decrypt(msg)

    return decmsg

def rsa_enc(msg, pubkey):
    # input : b'msg', int(pubkey)
    # output: encrypted msg(encrypted),
    # functions:
    #   * RSA-OAEP
    #       - prikey, pubkey
    cipher = PKCS1_OAEP.new(pubkey)
    encrypted = cipher.encrypt(msg)

    return encrypted


if __name__ == '__main__':
    main()
Private RSA key at 0x7FC727802A30 Public RSA key at 0x7FC72938D100
Plaintext :  samsjang loves python
Encrypted :  b"M\xb5t\xdfR\xe7\xcc\x9e\x8f1\xf5)\xa9Q\xda\x04\xb1\x9d\xe4\xc4\r\xa9c\x95\xd.........
Decrypted :  samsjang loves python

 

 


[실습] RSA 이용한 메시지 암호화/복호화

 

RSA 이용한 메시지 암호화/복호화

() RSA 개인키/공개키를 만들고

() 개인키/공개키 파일(PEM 형식)을 만든 후

() 메시지를 암복호화 한다.

 

 

RSAPrivatePublic.py

import os
import sys

from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA



def main():
    mymsg = b'samsjang loves python'

    myprikey, mypubkey = createPriPubkey()
    myprikeyfile, mypubkeyfile = createPEM(myprikey, mypubkey)

    myencmsg = rsa_enc(mymsg, mypubkeyfile)
    mydecmsg = rsa_dec(myencmsg, myprikeyfile)

    print("Plaintext : |%s|" % mymsg.decode())
    print("Encrypted : |%s|" % myencmsg)
    print("Decrypted : |%s|" % mydecmsg.decode())

def rsa_dec(msg, prifile):
    prikey = readPEM(prifile)
    cipher = PKCS1_OAEP.new(prikey)
    decrypted = cipher.decrypt(msg)

    return  decrypted


def rsa_enc(msg, pubfile):
    # input : b'msg', PEM file(pubfile)
    # output: encrypted msg(encrypted)
    # functions:
    #   * PEM file(pubfile) -> pubkey
    #   * msg -> Encryption -> encrypted msg(encrypted)
    pubkey = readPEM(pubfile)
    cipher = PKCS1_OAEP.new(pubkey)
    encrypted = cipher.encrypt(msg)

    return encrypted


def readPEM(file):
    # input : PEM file(private|public key file)
    # output: key(private key|public key)
    # functions:
    fd = open(file)
    key = RSA.import_key(fd.read())

    return key

def createPEM(prikey, pubkey):
    # input : int(prikey), int(Pubkey)
    # output: PEM Format file(private.pem), PEM Format file(Publickey.pem)
    # functions:
    #   * int(prikey) --> PEM File(Private.pem)
    #   * int(pubkey) --> PEM File(public.pem)
    prikeyfile = 'private.pem'
    pubkeyfile = 'public.pem'
    if not os.path.exists(prikeyfile) or not os.path.exists(pubkeyfile):
        fd1 = open(prikeyfile, 'wb')
        fd1.write(prikey.export_key('PEM'))
        fd1.close()
        print("[  OK  ] %s created." % prikeyfile)

        fd2 = open(pubkeyfile, 'wb')
        fd2.write(pubkey.export_key('PEM'))
        fd2.close()
        print("[  OK  ] %s created." % pubkeyfile)

    return prikeyfile, pubkeyfile


def createPriPubkey():
    # input : None
    # output: prikey, pubkey
    prikey = RSA.generate(2048)
    pubkey = prikey.publickey()

    return prikey, pubkey


if __name__ == '__main__':
    main()

 

Plaintext : |samsjang loves python|
Encrypted : |b'0u>\\\xb8\xab.\x00\xea ...... \xb8~\nJ>\xd8\xf1\xdb\xfe\xbd'|
Decrypted : |samsjang loves python|

 

 


[실습] RSA 공개키 서명 구현하기

  • 공개키 암호(암호화: public key, 복호화: private key) : RSA
  • 공개키 서명(암호화: private key, 복호화: public key) : RSA, DSA

 

RSA 공개키 서명 구현하기

  • RSA 공개키 서명을 구현 해 보자.
  • (준비) 이전 실습에서 사용했던 privatekey.pem, publickey.pem 파일이 필요하다.

 

공개키 서명 대해서

공개키 서명은 사용자의 개인키로 서명하고, 암호화한 정보를 사용자의 공개키로 확인하여 해당 정보를 보낸 사람이 당사자인지를 확인하는 방법이다.

 

 

아래 코드에 대한 설명

  • msg에 대한 SHA256 해시를 구하고, 이 값에 개인키를 이용하여 생성한 pkcs1_15 객체의 sign() 함수로 서명하고, 그 결과값을 상대방에게 전단한다.
  • rsa_sign() 함수는 개인 서명 과정을 나타낸다.
  • 서명을 확인하는 쪽에서는 확인 해야 할 msg를 이미 알고 있고, 개인키에 대응되는 공개키를 가지고 있다고 가정한다.
  • 서명 확인자는 개인키로 서명한 정보를 네트워크를 통해 전달바고, msg의 SHA256 해시를 구한다.
  • 공개키를 이용해 생성한 pkcs1_15 객체 verify() 함수의 인자로 해시값과 서명이 된 정보를 입력한다.
  • rsa_verify() 함수가 서명 확인 과정을 나타낸다.
  • 개인키로 서명이 된 정보와 해시가 일치하고, 공개키에 의해 확인이 되면, 예외가 발생하지 않으며, 확인이 불가하면 예외가 발생한다.

 

 

RSASignature.py

import os
import sys

from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256 as SHA
from Crypto.Signature import pkcs1_15


def main():
    # Sender Side
    mymsg = b'My name is samsjang'
    mypubkey, mysignature = rsa_sign(mymsg)

    # Recv Side
    recv_msg = mymsg
    recv_pubkey = mypubkey
    recv_sign = mysignature
    rsa_verify(recv_msg, recv_pubkey, recv_sign)

def rsa_verify(msg, pubkey, sign):
    hash = SHA.new(msg)

    try:
        pkcs1_15.new(pubkey).verify(hash, sign)
        print("Authentic")
    except Exception as e:
        print("Error: ", e)
        print("Not Authentic")


def rsa_sign(msg):
    # input : b'msg'
    # output: int(pubkey), sign(signature)
    # functions:
    #   * b'msg' -> Encryption(prikey, sign()) -> signature
    #   * return signature, pubkey
    prikeyfile = 'private.pem'
    prikey = readPEM(prikeyfile)
    pubkey= prikey.publickey()

    hash = SHA.new(msg)
    signature = pkcs1_15.new(prikey).sign(hash)

    return pubkey, signature



def readPEM(file):
    # input : PEM file(pri pem file|pub pem file)
    # output: key(private key|public key)
    # functions:
    fd = open(file)
    key = RSA.import_key(fd.read())

    return key


if __name__ == '__main__':
    main()