ECDSA in Go — A Simple Introduction
3 min readSep 4, 2023
The Elliptic Curve Digital Signature Algorithm (ECDSA) is an algorithm that many might find complex. However, it’s essential for various cryptographic operations. Today, let’s break down ECDSA, its advantages, and a straightforward example using Go.
1. Use Cases of ECDSA:
- Web Security: ECDSA provides robust security measures for web traffic, ensuring that the data being transferred between users and servers is safe.
- Cryptocurrencies: Many modern digital currencies, like Bitcoin, use ECDSA for their transaction signature algorithms. It allows for secure and verified transactions.
- Secure Email: ECDSA can be utilized in digital signatures for emails, making sure the recipient can verify the sender’s identity.
- Software Authenticity: Software developers can use ECDSA to sign their applications, ensuring users that the software has not been tampered with since its creation.
2. ECDSA: Pros and Cons
Pros:
- Security: For the same level of security, ECDSA requires shorter key lengths compared to other algorithms, which means faster processing and less storage.
- Efficiency: Due to its shorter key size, it is faster and consumes less power, making it ideal for devices with limited resources.
- Compatibility: ECDSA is now widely supported and has become a standard in modern cryptography practices.
Cons:
- Complexity: The underlying mathematics of elliptic curves can be quite complex, making the initial understanding and implementation a bit challenging.
- Vulnerabilities: Like all cryptographic algorithms, vulnerabilities can be found over time. Proper and secure implementation is crucial.
Example
Here’s a gist, which shows a simple implementation of ECDSA in Go:
package ec
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"io"
"reflect"
)
// Elliptic Curve Cryptography (ECC) is a key-based technique for encrypting data.
// ECC focuses on pairs of public and private keys for decryption and encryption of web traffic.
// ECC is frequently discussed in the context of the Rivest–Shamir–Adleman (RSA) cryptographic algorithm.
// EllipticCurve data struct
type EllipticCurve struct {
pubKeyCurve elliptic.Curve // http://golang.org/pkg/crypto/elliptic/#P256
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
}
// New EllipticCurve instance
func New(curve elliptic.Curve) *EllipticCurve {
return &EllipticCurve{
pubKeyCurve: curve,
privateKey: new(ecdsa.PrivateKey),
}
}
// GenerateKeys EllipticCurve public and private keys
func (ec *EllipticCurve) GenerateKeys() (privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey, err error) {
privKey, err = ecdsa.GenerateKey(ec.pubKeyCurve, rand.Reader)
if err == nil {
ec.privateKey = privKey
ec.publicKey = &privKey.PublicKey
}
return
}
// EncodePrivate private key
func (ec *EllipticCurve) EncodePrivate(privKey *ecdsa.PrivateKey) (key string, err error) {
encoded, err := x509.MarshalECPrivateKey(privKey)
if err != nil {
return
}
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: encoded})
key = string(pemEncoded)
return
}
// EncodePublic public key
func (ec *EllipticCurve) EncodePublic(pubKey *ecdsa.PublicKey) (key string, err error) {
encoded, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return
}
pemEncodedPub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: encoded})
key = string(pemEncodedPub)
return
}
// DecodePrivate private key
func (ec *EllipticCurve) DecodePrivate(pemEncodedPriv string) (privateKey *ecdsa.PrivateKey, err error) {
blockPriv, _ := pem.Decode([]byte(pemEncodedPriv))
x509EncodedPriv := blockPriv.Bytes
privateKey, err = x509.ParseECPrivateKey(x509EncodedPriv)
return
}
// DecodePublic public key
func (ec *EllipticCurve) DecodePublic(pemEncodedPub string) (publicKey *ecdsa.PublicKey, err error) {
blockPub, _ := pem.Decode([]byte(pemEncodedPub))
x509EncodedPub := blockPub.Bytes
genericPublicKey, err := x509.ParsePKIXPublicKey(x509EncodedPub)
publicKey = genericPublicKey.(*ecdsa.PublicKey)
return
}
// VerifySignature sign ecdsa style and verify signature
func (ec *EllipticCurve) VerifySignature(privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey) (signature []byte, ok bool, err error) {
h := md5.New()
_, err = io.WriteString(h, "This is a message to be signed and verified by ECDSA!")
if err != nil {
return
}
signhash := h.Sum(nil)
r, s, serr := ecdsa.Sign(rand.Reader, privKey, signhash)
if serr != nil {
return []byte(""), false, serr
}
signature = r.Bytes()
signature = append(signature, s.Bytes()...)
ok = ecdsa.Verify(pubKey, signhash, r, s)
return
}
// Can be used in _test.go
// Test encode, decode and test it with deep equal
func (ec *EllipticCurve) Test(privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey) (err error) {
encPriv, err := ec.EncodePrivate(privKey)
if err != nil {
return
}
encPub, err := ec.EncodePublic(pubKey)
if err != nil {
return
}
priv2, err := ec.DecodePrivate(encPriv)
if err != nil {
return
}
pub2, err := ec.DecodePublic(encPub)
if err != nil {
return
}
if !reflect.DeepEqual(privKey, priv2) {
err = errors.New("private keys do not match")
return
}
if !reflect.DeepEqual(pubKey, pub2) {
err = errors.New("public keys do not match")
return
}
return
}
https://gist.github.com/LukaGiorgadze/85b9e09d2008a03adfdfd5eea5964f93