ecdsa
는 타원 곡선 DSA (Elliptic Curve Digital Signature Algorithm) 입니다.
타원곡선을 사용한 비 대칭키 전자서명 알고리즘 입니다.
타원곡선 키 페어
다음 함수로 타원곡선 알고리즘의 공개/비밀키를 생성합니다.
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
// 타원, 랜덤 리더
타원은 elliptic.P224()
, elliptic.P256()
, elliptic.P384()
elliptic.P521()
가 있습니다.
서명
다음 함수로 메세지를 서명합니다.
r, s, err := ecdsa.Sign(rand.Reader, priv, data)
r
, s
는 서명 값입니다.
바이트 슬라이스로 얻고자 하면, 다음 함수를 사용합니다.
sign, err := ecdsa.SignASN1(rand.Reader, priv, data)
검증
다음 함수로 사인을 검증합니다.
ok := ecdsa.Verify(&priv.PublicKey, msg, r, s)
ok := ecdsa.VerifyASN1(&priv.PublicKey, msg, sign)
올바른 서명을 입력하면, true
가 리턴 됩니다.
인증서 인코딩
rsa
처럼, 공개키는 x509.MarshalPKIXPublicKey()
, x509.ParsePKIXPublicKey()
를
비밀키는x509.MarshalECPrivateKey()
, x509.ParseECPrivateKey()
또는,x509.MarshalPKCS8PrivateKey()
, x509.MarshalPKCS8PrivateKey()
를 선택해서 사용합니다.
OpenSSL
등이 출력하는 인증서는 비밀키 마샬/파싱을 x509.MarshalECPrivatekey()
, x509.ParseECPrivatekey()
을 사용해서 사용하면 됩니다.
pem 출력, 파싱
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
)
func main() {
priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
privDer, _ := x509.MarshalECPrivateKey(priv)
pubDer, _ := x509.MarshalPKIXPublicKey(&priv.PublicKey)
privBlock := &pem.Block{
Type: "ECDSA PRIVATE KEY",
Bytes: privDer,
}
pubBlock := &pem.Block{
Type: "ECDSA PUBLIC KEY",
Bytes: pubDer,
}
privPEM := pem.EncodeToMemory(privBlock)
pubPEM := pem.EncodeToMemory(pubBlock)
fmt.Printf("PRIVATE\n%s\n\n", privPEM)
fmt.Printf("PUBLIC\n%s\n", pubPEM)
}
// 출력
PRIVATE
-----BEGIN ECDSA PRIVATE KEY-----
MHcCAQEEILClafUZ8QAOQTRwCbZKjlL4l7T9z+zY2GqKgFepqVkGoAoGCCqGSM49
AwEHoUQDQgAEKKF+2h7j9gU883kLFcy+CYb7qJQF0Om2ALFDFvngZgsJGSEdkmFy
HZyqIgkJKYZj2uTnUsafs4N0iKullFmJ9Q==
-----END ECDSA PRIVATE KEY-----
PUBLIC
-----BEGIN ECDSA PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKKF+2h7j9gU883kLFcy+CYb7qJQF
0Om2ALFDFvngZgsJGSEdkmFyHZyqIgkJKYZj2uTnUsafs4N0iKullFmJ9Q==
-----END ECDSA PUBLIC KEY-----
예시
파일 서명
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"os"
)
var Path = "/home/code/LICENSE"
func main() {
f, _ := os.Open(Path)
hasher := sha256.New()
io.Copy(hasher, f)
fileHash := hasher.Sum(nil)
priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sign, _ := ecdsa.SignASN1(rand.Reader, priv, fileHash)
pubDer, _ := x509.MarshalPKIXPublicKey(&priv.PublicKey)
pubPEM := pem.EncodeToMemory(&pem.Block{
Type: "ECDSA PUBLIC KEY",
Bytes: pubDer,
})
fmt.Printf("file %s signiture:\n%x\n", Path, sign)
fmt.Printf("PUBLIC KEY:\n%s\n", pubPEM)
}
// 출력
file /home/code/LICENSE signiture:
304402200cf3fbba969cad28b904c33baa0c1dd973bd6d11b79b22f070a5602156405a0b0220371400e9446c3ddc13eae286ed36e793ba6505e375f63a0eb3d18342c52452bf
PUBLIC KEY:
-----BEGIN ECDSA PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMWv37ewL+vK1ynI2qQqX3QEtEcnG
5iKhrBxtkmYJgXhtaBDSNZikXu9045E5EJBPFfWolaWHFKIN1A0h/xY6Cg==
-----END ECDSA PUBLIC KEY-----
서명 검증
package main
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"io"
"os"
)
var Path = "/home/code/LICENSE"
var sign = `304402200cf3fbba969cad28b904c33baa0c1dd973bd6d11b79b22f070a5602156405a0b0220371400e9446c3ddc13eae286ed36e793ba6505e375f63a0eb3d18342c52452bf`
var pubPEM = `-----BEGIN ECDSA PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMWv37ewL+vK1ynI2qQqX3QEtEcnG
5iKhrBxtkmYJgXhtaBDSNZikXu9045E5EJBPFfWolaWHFKIN1A0h/xY6Cg==
-----END ECDSA PUBLIC KEY-----`
func main() {
pubPEMBlock, _ := pem.Decode([]byte(pubPEM))
if pubPEMBlock == nil {
println("invalid public key")
return
}
der, _ := x509.ParsePKIXPublicKey(pubPEMBlock.Bytes)
ecdsaDer := der.(*ecdsa.PublicKey)
f, _ := os.Open(Path)
hasher := sha256.New()
io.Copy(hasher, f)
fileHash := hasher.Sum(nil)
sigDecoded := make([]byte, len(sign))
n, _ := hex.Decode(sigDecoded, []byte(sign))
if ok := ecdsa.VerifyASN1(ecdsaDer, fileHash, sigDecoded[:n]); ok {
fmt.Printf("file: %s\nsign: %x\nis correct signature\n", Path, sigDecoded[:n])
} else {
fmt.Printf("file: %s\nsign: %x\nis invalid signature\n", Path, sigDecoded[:n])
}
}
// 출력
file: /home/code/LICENSE
sign: 304402200cf3fbba969cad28b904c33baa0c1dd973bd6d11b79b22f070a5602156405a0b0220371400e9446c3ddc13eae286ed36e793ba6505e375f63a0eb3d18342c52452bf
is correct signature
ECDH
ECDH
는 타원 곡선 디피-헬만(Elliptic-curve Diffie–Hellman) 알고리즘입니다.
타원 곡선을 사용하여 기존 디피-헬만 방식보다 더 짧은키와 더 빠른 연산속도로 상대측과 공유키를 만들 수 있습니다.
ECDH
함수 예제
func GenerateSharedKey(localPriv *ecdsa.PrivateKey, remotePub *ecdsa.PublicKey) []byte {
x, y := remotePub.Curve.ScalarMult(remotePub.X, remotePub.Y, localPriv.D.Bytes())
// 상대측과 동일한 x, y 값을 가짐
hasher := sha256.New()
hasher.Write(x.Bytes())
hasher.Write(y.Bytes())
return hasher.Sum(nil)
}
각각의 호스트는 상대측 호스트의 공개키를 사전에 공유해야 합니다.