[목차]
1. RSA PKCS 이론
2. RSA PKCS1_OAEP 실습
3. RSA를 이용한 메시지 암호화/복호화
4. RSA 공개키 서명 구현하기
5. ECDSA 사용한 전자서명 구현하기
[RSA PKCS 이론]
(출처) https://www.nexpert.net/438
PKCS (Public-Key Cryptography Standard)의 개요
PKCS는 공개키 기반 구조 (PKI, Public Key Infrastructure)에서 인터넷을 이용해 안전하게 정보를 교환하기 위한 제조사간 비공식 표준 프로토콜로 미국의 RSA가 개발한 암호 작성 시스템입니다. PKCS는 애플, 마이크로소프트, DEC 등의 회사에서 공동 개발하였지만, IETF에서 RFC 로 받아들이면서 공식적인 표준으로 인정하고 있습니다.
PKCS세부 항목
PKCS에서 다루고 있는 Credential은 정보 시스템에서 암호화된 개인정보로 개인의 공개키 암호 알고리즘을 위한 공개키 및 개인키 쌍, 공개키 인증서, CA 관련 정보, 패스워드 등을 포함하는 암호 정보의 총합입니다. 이를 다루는 PKCS는 #1 에서 #15까지 있으며, 아래 표를 참조하시기 바랍니다.
PKCS #1 RSA Cryptography Standard Version 1.5 (RFC 2313, 3447 )
RSA 알고리즘을 이용해 데이터를 암호화하고 전자 서명하는 방법에 대한 내용과 RSA 공개 키의 구문을 설명합니다. PKCS #2와 #4는 폐기되고 주요 내용은 PKCS #1에 포함되었습니다.
PKCS #3 Diffie-Hellman Key Agreement Standard 키 합의 표준
디피헬먼 키분배 알고리즘의 구현을 위한 방법을 설명하였습니다.PKCS #3는 OSI의 전송 및 네트워크 계층에서의 안전한 전송 채널 구축을 위한 프로토콜에 사용되는 것이 목적입니다.
PKCS #5 Password-based Cryptography Specification Version 2.0 (RFC 2898)
개인 키정보를 사용자의 패스워드에 기반하여 암호화하는 방법을 정의한 패스워드기반 암호표준입니다. PKCS #5는 외부에서 개인키를 생성하여 당사자에게 전송시킬 때 개인키를 암호화하는 것을 목적으로 합니다. 개인키를 특징 기기나 개인이 사용하기 위해 복호화하기 위해서는 패스워드가 필요합니다.
PKCS #6 Extended-Certificate Syntax Standard
X.509 인증서의 version 1을 확장했지만, 현재는 X.509 Version 3가 사용되므로 사용되지 않습니다.
PKCS #7 Cryptographic Message Syntax Version 1.5 (RFC 3369)
전자 서명이나 전자 봉투와 같은 암호 응용에 대한 일반적인 구문 표현이빈다. PKCS #7은 PEM과 호환되니다.
PKCS #8 Private-Key Information Syntax Specification Version 1.2 (RFC 5208)
개인키 정보 구문 표준으로 개인키와 속성 정보를 포함한 암호화된 개인키를 위한 구문을 정의합니다.
PKCS #9 Selected Object Classes and Attribute Types
선택된 속성 형식 표준으로 PKCS #6 확장 인증서 구문 표준의 확장 인증서, PKCS #7의 전자서명 메세지와 PKCS #8 개인키 정보에서 사용되는 속성 유형들을 정의합니다.
PKCS #10 Certification Request Syntax Specification v1.7 (RFC 2986)
인증 요청 구문 표준으로 인증서 발급 요청서를 위한 구문 정의합니다. 인증서 발급 요청서는 사용자 식별 명칭, 공개키, 옵션인 속성들로 구성되어 사용자가 인증서 발생을 요구하기 위하여 인증 기관에 요청하는 구문입니다.
PKCS #11 Cryptographic Token Interface Standard 암호 토큰 인터페이스 표준
스마트카드와 같은 암호화 장비를 위한 기술 독립 프로그래밍 인터페이스 (CAPI) 를 설명합니다.
PKCS #12 Personal Information Exchange Syntax Standard 개인 정보 교환 표준
사용자의 개인키, 인증 등의 저장과 교환을 위한 포맷을 설명합니다.
PKCS #13 ECC 타원 곡선 암호 표준
타원 곡선 암호에 기반한 공개 키를 암호화를 하고 서명하는 매커니즘을 설명합니다.
PKCS #14 의사 난수 생성 표준
블록 암호, 해시 함수 등 다양한 방법의 의사 난수 생성 설명합니다.
PKCS #15 암호 토큰 정보 형식 표준
암호화 토큰에 저장된 암호화 보증서의 포맷을 위한 표준을 설명합니다.
인증서 발행 방식의 차이 : PKCS #10 과 PKCS #5
우리가 일반적으로 사용하는 것은 PKCS #5와 PKCS #10입니다. 이 두 가지는 개인키와 공개키를 생성하는 방식에서 차이가 있습니다. PKCS #10은 단말이나 사용자가 직접 키를 생성한 후에 공개키를 CA 서버로 부터 인증받아 인증서를 서명받은 후에 배포합니다. 개인키가 기기나 사용자를 벗어나지 않음으로 안정성을 보장받는 방법입니다. PKCS #5는 외부 CA 서버로 부터 단말이나 사용자의 개인키와 공인키를 생성한 후에 패스워드로 안전하게 암호화하여 기기나 사용자에게 주입하는 방식입니다. 따라서, PKCS #5 방식을 지원하면 보통 PKCS #10을 지원하지 않고, 그 역도 마찬가지 입니다.
인터넷 전화 보안의 관점에서 어떤 방식이 더 안전한가라는 물음이 생깁니다. 웹서핑도 해보고 자료도 찾아보았지만, 이 두가지에 대한 비교 자료가 없으므로 쉽게 단언할 수는 없습니다. 일반적인 환경에서 두 가지가 혼용되어 사용되고 기업의 상황에 따라 적절하게 사용하면 될 듯합니다.
현재 인터넷 전화 보안을 위해 두 가지 방식이 혼용되고 있습니다. 국산 장비들은 PKCS #5를, 외산 장비들은 PKCS #10을 지원합니다. 시스코 UC 구현을 위한 CUCM, IP Phone, CUBE 등은 모두 PKCS #10을 지원합니다.
[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
# 공개키 암호화
# C = E(msg, public key)
# P = D(encmsg, private key)
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
def main():
mymsg = b'takudaddy loves python'
myprikey = RSA.generate(1024)
mypubkey = myprikey.publickey()
print("Plaintext :", mymsg.decode())
myencmsg = rsa_enc(mymsg, mypubkey)
print("Ciphertext :", myencmsg)
mydecmsg = rsa_dec(myencmsg, myprikey)
print("Decrypted : ", mydecmsg.decode())
def rsa_dec(encmsg, prikey):
# input : bstr(encmsg), prikey
# output : msg
# function :
cipher = PKCS1_OAEP.new(prikey) # 객체를 생성
decmsg = cipher.decrypt(encmsg)
return decmsg
def rsa_enc(msg, key):
# input : bstr(msg), pubkey
# output : encmsg
# function :
cipher = PKCS1_OAEP.new(key)
encdata = cipher.encrypt(msg)
return encdata
if __name__ == '__main__':
main()
[RSA를 이용한 메시지 암호화/복호화 실습]
■ RSA 이용한 메시지 암호화/복호화
(ㄱ) RSA 개인키/공개키를 만들고
(ㄴ) 개인키/공개키 파일(PEM 형식)을 만든 후
(ㄷ) 메시지를 암복호화 한다.
# vi RSAPrivatePublic.py
# RSA
# KEY : 1024
# pubkey -> pubkey.pem -> E(msg, pubkey.pem) -> encmsg
# prikey -> prikey.pem -> D(encmsg, prikey.pem) -> decmsg
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
def main():
mymsg = b'Takudaddy loves python'
createPEM()
myencmsg = rsa_enc(mymsg)
print("Plaintext :", mymsg.decode())
print("Ciphertext:", myencmsg)
mydecmsg = rsa_dec(myencmsg)
print("Decrypted :", mydecmsg)
def rsa_dec(encmsg):
# input : bstr(encmsg), file(pubkey.pem)
# output : str(decmsg)
prikey = readPEM('prikey.pem')
# print(prikey)
cipher = PKCS1_OAEP.new(prikey)
decmsg = cipher.decrypt(encmsg)
return decmsg.decode()
def rsa_enc(bmsg):
# input : bstr(msg), file(prikey.pem, pubkey.pem)
# output : emcmsg
# function :
pubkey = readPEM('pubkey.pem')
# print(pubkey)
cipher = PKCS1_OAEP.new(pubkey)
encmsg = cipher.encrypt(bmsg)
return encmsg
def readPEM(file):
# input : file(prikey.pem|pubkey.pem)
# output : prikey|pubkey
# function :
fd = open(file)
# print(fd.read())
key = RSA.import_key(fd.read())
# print(key)
fd.close()
return key
def createPEM():
# input : NONE
# output : prikey.pem,
# function
prikey = RSA.generate(1024)
pubkey= prikey.publickey()
# 1) prikey => prikey.pem
fd = open('prikey.pem', 'wb+')
fd.write(prikey.export_key('PEM'))
fd.close()
# 2) pubkey => pubkey.pem
fd = open('pubkey.pem', 'wb+')
fd.write(pubkey.export_key('PEM'))
fd.close()
if __name__ == '__main__':
main()
[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 sys
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256 as SHA
def main():
# 1) Sender
mymsg = b'I am Takudaddy'
mypubkey, signature = rsa_sign(mymsg)
# print(mypubkey, signature)
send_msg = mymsg
send_pubkey = mypubkey
send_signature = signature
# 2) Reciever
recv_msg = send_msg
recv_pubkey = send_pubkey
recv_sign = send_signature
rsa_verify(recv_msg, recv_pubkey, recv_sign)
def rsa_verify(msg, pubkey, sign):
# input : bstr(msg), pubkey, signature
# output : msg("Authentic"|"Now authentic")
#
hash = SHA.new(msg)
try:
pkcs1_15.new(pubkey).verify(hash, sign)
print('Authentic')
except Exception as e:
print("Not authentic")
sys.exit("Error:", e)
def rsa_sign(msg):
# input : bstr(msg)
# output : pubkey, signature
# function:
prikey = readPEM('prikey.pem')
# print(key)
pubkey = prikey.publickey()
hash = SHA.new(msg)
signature = pkcs1_15.new(prikey).sign(hash)
return pubkey, signature
def readPEM(file):
# imput :
# output :
# function :
fd = open(file)
# print(fd.read())
key = RSA.importKey(fd.read())
fd.close()
return key
if __name__ == '__main__' :
main()
[ECDSA 사용한 전자서명 구현하기]
■ DSA
■ ECC
■ ECDSA 전자 서명 구현하기
1. DSA(Digital Signature Authentication)
DSA(Digital Signature Authentication)
광범위한 공개 키 서명 알고리즘이다.
보안은 이산 로그 문제(DLP)를 기반으로 한다.
(1). DSA key 생성(private key, public key) 생성
DSAKeyGen.py
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256 as SHA
prikey = DSA.generate(2048)
print(prikey.export_key())
pubkey = prikey.publickey().export_key()
print(pubkey)
(2). DSA 서명(signature)/검증(verification)
DSASigVerify.py
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256 as SHA
import sys
def main():
# 1) Singnature Side
# msg -> E(msg, prikey) -> sign, pubkey
# 1-1) msg
mymsg = b'Takudaddy'
# 1-2) prikey -> pubkey.pem
prikey = DSA.generate(2048)
fd = open('pubkey.pem', 'wb+')
fd.write(prikey.publickey().export_key('PEM'))
fd.close()
# 1-3) sign
hash = SHA.new(mymsg)
signer = DSS.new(prikey, 'fips-186-3')
signature = signer.sign(hash)
# print(mymsg)
# print(open('pubkey.pem').read())
# print(signature)
send_msg = mymsg
send_pubfile = 'pubkey.pem'
send_sign = signature
# 2) Verification side
# encmsg, sign, pubkey -> D(encmsg, pubkey, sign) -> AUTHENTIC|Not AUTHENTIC
recv_msg = send_msg
recv_pubfile = send_pubfile
recv_sign = send_sign
fd = open(recv_pubfile)
# print(fd.read())
pubkey = DSA.import_key(fd.read())
# print(pubkey)
fd.close()
# 처리 구문은 try exeption
try:
hash = SHA.new(recv_msg)
verifier = DSS.new(pubkey, 'fips-186-3')
verifier.verify(hash, recv_sign) # 비교
print("Authentic")
except Exception as e:
print("Not Authentic")
sys.exit("Error:", e)
if __name__ == '__main__':
main()
or
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
#
# (1) Signature(signer)
#
# sender : message, private key => signature, public key
# 새로운 DSA private/public key 생성
prikey = DSA.generate(2048)
with open('public_key.pem', 'wb') as fd1:
fd1.write(prikey.publickey().export_key('PEM'))
# private key로 메세지에 sign(=> 메세지의 해시 값에 sign)
message = b'hello'
hash = SHA256.new(message)
signer = DSS.new(prikey, 'fips-186-3')
signature = signer.sign(hash)
print('sender: message:', message)
print('sender: signature:', signature)
#
# (2) Verification(verifier)
#
# receiver : message, public key, signature
# public keys는
# * 일반적으로 상대편에게 받거나 인터넷에서 받는다.
# * 실습에서는 직접 작업하였다.
# 받은 message & signature
received_msg = b'hello'
received_signature = signature
# public key 읽기
with open('public_key.pem', 'rb') as fd2:
pubkey = DSA.import_key(fd2.read())
# 해시 값 계산
hash = SHA256.new(received_msg)
try:
verifier = DSS.new(pubkey, 'fips-186-3')
verifier.verify(hash, received_signature)
print('[ OK ] : Authentic')
except Exception as e:
print('[ FAIL ] : Not authentic')
2. ECC(Elliptic Curve Cryptography)
ECC(Elliptic Curve Cryptography)
현대적이고 효율적인 유형의 공개 키 암호이다.
보안은 곡선을 통해 계산된 특정 방정식으로 정의 된 필드에서
이산 로그를 풀기가 어렵다는 점을 기반으로 한다.
ECC를 사용하여 (ㄱ)디지털 서명을 만들거나 (ㄴ)키 교환을 수행 할 수 있다.
곡선(Curve) |
가능한 식별자 |
NIST P-256 |
'NIST P-256', 'p256', 'P-256', 'prime256v1', 'secp256r1' |
NIST P-384 |
'NIST P-384', 'p384', 'P-384', 'prime384v1', 'secp384r1' |
NIST P-521 |
'NIST P-521', 'p521', 'P-521', 'prime521v1', 'secp521r1' |
■ 새로운 ECC 키를 생성하고, export 하여 애플리케이션이 다시 load 하기
ECCKeyGen.py
from Crypto.PublicKey import ECC
# private key
prikey = ECC.generate(curve='P-256')
fd = open('prikey.pem', 'wt')
fd.write(prikey.export_key(format='PEM'))
fd.close()
# public key
pubkey = prikey.public_key()
print(pubkey)
fd2 = open('prikey.pem', 'rt')
pubkey2 = ECC.import_key(fd2.read())
print(pubkey2)
3. ECDSA 전자 서명 구현하기
ECDSA = ECC + DSA
■ ECDSA 사용한 전자서명 구현하기
ECDSA 방식을 사용하여 전자서명을 구현해 보자.
■ ECDSA 대해서
RSA는 1994년 표준화된 공개키 암호로 현재까지 광범위하게 오랫동안 사용되어온 기술이다.
ECDSA는 타원 곡선 기반의 전자서명 알고리즘으로 비교적 최근인 2008년 TLS v1.2의 기술 명세서에 소개된 공개키 암호 체계이다.
동일한 보안 수준으로 비교할 때 ECDSA는 RSA 보다 휠씬 작은 key size 갖는다.
예를 들어, 3072비트 key size RSA는 768바이트 데이터 크기를 가지지만, 동일한 보안 수준의 ECDSA는 RSA의 1/24 크기인 32바이트 데이터 크기를 가진다.
RSA와 ECDSA의 성능을 비교해 볼때, 서명 속도는 RSA가 ECDSA에 비해 빠르지만, 서명을 검증하는 속도는 ECDSA가 RSA보다 빠르다.
# ECDSASign.py
from Crypto.PublicKey import ECC
from Crypto.Signature import DSS
from Crypto.Hash import SHA256 as SHA
import os
import sys
def createPEM_ECDSA():
# ECC.generate : 타원곡석암호를 이용해 개인키를 생성한다.
# 'P-256' : (권장) 타원곡선암호를 만들기 위한 상수
# input : None
# output : file(privatekey_ecsa.pem, publicey_ecsa.pem)
key = ECC.generate(curve='P-256')
with open('privatekey_ecdsa.pem', 'w') as h:
h.write(key.export_key(format='PEM'))
key = key.public_key()
with open('publickey_ecdsa.pem', 'w') as h:
h.write(key.export_key(format='PEM'))
def readPEM_ECC(pemfile):
with open(pemfile, 'r') as h:
key = ECC.import_key(h.read())
# print("==>", key) ; input()
return key
def ecdsa_sign(msg):
private_key = readPEM_ECC('privatekey_ecdsa.pem')
# print('==>', private_key) ; input()
sha = SHA.new(msg)
signer = DSS.new(private_key, 'fips-186-3')
signature = signer.sign(sha)
# print('==>', signature) ; input()
return signature
def ecdsa_verify(msg, signature):
public_key = readPEM_ECC('publickey_ecdsa.pem')
sha = SHA.new(msg)
verifier = DSS.new(public_key, 'fips-186-3')
try:
verifier.verify(sha, signature)
print('Authentic')
except Exception as e: # 특정 타입이 아닌 모든 에러에 대한 처리
print('Not authentic')
sys.exit('Error:', e)
def main():
if os.path.exists('privatekey_ecdsa.pem') and os.path.exists('publickey_ecdsa.pem'):
print('privatekey_ecdsa.pem, publickey_ecdsa.pem file exist')
else:
createPEM_ECDSA()
print('privatekey_ecdsa.pem, publickey_ecdsa.pem file created.')
msg = 'My name is takudaddy'
signature = ecdsa_sign(msg.encode('utf-8'))
# print('==>', signature) ; input()
ecdsa_verify(msg.encode('utf-8'), signature)
if __name__ == '__main__':
main()
'정보보안공부 > 정보보안전문과정' 카테고리의 다른 글
정보보안 과정 Day88 : 일방향 해시 함수(이론) (0) | 2021.01.12 |
---|---|
정보보안 과정 Day87-1 : 하이브리드 암호 시스템 (0) | 2021.01.11 |
정보보안 과정 Day86-1 : 공개 키 암호 실습 (0) | 2021.01.11 |
정보보안 과정 Day86: 공개 키 암호2 (이론) (0) | 2021.01.08 |
정보보안 과정 Day85-1 : 무선 WEP 패킷 분석 / 공개 키 암호 (0) | 2021.01.07 |