가장 빨리 만나는 Go 언어 Unit 53.3 RSA 공개키 알고리즘 사용하기

저작권 안내
  • 책 또는 웹사이트의 내용을 복제하여 다른 곳에 게시하는 것을 금지합니다.
  • 책 또는 웹사이트의 내용을 발췌, 요약하여 강의 자료, 발표 자료, 블로그 포스팅 등으로 만드는 것을 금지합니다.

암호화 사용하기

이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07

RSA 공개키 알고리즘 사용하기

대칭키 알고리즘은 암호 키가 유출되면 암호화된 데이터를 모두 풀 수 있습니다. 특히 네트워크로 암호 키를 주고 받으면 노출될 위험이 커집니다. 따라서 네트워크로 데이터를 주고 받을때는 공개키 암호화 알고리즘을 많이 사용합니다. 단 공개키 알고리즘은 대칭키 알고리즘에 비해 속도가 느리므로 대칭키 알고리즘의 암호 키만 공개키 알고리즘으로 암호화하여 네트워크로 전송하기도 합니다.

다음은 crypto/rsa 패키지에서 제공하는 공개키 알고리즘 함수입니다.

  • func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error): 개인 키와 공개키 생성
  • func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error): 평문을 공개 키로 암호화
  • func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error): 암호화된 데이터를 개인 키로 복호화

RSA 공개키 알고리즘을 사용하여 암호화와 복호화를 해보겠습니다.

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"fmt"
)

func main() {
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048) // 개인 키와 공개키 생성
	if err != nil {
		fmt.Println(err)
		return
	}
	publicKey := &privateKey.PublicKey // 개인 키 변수 안에 공개 키가 들어있음

	s := `동해 물과 백두산이 마르고 닳도록
하느님이 보우하사 우리나라 만세.
무궁화 삼천리 화려강산
대한 사람, 대한으로 길이 보전하세.`

	ciphertext, err := rsa.EncryptPKCS1v15( // 평문을 공개 키로 암호화
		rand.Reader,
		publicKey, // 공개키
		[]byte(s),
	)

	fmt.Printf("%x\n", ciphertext)

	plaintext, err := rsa.DecryptPKCS1v15( // 암호화된 데이터를 개인 키로 복호화
		rand.Reader,
		privateKey, // 개인키
		ciphertext,
	)

	fmt.Println(string(plaintext))
}

rsa.GenerateKey 함수에 rand.Reader와 키의 길이를 넣으면 개인 키와 공개키가 생성됩니다. 키의 길이는 비트 단위이며 2의 제곱으로 입력합니다.

rsa.EncryptPKCS1v15 함수에 rand.Reader, publicKey, 암호화할 데이터를 넣으면 암호화가 됩니다. 그리고 rsa.DecryptPKCS1v15 함수에 rand.Reader, privateKey, 암호화된 데이터를 넣으면 복호화가 됩니다.

이처럼 공개키 알고리즘은 암호화할 때는 공개 키를 사용하고, 복호화할 때는 개인 키를 사용합니다. 공개 키를 외부에 공개하여 다른 사람한테 전달한 뒤 암호화를 하고, 암호화된 데이터를 받는 사람은 자기가 가진 개인키로 복호화하는 방식입니다. 개인 키는 외부에 노출할 일이 없으며 공개 키로는 개인 키를 추출하기 어렵기 때문에 안전하게 암호화된 데이터를 주고 받을 수 있습니다.

이번에는 RSA 알고리즘을 사용하여 메시지를 서명(Signing)하고 인증(Verification)하는 방법을 알아보겠습니다.

다음은 crypto/rsa 패키지에서 제공하는 서명, 인증 함수입니다.

  • func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err error): 개인 키로 서명
  • func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error): 공개키로 서명 검증

다음은 crypto/md5 패키지에서 제공하는 해시 함수입니다.

  • func New() hash.Hash: MD5 해시 인스턴스 생성
  • func (d *digest) Write(p []byte) (nn int, err error): 해시 인스턴스에 데이터 추가
  • func (d0 *digest) Sum(in []byte) []byte: 해시 인스턴스에 저장된 데이터의 MD5 해시 값 추출
package main

import (
	"crypto"
	"crypto/md5"
	"crypto/rand"
	"crypto/rsa"
	"fmt"
)

func main() {
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048) // 개인 키와 공개 키 생성
	if err != nil {
		fmt.Println(err)
		return
	}
	publicKey := &privateKey.PublicKey // 개인 키 변수 안에 공개 키가 들어있음

	message := "안녕하세요. Go 언어"
	hash := md5.New()           // 해시 인스턴스 생성
	hash.Write([]byte(message)) // 해시 인스턴스에 문자열 추가
	digest := hash.Sum(nil)     // 문자열의 MD5 해시 값 추출

	var h1 crypto.Hash
	signature, err := rsa.SignPKCS1v15( // 개인 키로 서명
		rand.Reader,
		privateKey, // 개인 키
		h1,
		digest,     // MD5 해시 값
	)

	var h2 crypto.Hash
	err = rsa.VerifyPKCS1v15( // 공개 키로 서명 검증
		publicKey, // 공개 키
		h2,
		digest,    // MD5 해시 값
		signature, // 서명 값
	)

	if err != nil {
		fmt.Println("검증 실패")
	} else {
		fmt.Println("검증 성공")
	}
}

먼저 서명할 데이터의 해시 값을 구합니다. 여기서는 md5로 해시 값을 구합니다.

message := "안녕하세요. Go 언어"
hash := md5.New()           // 해시 인스턴스 생성
hash.Write([]byte(message)) // 해시 인스턴스에 문자열 추가
digest := hash.Sum(nil)     // 문자열의 MD5 해시 값 추출

이제 "안녕하세요. Go 언어"의 해시 값을 개인 키로 서명합니다. 여기서 서명이란 내가 "안녕하세요. Go 언어"라는 메시지를 보냈다고 보증하는 것입니다.

var h1 crypto.Hash
signature, err := rsa.SignPKCS1v15( // 개인 키로 서명
	rand.Reader,
	privateKey, // 개인 키
	h1,
	digest,     // MD5 해시 값
)

rsa.SignPKCS1v15 함수에 rand.Reader, crypto.Hash, 개인 키, 해시 값을 넣으면 서명이 됩니다. 이렇게 해서 메시지(message), 메시지에 대한 해시 값(digest), 서명(signature)이 모두 준비되었습니다.

메시지를 받는 사람은 정말 저 사람(개인 키를 가진 사람)이 "안녕하세요. Go 언어"라는 말을 했는지 검증(인증)을 해야됩니다.

var h2 crypto.Hash
err = rsa.VerifyPKCS1v15( // 공개 키로 서명 검증
	publicKey, // 공개 키
	h2,
	digest,    // MD5 해시 값
	signature, // 서명 값
)

rsa.VerifyPKCS1v15 함수에 공개 키, crypto.Hash, 해시 값, 서명을 넣으면 검증이 됩니다. err 값이 nil이면 정상적인 메시지입니다.

메시지, 메시지에 대한 해시 값, 서명, 공개 키는 모두 공개된 정보입니다. 이 값들을 이용해서 메시지가 변조되지 않고, 올바른 사람한테서 왔는지 검증하는 것입니다.

  • 메시지에 대한 해시 값으로 메시지가 변조되었는지 확인할 수 있습니다.
  • 서명 값으로 메시지가 올바른 사람한테서 왔는지 확인할 수 있습니다. 즉 서명을 할 수 있는 사람은 개인 키를 가진 사람밖에 없기 때문입니다.

개인 키를 가지고 서명을 하는 주체는 사람이 될 수도 있고 컴퓨터가 될 수도 있습니다. 여기서는 편의상 사람이라 칭했습니다. 보통 금융거래에서 많이 쓰는 공인인증서가 서명과 인증 방식에 RSA 알고리즘을 사용합니다. 다른 사례로는 인터넷에서 파일을 받을 때 해시 값과 서명 파일이 함께 있는 곳이 있습니다. 이때는 해시 값으로 파일 변조 여부를 확인하고, 서명 파일로 공식 개발자(회사, 단체)가 배포한 것인지 검증할 수 있습니다.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

2015-06-01