서론
블록체인을 공부하면서 알게된 보안 방법인 디지털 서명에 대해 알아 보고자 한다. 생각보다 어려운 내용은 아니었다. 그렇지만 정확히 이해하지 않는다면 헷갈리기 쉬운 내용이며 당장 이해한다고 해도 바탕을 꽉 잡아두지 않는다면 뒤죽박죽 기억할 느낌이다. 익혀두면 요긴하게 사용할 수 있을 듯 하다. 이 후에 설명할 디지털 서명은 주로 블록체인에서 사용할 방식들 이다.
디지털 서명
디지털 데이터의 진위성과 무결성을 검증하는 암호화 방법.
블록체인에서는 트랜잭션의 진위성과 무결성을 검증하기 위해 사용한다.
공개키 암호화 방식 PKC
하나의 공개키와 하나의 개인키를 통해 암호화하는 방식. 데이터를 암호화할 수 있고 디지털 서명에도 이용할 수 있다.
공개키를 통해 데이터를 암호화 하고 개인키를 통해 암호화 된 데이터를 해독할 수 있다.
디지털 서명에서는 데이터 제공자가 개인키로 데이터를 해싱(서명)한 후 수신자가 이를 제공받은 공개키를 통해 검증 할 수 있다.
디지털 서명으로 얻을 수 있는 효과
- 데이터 무결성 : 데이터의 버전 당 하나의 서명만이 존재할 수 있다. 만약 데이터가 변경이 된다면 새로운 검증을 해야 한다.
- 진위성 : 한 쌍의 키가 존재하기에 수신자는 데이터를 서명하여 제공한 것이 반드시 제공자임을 알 수 있다.
- 부인 방지 : 수신자가 가지고 있는 공개키를 통해 서만이 제공된 데이터를 검증할 수 있기에 데이터를 제공한 쪽이 제공자임을 제공자는 부인할 수 없다.
디지털 서명 과정
블록체인의 디지털 서명에는 해싱, 서명, 검증 세단계가 존재한다. 간단히 살펴보겠다.
해싱
다양한 형태의 데이터들을 해싱 알고리즘을 통해 동일한 길이의 해시값을 갖도록 하는 과정이다.
서명
데이터 제공자가 해싱된 데이터를 개인키를 통해 암호화 하는 작업을 말한다.
검증
해싱된 데이터를 제공자가 서명을하고 이를 수신자에게 제공할 때 서명된 데이터와 함께 제공자가 가진 개인키와 한 쌍인 공개키를 같이 제공하게 된다. 수신자는 이 공개키를 통해 서명된 데이터를 검증할 수 있다.
Go를 통해 살펴보는 디지털 서명 과정
우선 키 한쌍을 만드는 것 부터 시작해 보겠다.
// 기본 라이브러리 ecdsa를 사용
// elliptic.P256()알고리즘을 이용해 공개키와 비공개키를 한 쌍 생성
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
이렇게 한줄이면 키 한쌍이 만들어 진다.
어떻게 그렇게 되는지 ecdsa.GenerateKey()를 살펴보자.
PrivateKey와 error를 return 하는 함수인 것을 쉽게 확인할 수 있다.
그런데 PrivateKey 뿐 아니라 PublicKey역시 만들어야 하는것으로 생각이되는데..
그 이유는 PrivateKey 구조체를 살펴보면 알 수 있다.
PrivateKey 구조체이다. 생각했던 것 보다 훨씬 간단한 구조인 것을 볼 수 있다.
PrivateKey 구조체에 PublicKey가 embedding 돼 있는걸 볼 수 있다.
또한, big.Int 타입의 D가 있는데 이는 아주 커다란 정수이다.
PublicKey 구조체이다. 역시나 간단하다. elliptic.Curve라는 인터페이스를 가지고있고 X, Y 두개의 big.Int 타입의 변수를 가지고 있다.
elliptic.Curve가 궁금하다면.. 구글링을 해 보시는게 좋을 듯.
여기서 X,Y는 똑똑한 수학자님들이 만든 암호화에 사용하는 타원곡선에서의 좌표이다. 🙄
만들어진 PrivateKey가 가진 D를 출력한 결과를 보자.
101214042641956426562101591357280062068314855860965974435039457142265879716085
어마어마한 정수가 나왔다. 우리는 이 big.Int타입을 이리 저리 굴려 데이터를 보안을 하는 것이다.
준비물은 모두 챙겼다. 그렇다면 디지털 서명의 과정을 살펴보자.
해쉬
data := "I'm data"
hash := sha256.Sum256([]byte(data))
다양한 해쉬 알고리즘이 있지만 sha256 알고리즘을 통해 진행해 보았다.
너무 간단하다. 어떠한 데이터가 들어오든 byte array로 만들어 준다.
실행 결과를 살펴보자.
data : I'm data
hash : [147 248 72 189 43 18 33 228 44 248 241 218 184 120 135 141 193 71 8 64 115 108 217 184 161 157 94 209 6 250 55 126]
해싱을 진행한 값 까지 얻어냈다.
서명
r,s,err := ecdsa.Sign(rand.Reader, privateKey, hash[:])
if err != nil {
panic(err)
}
Go에서 우리가 해야할 일은 단지 준비된 함수 Sign에 난수, privateKey, 그리고 해싱된 결과 값인 바이트값만 넣어주면 된다. 그러면 우린 서명의 결과인 r, s 를 return 받게 된다. 사인 과정을 마친것이다.
r과 s가 가진 값은 무엇일까
r : 65746470710239193580711389831554869158783657841982158766127274205607788530764
s : 51789230426181530575026329232510941582673184336295255818113163235606628208224
big.Int 타입의 값이다. 이제 이 값을 어떻게 사용할 것인가.
검증
ok := ecdsa.Verify(&privateKey.PublicKey, hash[:], r, s)
검증 또한 준비된 함수인 Verify에 공개키, 해쉬된 data, Sign의 결과로 얻은 서명 값 r, s값을 넣어주면 된다. 만일 이 중 하나라도 일치하지 않는다면 검증 실패의 결과를 얻을 것이다.
ok의 결과값을 살펴보자.
true
true값을 반환했다!!
우리가 진행한
- 데이터 해싱
- PrivateKey 값을 통한 해싱된 데이터 서명
- PublicKey 값을 통한 서명 검증
이 모두 성공 했다는 것을 의미한다.
전체 코드
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
)
func main() {
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // 공개키와 비공개키를 한 쌍 생성
if err != nil {
panic(err)
}
data := "I'm data"
hash := sha256.Sum256([]byte(data))
r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash[:])
if err != nil {
panic(err)
}
ok := ecdsa.Verify(&privateKey.PublicKey, hash[:], r, s)
fmt.Println(ok)
}
마무리
Go의 기본 라이브러리를 통해서도 간단히 디지털 서명을 알아볼 수 있었다.
물론 실제로 사용할 때에는 이 과정이 이렇게 한곳에 모여있을 가능성은 0%다.
하지만 이렇게 모아둔 코드를 통해 디지털 서명의 작동 순서를 간편히 살펴보면 후에 실제로 사용할 때에 큰 도움이 될것이라 믿어 의심치 않는다!
재밌따!
'Study > Blockchain' 카테고리의 다른 글
[블록체인] UTXO란? (0) | 2021.08.14 |
---|