27/01/2024 - GO
Bu örnekte ECDSA'yı kullanarak bir özel anahtar ve bir genel anahtar oluşturacağız. ECDSA, hem imzalama hem de doğrulama için farklı anahtarlar kullanan asimetrik bir imzalama yöntemidir. Bu, istemci ve sunucu iletişimlerinde kullanışlıdır. Mesajı oluşturan kişi hem özel hem de genel anahtarları bilirken, kullanıcı yalnızca genel anahtarı bilebilir. Mesaj özel anahtarla imzalanır ve genel anahtarla doğrulanır.
RSA ve ECDSA arasında sıkışıp kalırsanız, neredeyse her zaman tavsiye ECDSA'yı kullanmak olacaktır. Daha fazla ayrıntı için Internet'i kontrol edebilirsiniz, ancak işte onlardan bazıları. Avantajları nedeniyle daha fazla uygulama ECDSA'ya yöneliyor.
Daha kısa sonuçlar için hex
kodlayıcıya alternatif olarak base64.URLEncoding/base64.RawStdEncoding
işlevlerini kullanabilirsiniz.
package ecdsa
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
)
type keyType string
const (
Public keyType = "PUBLIC KEY"
Private keyType = "PRIVATE KEY" // Or EC PRIVATE KEY
)
type Key struct {
private *ecdsa.PrivateKey
public *ecdsa.PublicKey
}
// NewKey accepts a Curve type to generate keys.
// REF: https://pkg.go.dev/crypto/elliptic#Curve
func NewKey(curve elliptic.Curve) (Key, error) {
prv, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return Key{}, err
}
return Key{
private: prv,
public: &prv.PublicKey,
}, nil
}
// NewKeyFromPrivate accepts a private key to reproduce keys.
// REF: https://pkg.go.dev/crypto/elliptic#Curve
func NewKeyFromPrivate(curve elliptic.Curve, private string) (Key, error) {
block, _ := pem.Decode([]byte(private))
prv, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return Key{}, err
}
return Key{
private: prv,
public: &prv.PublicKey,
}, nil
}
func (k Key) String(keyType keyType) (string, error) {
var (
enc []byte
err error
)
switch keyType {
case Public:
enc, err = x509.MarshalPKIXPublicKey(k.public)
case Private:
enc, err = x509.MarshalECPrivateKey(k.private)
default:
return "", fmt.Errorf("invalid key type: %s", keyType)
}
if err != nil {
return "", err
}
return string(pem.EncodeToMemory(&pem.Block{Type: string(keyType), Bytes: enc})), nil
}
package ecdsa
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
)
type Message struct {
key Key
text string
}
// NewMessage returns a new message.
func NewMessage(key Key, text string) Message {
return Message{
key: key,
text: text,
}
}
func (m Message) Sign() (string, error) {
hash := sha256.Sum256([]byte(m.text)) // Prefer blake2b
sig, err := ecdsa.SignASN1(rand.Reader, m.key.private, hash[:])
if err != nil {
return "", err
}
return hex.EncodeToString(sig), nil
}
func (m Message) Verify(signature string) error {
sig, err := hex.DecodeString(signature)
if err != nil {
return err
}
hash := sha256.Sum256([]byte(m.text)) // Prefer blake2b
if !ecdsa.VerifyASN1(m.key.public, hash[:], sig) {
return errors.New("invalid signature")
}
return nil
}
Aynı genel anahtarı yeniden oluşturmak için özel anahtarı ortam değişkenlerine kaydedebilir ve yeniden kullanabilirsiniz. Bunun için NewKeyFromPrivate
yöntemini kullanın.
package main
import (
"crypto/elliptic"
"fmt"
"random/ecdsa"
)
func main() {
// FRESH KEY ---------------------------------------------------------------
key, err := ecdsa.NewKey(elliptic.P256())
if err != nil {
panic(err)
}
pub, err := key.String(ecdsa.Public)
if err != nil {
panic(err)
}
prv, err := key.String(ecdsa.Private)
if err != nil {
panic(err)
}
fmt.Printf("PUBLIC KEY (NewKey):\n%s\nPRIVATE KEY (NewKey):\n%s\n", pub, prv)
// KEY FROM EXISTING PRIVATE KEY ------------------------------------------
key, err = ecdsa.NewKeyFromPrivate(elliptic.P256(), prv)
if err != nil {
panic(err)
}
pub, err = key.String(ecdsa.Public)
if err != nil {
panic(err)
}
prv, err = key.String(ecdsa.Private)
if err != nil {
panic(err)
}
fmt.Printf("PUBLIC KEY (NewKeyFromPrivate):\n%s\nPRIVATE KEY (NewKeyFromPrivate):\n%s\n", pub, prv)
// SIGN AND VERIFY MESSAGE -------------------------------------------------
plainMessage := "I am a plain message but I will be signed soon!"
fmt.Printf("PLAIN MESSAGE:\n%s\n", plainMessage)
msg := ecdsa.NewMessage(key, plainMessage)
signature, err := msg.Sign()
if err != nil {
panic(err)
}
fmt.Printf("MESSAGE SIGNATURE:\n%s\n", signature)
if err := msg.Verify(signature); err != nil {
panic(err)
}
fmt.Println("VERIFY SIGNATURE:\nOK")
if err := msg.Verify(signature[:len(signature)-3]+"ABC"); err != nil {
panic(err)
}
fmt.Println("VERIFY SIGNATURE:\nOK")
}
$ go run -race main.go
PUBLIC KEY (NewKey):
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOAKvZyZ+V8YwhvT3pmbXivGJK/pU
nFJpnQ5yF4w+VcZK/eyuEfaaBqdWPAWf5qIKEqc42vq/SwzYJAvZlpUCsg==
-----END PUBLIC KEY-----
PRIVATE KEY (NewKey):
-----BEGIN PRIVATE KEY-----
MHcCAQEEINPB45II2s55I08oM3Vix56rLApzLcFk6yj5ISEsyoYloAoGCCqGSM49
AwEHoUQDQgAEOAKvZyZ+V8YwhvT3pmbXivGJK/pUnFJpnQ5yF4w+VcZK/eyuEfaa
BqdWPAWf5qIKEqc42vq/SwzYJAvZlpUCsg==
-----END PRIVATE KEY-----
PUBLIC KEY (NewKeyFromPrivate):
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOAKvZyZ+V8YwhvT3pmbXivGJK/pU
nFJpnQ5yF4w+VcZK/eyuEfaaBqdWPAWf5qIKEqc42vq/SwzYJAvZlpUCsg==
-----END PUBLIC KEY-----
PRIVATE KEY (NewKeyFromPrivate):
-----BEGIN PRIVATE KEY-----
MHcCAQEEINPB45II2s55I08oM3Vix56rLApzLcFk6yj5ISEsyoYloAoGCCqGSM49
AwEHoUQDQgAEOAKvZyZ+V8YwhvT3pmbXivGJK/pUnFJpnQ5yF4w+VcZK/eyuEfaa
BqdWPAWf5qIKEqc42vq/SwzYJAvZlpUCsg==
-----END PRIVATE KEY-----
PLAIN MESSAGE:
I am a plain message but I will be signed soon!
MESSAGE SIGNATURE:
3045022059ca575ac9e672db5a4d8f38408434ab5fb59b6d0e9c5f0e7fd7da54b234a721022100b9b762be232af27ed958cf1aa7e10ed9519296467434547b998e9614653e54a0
VERIFY SIGNATURE:
OK
panic: invalid signature
goroutine 1 [running]:
main.main()
/main.go:70 +0x585
exit status 2